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 {
export interface LshData {
candidates: number[][][];
tables: {[bucket: string]: number[]}[];
distances: number[][][];
average_candidates: number[];
average_distances: number[];
tables: {[bucket: string]: number[]}[];
average_table: {[bucket: string]: number[]};
samples: number[];
distances: number[][][];
hash_functions: number[][];
parameters?: number[];
}
......@@ -25,23 +26,37 @@ export interface TableInfoData {
distances: number[][];
}
export interface Parameters {
windowsize: number;
hashsize: number;
tablesize: number;
stepsize: number;
}
@Injectable({
providedIn: 'root'
})
/**
* This service acts as the interface between the client and server side.
*/
export class ApiService {
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[]> {
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();
}
// 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 response = await fetch('http://127.0.0.1:5000/create-mts-windows', {
await fetch('http://127.0.0.1:5000/create-windows', {
method: 'POST',
headers: {
'Accept': 'application/json',
......@@ -51,32 +66,39 @@ export class ApiService {
});
}
// Calculate parameters for LSH + find candidates using LSH
async lshInitial(query): Promise<LshData> {
const response = await fetch('http://127.0.0.1:5000/initialize', {
/**
* Get weights which will be applied to the LSH hash functions
*/
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',
headers: {
'Accept': '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();
}
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',
headers: {
'Accept': '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();
}
// Find candidates using LSH with weights
/**
* Do another iteration of LSH, with weights, and return important information
*/
async lshUpdate(query, weights, parameters): Promise<LshData> {
const response = await fetch('http://127.0.0.1:5000/update', {
method: 'POST',
......@@ -89,20 +111,24 @@ export class ApiService {
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', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({window})
body: JSON.stringify({indices})
});
return await response.json();
}
// Get data of a window by indices
/**
* Get data of a window by indices
*/
async getWindowByIndices(indices: number[]): Promise<number[][][]> {
const response = await fetch('http://127.0.0.1:5000/window', {
method: 'POST',
......@@ -115,14 +141,17 @@ export class ApiService {
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', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({windows})
body: JSON.stringify({table})
});
return await response.json();
}
......
import {Component, OnInit, ViewChild} from '@angular/core';
import {StateService} from '../state.service';
import * as d3 from 'd3';
import {TableInfoData} from '../api.service';
@Component({
selector: 'app-progress-view',
......@@ -21,12 +22,14 @@ export class ProgressViewComponent implements OnInit {
constructor(private state: StateService) { }
ngOnInit(): void {
this.state.onNewTableInfo.subscribe(() => { this.showgraph(); });
this.state.onNewTableInfo.subscribe(() => { this.showHistogram(); });
this.state.onNewLshData.subscribe(() => {
this.showgraph();
this.showHistogram();
});
}
showHistogram() {
const table = this.state._averageTable;
const table = this.state.lshData.average_table;
this.hist = {
data: [{
x: Object.keys(table),
......@@ -162,7 +165,7 @@ export class ProgressViewComponent implements OnInit {
d3.selectAll('circle').transition().style('stroke', undefined);
d3.select('#node-' + v.value).transition().style('stroke', 'black').style('stroke-width', 20);
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;
});
this.hist = data;
......@@ -177,102 +180,15 @@ export class ProgressViewComponent implements OnInit {
}
public get table() {
return this.state._averageTable;
return this.state.lshData.average_table;
}
async showgraph() {
const nodes = [];
const links = [];
const keys = Object.keys(this.table);
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;
// }
const tableInfo: TableInfoData = await this.state.getTableInfo(Object.values(this.state.lshData.average_table));
this.hoverPlot(tableInfo.prototypes);
const distances = tableInfo.distances;
}
getColor(value) {
const hue=((1-value)*120).toString(10);
return ["hsl(",hue,",100%,50%)"].join("");
......
import {EventEmitter, Injectable} from '@angular/core';
import {ApiService, LshData, RawData, TableInfoData} from './api.service';
import {ApiService, LshData, Parameters, RawData, TableInfoData} from './api.service';
@Injectable({
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 {
public loadingProgress: number = 0;
/**
* These are all LSH specific variables. The variables can be accessed using the getters and setters
*/
private _rawData: RawData[];
private _lshData: LshData;
private _tableInfo: TableInfoData;
private _queryWindow: number[][];
private _table: {[bucket: string]: number[]}[];
public _averageTable: {[bucket: string]: number[]};
private _weights: number[];
private _currentTab: number;
private _labels = {};
private _sliderValue;
private _lshParameters: number[];
private states = [];
public windowSize = 120;
public nrOfTables = 5;
public hashSize = 5;
public stepSize = 200;
/**
* These are all GUI variables
*/
public loadingProgress = 0;
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 onNewWindows: 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 onNewLabels: EventEmitter<void> = new EventEmitter<void>();
......@@ -43,6 +47,9 @@ export class StateService {
this.initialize();
}
/**
* This function initializes the application. It retrieves the raw data and creates windows.
*/
async initialize(): Promise<void> {
this.loadingProgress = 0;
await this.getRawData();
......@@ -51,72 +58,73 @@ export class StateService {
this.loadingProgress = 100;
}
/**
* This function resets the application. It re-creates the windows
*/
async reset(): Promise<void> {
this.loadingProgress = 50;
await this.createWindows();
this.loadingProgress = 100;
}
/**
* This function retrieves the raw data
*/
async getRawData(): Promise<void> {
this.rawData = await this.api.readFile();
}
/**
* This function creates the windows on the server side
*/
async createWindows(): Promise<void> {
await this.api.createWindows(this.parameters);
this.onNewWindows.emit();
}
/**
* This function performs the first iteration of LSH
*/
async lshInitial(): Promise<void> {
this._weights = Array(this._queryWindow.length).fill(1);
this.lshData = await this.api.lshInitial(this._queryWindow);
console.log('data loaded');
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> {
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.createTable();
}
/**
* This function retrieves additional information given a table
*/
async getTableInfo(table: number[][]): Promise<TableInfoData> {
// console.log(this.tableInfo);
return await this.api.getTableInfo(table);
}
/**
* This function retrieves the query
*/
async getQueryWindow(windowIndex: number | {[index: number]: boolean}): Promise<number[][]> {
this.queryWindow = await this.api.getQueryWindow(windowIndex);
console.log(this.queryWindow);
return this._queryWindow;
}
/**
* This function retrieves the window given the window index
*/
async getWindow(indices: number[]): Promise<number[][][]> {
return await this.api.getWindowByIndices(indices);
}
async createTable() {
console.log('setting table param');
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)];
const stepsize = median / 10;
const indices: number[] = this.lshData.average_distances.map((x) => x > median * 2 ? 19 : Math.floor(x / stepsize));
this.lshData.average_candidates.forEach((candidate: number, index: number) => {
if (averageTable[indices[index]] === undefined)
{
averageTable[indices[index]] = [];
}
averageTable[indices[index]].push(candidate);
});
this._averageTable = averageTable;
console.log('table created');
this.tableInfo = await this.getTableInfo(Object.values(this._averageTable));
}
/**
* These are all setters and getters
*/
public set rawData(v: RawData[]) {
this._rawData = v;
console.log(this._rawData);
......@@ -137,26 +145,6 @@ export class StateService {
return this._lshData;
}
public set tableInfo(v: TableInfoData) {
this._tableInfo = v;
this.onNewTableInfo.emit();
}
public get tableInfo(): TableInfoData {
return this._tableInfo;
}
public set table(v: {[bucket: string]: number[]}[]) {
console.log(v);
this._table = v;
console.log('emitting onNewTable');
this.onNewTable.emit();
}
public get table(): {[bucket: string]: number[]}[] {
return this._table;
}
public set labels(v) {
this._labels = v;
this.onNewLabels.emit();
......@@ -197,7 +185,7 @@ export class StateService {
return this._lshParameters;
}
public get parameters(): {[parameter: string]: number} {
public get parameters(): Parameters {
return {
windowsize: this.windowSize,
hashsize: this.hashSize,
......
......@@ -15,7 +15,7 @@ export class TableOverviewComponent implements OnInit {
constructor(private state: StateService) { }
ngOnInit(): void {
this.state.onNewTable.subscribe(() => {
this.state.onNewLshData.subscribe(() => {
this.createHistograms();
this.createPrototypes();
});
......@@ -132,7 +132,7 @@ export class TableOverviewComponent implements OnInit {
console.log('creating table histograms');
this.subplots = [];
this.averages = [];
const tables = this.state.table;
const tables = this.state.lshData.tables;
console.log('start of table histograms');
tables.forEach((table, index) => {
console.log(index);
......@@ -181,7 +181,7 @@ export class TableOverviewComponent implements OnInit {
// }
public get tables() {
return this.state.table;
return this.state.lshData.tables;
}
public get visible() {
......
......@@ -20,18 +20,23 @@
</component>
<component name="ChangeListManager">
<list default="true" id="556080ba-825c-4b55-a92a-867a4df4fb32" name="Default Changelist" comment="">
<change beforePath="$PROJECT_DIR$/../AngularApp/prototype/src/app/api.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../AngularApp/prototype/src/app/api.service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../AngularApp/prototype/src/app/labeling-window/labeling-window.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../AngularApp/prototype/src/app/labeling-window/labeling-window.component.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../AngularApp/prototype/src/app/overview-window/overview-window.component.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../AngularApp/prototype/src/app/overview-window/overview-window.component.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/../AngularApp/prototype/src/app/state.service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../AngularApp/prototype/src/app/state.service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/main.py" beforeDir="false" afterPath="$PROJECT_DIR$/main.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pseudo.py" beforeDir="false" afterPath="$PROJECT_DIR$/pseudo.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/.." />
</component>
......@@ -50,6 +55,10 @@
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/data" />
<recent name="$PROJECT_DIR$/libs" />
</key>
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$" />
</key>
......@@ -144,6 +153,10 @@
<screen x="72" y="27" width="1848" height="1053" />
</state>
<state x="779" y="311" width="424" height="491" key="FileChooserDialogImpl/72.27.1848.1053@72.27.1848.1053" timestamp="1606260652750" />
<state x="687" y="162" width="618" height="783" key="find.popup" timestamp="1606586473850">
<screen x="72" y="27" width="1848" height="1053" />
</state>
<state x="687" y="162" width="618" height="783" key="find.popup/72.27.1848.1053@72.27.1848.1053" timestamp="1606586473850" />
<state x="659" y="259" width="672" height="678" key="search.everywhere.popup" timestamp="1604929652702">
<screen x="72" y="27" width="1848" height="1053" />
</state>
......