Commit 36a81897 authored by Kruyff,D.L.W. (Dylan)'s avatar Kruyff,D.L.W. (Dylan)
Browse files

Refactor code


Former-commit-id: a63b1327
parent 0f4f8f6f
...@@ -7,11 +7,12 @@ export interface RawData { ...@@ -7,11 +7,12 @@ export interface RawData {
export interface LshData { export interface LshData {
candidates: number[][][]; candidates: number[][][];
tables: {[bucket: string]: number[]}[]; distances: number[][][];
average_candidates: number[]; average_candidates: number[];
average_distances: number[]; average_distances: number[];
tables: {[bucket: string]: number[]}[];
average_table: {[bucket: string]: number[]};
samples: number[]; samples: number[];
distances: number[][][];
hash_functions: number[][]; hash_functions: number[][];
parameters?: number[]; parameters?: number[];
} }
...@@ -25,23 +26,37 @@ export interface TableInfoData { ...@@ -25,23 +26,37 @@ export interface TableInfoData {
distances: number[][]; distances: number[][];
} }
export interface Parameters {
windowsize: number;
hashsize: number;
tablesize: number;
stepsize: number;
}
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
/**
* This service acts as the interface between the client and server side.
*/
export class ApiService { export class ApiService {
constructor() { } constructor() { }
// Read input data /**
* Read input data. The format is a list of channels, where each channel is an object of type RawData
*/
async readFile(): Promise<RawData[]> { async readFile(): Promise<RawData[]> {
const response = await fetch('http://127.0.0.1:5000/read-mts-data'); const response = await fetch('http://127.0.0.1:5000/read-data');
return await response.json(); return await response.json();
} }
// Split data into windows and normalize /**
async createWindows(parameters): Promise<any> { * Split the data into windows (server side)
*/
async createWindows(parameters: Parameters): Promise<any> {
const postData = {parameters}; const postData = {parameters};
const response = await fetch('http://127.0.0.1:5000/create-mts-windows', { await fetch('http://127.0.0.1:5000/create-windows', {
method: 'POST', method: 'POST',
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
...@@ -51,32 +66,39 @@ export class ApiService { ...@@ -51,32 +66,39 @@ export class ApiService {
}); });
} }
// Calculate parameters for LSH + find candidates using LSH /**
async lshInitial(query): Promise<LshData> { * Get weights which will be applied to the LSH hash functions
const response = await fetch('http://127.0.0.1:5000/initialize', { */
async getWeights(query: number[][], labels: {[index: number]: boolean}, weights: number[], hash_functions: number[][]): Promise<number[]> {
const response = await fetch('http://127.0.0.1:5000/weights', {
method: 'POST', method: 'POST',
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: new Blob( [ JSON.stringify({query}) ], { type: 'text/plain' } ) body: new Blob( [ JSON.stringify({query, labels, weights, hash_functions}) ], { type: 'text/plain' } )
}); });
return await response.json(); return await response.json();
} }
async getWeights(query: number[][], labels: {[index: number]: boolean}, weights: number[], hash_functions: number[][]): Promise<number[]> { /**
const response = await fetch('http://127.0.0.1:5000/weights', { * Do the first iteration of LSH and return important information
*/
async lshInitial(query: number[][]): Promise<LshData> {
const response = await fetch('http://127.0.0.1:5000/initialize', {
method: 'POST', method: 'POST',
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: new Blob( [ JSON.stringify({query, labels, weights, hash_functions}) ], { type: 'text/plain' } ) body: new Blob( [ JSON.stringify({query}) ], { type: 'text/plain' } )
}); });
return await response.json(); return await response.json();
} }
// Find candidates using LSH with weights /**
* Do another iteration of LSH, with weights, and return important information
*/
async lshUpdate(query, weights, parameters): Promise<LshData> { async lshUpdate(query, weights, parameters): Promise<LshData> {
const response = await fetch('http://127.0.0.1:5000/update', { const response = await fetch('http://127.0.0.1:5000/update', {
method: 'POST', method: 'POST',
...@@ -89,20 +111,24 @@ export class ApiService { ...@@ -89,20 +111,24 @@ export class ApiService {
return await response.json(); return await response.json();
} }
// Get query window based on windows labeled correct /**
async getQueryWindow(window): Promise<number[][]> { * Get query window based on windows labeled correct
*/
async getQueryWindow(indices: number | {[index: number]: boolean}): Promise<number[][]> {
const response = await fetch('http://127.0.0.1:5000/query', { const response = await fetch('http://127.0.0.1:5000/query', {
method: 'POST', method: 'POST',
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({window}) body: JSON.stringify({indices})
}); });
return await response.json(); return await response.json();
} }
// Get data of a window by indices /**
* Get data of a window by indices
*/
async getWindowByIndices(indices: number[]): Promise<number[][][]> { async getWindowByIndices(indices: number[]): Promise<number[][][]> {
const response = await fetch('http://127.0.0.1:5000/window', { const response = await fetch('http://127.0.0.1:5000/window', {
method: 'POST', method: 'POST',
...@@ -115,14 +141,17 @@ export class ApiService { ...@@ -115,14 +141,17 @@ export class ApiService {
return await response.json(); return await response.json();
} }
async getTableInfo(windows): Promise<TableInfoData> { /**
* Get additional information for a given table
*/
async getTableInfo(table: number[][]): Promise<TableInfoData> {
const response = await fetch('http://127.0.0.1:5000/table-info', { const response = await fetch('http://127.0.0.1:5000/table-info', {
method: 'POST', method: 'POST',
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({windows}) body: JSON.stringify({table})
}); });
return await response.json(); return await response.json();
} }
......
import {Component, OnInit, ViewChild} from '@angular/core'; import {Component, OnInit, ViewChild} from '@angular/core';
import {StateService} from '../state.service'; import {StateService} from '../state.service';
import * as d3 from 'd3'; import * as d3 from 'd3';
import {TableInfoData} from '../api.service';
@Component({ @Component({
selector: 'app-progress-view', selector: 'app-progress-view',
...@@ -21,12 +22,14 @@ export class ProgressViewComponent implements OnInit { ...@@ -21,12 +22,14 @@ export class ProgressViewComponent implements OnInit {
constructor(private state: StateService) { } constructor(private state: StateService) { }
ngOnInit(): void { ngOnInit(): void {
this.state.onNewTableInfo.subscribe(() => { this.showgraph(); }); this.state.onNewLshData.subscribe(() => {
this.state.onNewTableInfo.subscribe(() => { this.showHistogram(); }); this.showgraph();
this.showHistogram();
});
} }
showHistogram() { showHistogram() {
const table = this.state._averageTable; const table = this.state.lshData.average_table;
this.hist = { this.hist = {
data: [{ data: [{
x: Object.keys(table), x: Object.keys(table),
...@@ -162,7 +165,7 @@ export class ProgressViewComponent implements OnInit { ...@@ -162,7 +165,7 @@ export class ProgressViewComponent implements OnInit {
d3.selectAll('circle').transition().style('stroke', undefined); d3.selectAll('circle').transition().style('stroke', undefined);
d3.select('#node-' + v.value).transition().style('stroke', 'black').style('stroke-width', 20); d3.select('#node-' + v.value).transition().style('stroke', 'black').style('stroke-width', 20);
const data = this.hist; const data = this.hist;
data.data[0].marker.line.width = Object.keys(this.state._averageTable).map((key) => { data.data[0].marker.line.width = Object.keys(this.state.lshData.average_table).map((key) => {
return Number(key) === v.value ? 4 : 0; return Number(key) === v.value ? 4 : 0;
}); });
this.hist = data; this.hist = data;
...@@ -177,102 +180,15 @@ export class ProgressViewComponent implements OnInit { ...@@ -177,102 +180,15 @@ export class ProgressViewComponent implements OnInit {
} }
public get table() { public get table() {
return this.state._averageTable; return this.state.lshData.average_table;
} }
async showgraph() { async showgraph() {
const nodes = []; const tableInfo: TableInfoData = await this.state.getTableInfo(Object.values(this.state.lshData.average_table));
const links = []; this.hoverPlot(tableInfo.prototypes);
const keys = Object.keys(this.table); const distances = tableInfo.distances;
this.hoverPlot(this.state.tableInfo.prototypes);
const distances = this.state.tableInfo.distances;
// for (const key in this.table) {
// const size = this.table[key].length;
// nodes.push({id: key, group: Number(key), size: size});
// }
// for (const key in this.table) {
// for (const key2 in this.table) {
// if (key === key2) {
// continue;
// }
// links.push({source: key, target: key2, value: 0.001 * (100 - 5 * distances[keys.indexOf(key)][keys.indexOf(key2)])});
// }
// }
// const graph = {nodes, links};
//
// const svg = d3.select('#visual');
// const width = +svg.attr('width');
// const height = +svg.attr('height');
//
// svg.selectAll('*').remove();
//
// const simulation = d3.forceSimulation()
// .force('link', d3.forceLink().id((d: any) => d.id))
// .force('charge', d3.forceManyBody().strength(100)) // Gravity force
// .force('collide', d3.forceCollide().radius(25).iterations(3)) // Repulsion force
// .force('center', d3.forceCenter(width / 2, height / 2)); // Position force
//
// const link = svg.append('g')
// .selectAll('line')
// .data(graph.links)
// .enter().append('line')
// .attr('stroke', 'grey')
// .attr('stroke-width', (d: any) => d.value);
//
// const node = svg.append('g')
// .selectAll('circle')
// .data(graph.nodes)
// .enter().append('circle')
// .attr('r', (d: any) => 5 * Math.log(d.size) / Math.log(10))
// .attr('fill', (d: any) => this.getColor(d.group / graph.nodes.length))
// .attr('id', (d: any) => 'node-' + d.group)
// .on('mouseover', (d: any) => {this.sliderValue = d.group; })
// .call(d3.drag()
// .on('start', dragstarted)
// .on('drag', dragged)
// .on('end', dragended));
//
// simulation
// .nodes(graph.nodes as any)
// .on('tick', ticked);
//
// simulation.force<any>('link')
// .links(graph.links);
//
// function ticked() {
// link
// .attr('x1', (d: any) => d.source.x)
// .attr('y1', (d: any) => d.source.y)
// .attr('x2', (d: any) => d.target.x)
// .attr('y2', (d: any) => d.target.y);
//
// node
// .attr('cx', (d: any) => d.x)
// .attr('cy', (d: any) => d.y);
// }
//
// function dragstarted(d) {
// if (!d3.event.active) {
// simulation.alphaTarget(0.1).restart();
// }
// d.fx = d.x;
// d.fy = d.y;
// }
//
// function dragged(d) {
// d.fx = d3.event.x;
// d.fy = d3.event.y;
// }
//
// function dragended(d) {
// if (!d3.event.active) {
// simulation.alphaTarget(0);
// }
// d.fx = null;
// d.fy = null;
// }
} }
getColor(value) { getColor(value) {
const hue=((1-value)*120).toString(10); const hue=((1-value)*120).toString(10);
return ["hsl(",hue,",100%,50%)"].join(""); return ["hsl(",hue,",100%,50%)"].join("");
......
import {EventEmitter, Injectable} from '@angular/core'; import {EventEmitter, Injectable} from '@angular/core';
import {ApiService, LshData, RawData, TableInfoData} from './api.service'; import {ApiService, LshData, Parameters, RawData, TableInfoData} from './api.service';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
/**
* This service acts as the state of the entire application. Components can subscribe to EventEmitters within this state to update their
* contents.
*/
export class StateService { export class StateService {
public loadingProgress: number = 0; /**
* These are all LSH specific variables. The variables can be accessed using the getters and setters
*/
private _rawData: RawData[]; private _rawData: RawData[];
private _lshData: LshData; private _lshData: LshData;
private _tableInfo: TableInfoData;
private _queryWindow: number[][]; private _queryWindow: number[][];
private _table: {[bucket: string]: number[]}[];
public _averageTable: {[bucket: string]: number[]};
private _weights: number[]; private _weights: number[];
private _currentTab: number;
private _labels = {}; private _labels = {};
private _sliderValue;
private _lshParameters: number[]; private _lshParameters: number[];
private states = [];
public windowSize = 120; public windowSize = 120;
public nrOfTables = 5; public nrOfTables = 5;
public hashSize = 5; public hashSize = 5;
public stepSize = 200; public stepSize = 200;
/**
* These are all GUI variables
*/
public loadingProgress = 0;
public querySelectionMode = true; public querySelectionMode = true;
private _currentTab: number;
private _sliderValue;
/**
* These are all EventEmitters. Subscribe to these if you want to be informed about an update in state.
*/
public onNewData: EventEmitter<void> = new EventEmitter<void>(); public onNewData: EventEmitter<void> = new EventEmitter<void>();
public onNewWindows: EventEmitter<void> = new EventEmitter<void>(); public onNewWindows: EventEmitter<void> = new EventEmitter<void>();
public onNewQuery: EventEmitter<void> = new EventEmitter<void>(); public onNewQuery: EventEmitter<void> = new EventEmitter<void>();
public onNewTable: EventEmitter<void> = new EventEmitter<void>();
public onNewTableInfo: EventEmitter<void> = new EventEmitter<void>();
public onNewLshData: EventEmitter<void> = new EventEmitter<void>(); public onNewLshData: EventEmitter<void> = new EventEmitter<void>();
public onNewLabels: EventEmitter<void> = new EventEmitter<void>(); public onNewLabels: EventEmitter<void> = new EventEmitter<void>();
...@@ -43,6 +47,9 @@ export class StateService { ...@@ -43,6 +47,9 @@ export class StateService {
this.initialize(); this.initialize();
} }
/**
* This function initializes the application. It retrieves the raw data and creates windows.
*/
async initialize(): Promise<void> { async initialize(): Promise<void> {
this.loadingProgress = 0; this.loadingProgress = 0;
await this.getRawData(); await this.getRawData();
...@@ -51,72 +58,73 @@ export class StateService { ...@@ -51,72 +58,73 @@ export class StateService {
this.loadingProgress = 100; this.loadingProgress = 100;
} }
/**
* This function resets the application. It re-creates the windows
*/
async reset(): Promise<void> { async reset(): Promise<void> {
this.loadingProgress = 50; this.loadingProgress = 50;
await this.createWindows(); await this.createWindows();
this.loadingProgress = 100; this.loadingProgress = 100;
} }
/**
* This function retrieves the raw data
*/
async getRawData(): Promise<void> { async getRawData(): Promise<void> {
this.rawData = await this.api.readFile(); this.rawData = await this.api.readFile();
} }
/**
* This function creates the windows on the server side
*/
async createWindows(): Promise<void> { async createWindows(): Promise<void> {
await this.api.createWindows(this.parameters); await this.api.createWindows(this.parameters);
this.onNewWindows.emit(); this.onNewWindows.emit();
} }
/**
* This function performs the first iteration of LSH
*/
async lshInitial(): Promise<void> { async lshInitial(): Promise<void> {
this._weights = Array(this._queryWindow.length).fill(1);
this.lshData = await this.api.lshInitial(this._queryWindow); this.lshData = await this.api.lshInitial(this._queryWindow);
console.log('data loaded');
this._lshParameters = this.lshData.parameters; this._lshParameters = this.lshData.parameters;
this._weights = [1, 1, 1];
this.createTable();
} }
/**
* This function performs every other iteration of LSH
*/
async update(labels, hashFunctions): Promise<void> { async update(labels, hashFunctions): Promise<void> {
this._weights = await this.api.getWeights(this._queryWindow, labels, this._weights, hashFunctions); this._weights = await this.api.getWeights(this._queryWindow, labels, this._weights, hashFunctions);
console.log(this._weights);
this.lshData = await this.api.lshUpdate(this._queryWindow, this._weights, this._lshParameters); this.lshData = await this.api.lshUpdate(this._queryWindow, this._weights, this._lshParameters);
this.createTable();
} }
/**
* This function retrieves additional information given a table
*/
async getTableInfo(table: number[][]): Promise<TableInfoData> { async getTableInfo(table: number[][]): Promise<TableInfoData> {
// console.log(this.tableInfo);
return await this.api.getTableInfo(table); return await this.api.getTableInfo(table);
} }
/**
* This function retrieves the query
*/
async getQueryWindow(windowIndex: number | {[index: number]: boolean}): Promise<number[][]> { async getQueryWindow(windowIndex: number | {[index: number]: boolean}): Promise<number[][]> {
this.queryWindow = await this.api.getQueryWindow(windowIndex); this.queryWindow = await this.api.getQueryWindow(windowIndex);
console.log(this.queryWindow); console.log(this.queryWindow);
return this._queryWindow; return this._queryWindow;
} }
/**
* This function retrieves the window given the window index
*/
async getWindow(indices: number[]): Promise<number[][][]> { async getWindow(indices: number[]): Promise<number[][][]> {
return await this.api.getWindowByIndices(indices); return await this.api.getWindowByIndices(indices);
} }
async createTable() { /**
console.log('setting table param'); * These are all setters and getters
this.table = this.lshData.tables; */
console.log('table param set');
const averageTable = {};
const length = this.lshData.average_distances.length;
const median = this.lshData.average_distances[Math.ceil(length / 2)];