diff --git a/README.md b/README.md index 2c4e2fb..30d11e5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ - [NeTS](#nets) - [Basic Functionality](#basic-functionality) - [Extra functionality](#extra-functionality) +- [Quadruplets](#quadruplets) ## NeTS @@ -59,3 +60,9 @@ const net = await loadAdtacencyMatrix("file_name.csv"); For testing, you can use the `randomNetworkGen` function. It randomly generates a network with the given arguments. + +## Quadruplets + +The quadruplets algorithm has the same exponetial time complexity as +the edge pair algorithm. +However, the less dense the network, the faster the quadruplets algorithm can get. diff --git a/algorithms.ts b/algorithms.ts index 1dbaf0d..b4fe804 100644 --- a/algorithms.ts +++ b/algorithms.ts @@ -1,5 +1,5 @@ import { Network } from "./network.ts"; -import { ParsedCSV, base_id } from "./enums.ts"; +import { ParsedCSV, base_id, NetworkArgs } from "./enums.ts"; /** * Tries to generate a network with the given number of nodes and edges. @@ -9,7 +9,7 @@ import { ParsedCSV, base_id } from "./enums.ts"; * @param {boolean} [args.is_directed] * @returns Network */ -export function randomNetworkGen(args: { +export function genRandomNetwork(args: { number_vertices: number; number_edges: number; is_directed?: boolean; @@ -22,21 +22,42 @@ export function randomNetworkGen(args: { for (let vertex = 0; vertex < number_vertices; vertex++) net.addVertex({ id: vertex }); - args.edge_tries ??= 20; - while (net.edges.size < number_edges && args.edge_tries > 0) { + let edge_tries = args.edge_tries ?? 30; + while (net.edges.size < number_edges && edge_tries > 0) { const from = Math.floor(Math.random() * number_vertices); const to = Math.floor(Math.random() * number_vertices); - try { - net.addEdge({ from, to, do_force: false }); - } catch (e) { - args.edge_tries--; - console.log(e.message); - } + if (from !== to && net.addEdge({ from, to, do_force: false })) + edge_tries = args.edge_tries ?? 30; + + edge_tries--; } return net; } +export function genCompleteNetwork( + size: number, + args: NetworkArgs = {} +): Network { + const complete_net = new Network( + Object.assign(args, { + vertex_limit: size, + edge_limit: Math.floor((size * (size - 1)) / 2), + }) + ); + + for (let vertex = 0; vertex < size; vertex++) { + complete_net.addVertex({ id: vertex }); + complete_net.vertices.forEach((v) => { + if (v.id !== vertex) { + complete_net.addEdge({ from: v.id, to: vertex }); + } + }); + } + + return complete_net; +} + /** * Reads an [adjacency matrix](https://www.wikiwand.com/en/Adjacency_matrix) CSV and returns a network object. * @param {string} file_name diff --git a/network.ts b/network.ts index 8f1c3b2..39fbd1d 100644 --- a/network.ts +++ b/network.ts @@ -181,7 +181,7 @@ export class Network { /** * @param {EdgeArgs} args */ - addEdge(args: EdgeArgs) { + addEdge(args: EdgeArgs): boolean { args.do_force ??= true; args.weight ??= 1; @@ -203,12 +203,13 @@ export class Network { if (!this.vertices.has(args.to)) this.addVertex({ id: args.to }); } - if (!this.is_multigraph && this.hasEdge(args.from, args.to)) return; + if (!this.is_multigraph && this.hasEdge(args.from, args.to)) return false; // throw { message: ERROR.NOT_MULTIGRAPH }; if (!this.is_directed) [args.from, args.to] = [args.from, args.to].sort(); this.edges.set(args.id, new Edge(args)); + return true; } /** diff --git a/tester.ts b/tester.ts index 3ecb823..1aa3832 100644 --- a/tester.ts +++ b/tester.ts @@ -81,35 +81,159 @@ function getTestTime(): string { ); } -const net_csv = await nex.loadAdjacencyMatrix("./data/networkMatrix.csv"); +async function quadrupletsAdjacencyMatrixTimeTesting() { + const net_csv = await nex.loadAdjacencyMatrix( + "./data/FTXAdjacencyMatrix.csv" + ); -// let test_data = valuesTest(net_csv) + "\n" + algorithmTest(net_csv); + let algo_start = Date.now(); + const quad_csv_pair = net_csv.quadrupletsEdgePairing(); + const pair_time = (Date.now() - algo_start) / 1000; + console.log("Edge pairing time taken:", pair_time); + console.log("Edge pairing size:", quad_csv_pair.length); + console.log("----------"); + algo_start = Date.now(); + const quad = net_csv.quadruplets(); + const quad_time = (Date.now() - algo_start) / 1000; + // console.log(quad_csv_pair.map((c) => [...c.simple_edge_list])); + console.log("Quadruplets time taken:", quad_time); + console.log("Quad size:", quad.length); +} -// const end_time = new Date().getTime(); -// const elapsed_time = (end_time - start_time) / 1000; +async function mainTest() { + const net_csv = await nex.loadAdjacencyMatrix( + "./data/FTXAdjacencyMatrix.csv" + ); + let test_data = valuesTest(net_csv) + "\n" + algorithmTest(net_csv); + const end_time = new Date().getTime(); + const elapsed_time = (end_time - start_time) / 1000; + test_data += "\nElapsed time: " + elapsed_time; + Deno.writeTextFile( + `./data/test_${getTestTime()}_${Math.floor(200 * Math.random())}.txt`, + test_data + ); +} -// test_data += "\nElapsed time: " + elapsed_time; +function compareQuadAlgorithms(net: nets.Network, debug = false) { + let start = Date.now(); -// Deno.writeTextFile( -// `./data/test_${getTestTime()}_${Math.floor(200 * Math.random())}.txt`, -// test_data -// ); + const quad = net.quadruplets(); + const quad_time = (Date.now() - start) / 1000; -const randomNet = nex.randomNetworkGen({ - number_vertices: 6, - number_edges: 10, -}); + start = Date.now(); + const quad_pair = net.quadrupletsEdgePairing(); + const quad_pair_time = (Date.now() - start) / 1000; -// nex.writeAdjacencyMatrix(randomNet); + if (debug) { + console.log("Quad algorithm"); + console.log(quad.length); + console.log("Time taken: ", quad_time); -const quad_time = Date.now(); + console.log("------"); + + console.log("Edge pairing algorithm"); + console.log(quad_pair.length); + console.log("Time taken: ", quad_pair_time); + } + + return [quad_time, quad_pair_time]; +} + +function getQuadTime(net: nets.Network): number { + const start = Date.now(); + net.quadruplets(); + return (Date.now() - start) / 1000; +} +function getPairTime(net: nets.Network): number { + const start = Date.now(); + net.quadrupletsEdgePairing(); + return (Date.now() - start) / 1000; +} -const quad = randomNet.quadruplets(); +function quadEfficiencyTest(num = 20) { + const quad_data: number[] = []; + for (let n = 4; n < num; n++) { + console.log(n); + const complete_net = nex.genCompleteNetwork(n); + quad_data.push(getQuadTime(complete_net)); + } -nex.writeAdjacencyMatrix(randomNet, "test_random_quad.csv"); + Deno.writeTextFile(`./data/quad_data.json`, JSON.stringify(quad_data)); +} + +function pairEfficiencyTest(num = 20) { + const quad_data: number[] = []; + for (let n = 4; n < num; n++) { + console.log(n); + const complete_net = nex.genCompleteNetwork(n); + quad_data.push(getPairTime(complete_net)); + } + + Deno.writeTextFile(`./data/quad_pair_data.json`, JSON.stringify(quad_data)); +} + +function randomNetEfficiencyTest( + number_vertices = 20, + edges_num = { min: 10, max: 50 }, + name = `random_efficiency_test.json` +) { + const quad: number[] = []; + const pair: number[] = []; + const net_list: number[] = []; + + for ( + let number_edges = edges_num.min; + number_edges < edges_num.max; + number_edges++ + ) { + console.log(number_edges); + const net = nex.genRandomNetwork({ number_vertices, number_edges }); + + if (!net_list.includes(net.edges.size)) { + quad.push(getQuadTime(net)); + pair.push(getPairTime(net)); + + net_list.push(net.edges.size); + } + } + + Deno.writeTextFile( + `./data/${name}`, + JSON.stringify({ quad, pair, number_vertices, net_list }) + ); +} + +function quadrupletsEfficiencyTest(num = 20) { + quadEfficiencyTest(num); + pairEfficiencyTest(num); +} + +function generalTesting() { + // const test_net = new nets.Network(); + // try { + // test_net.addEdgeList([ + // [1, 0], + // [1, 2], + // [1, 3], + // [1, 4], + // [1, 5], + // [0, 6], + // [0, 7], + // [1, 8], + // [6, 7], + // [2, 5], + // [5, 4], + // [3, 8], + // [4, 3], + // ]); + // } catch (e) { + // console.log(e); + // } + // const random_net = nex.genRandomNetwork({ + // number_vertices: 15, + // number_edges: 20, + // }); +} -console.log( - quad.map((c) => [...c.edges.values()]), - quad.length, - randomNet.edges -); +// compareQuadAlgorithms(nex.genCompleteNetwork(10), true); +randomNetEfficiencyTest(20, { min: 20, max: 120 }, "rand_dense.json");