dress-graph 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dress.d.ts ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * TypeScript type declarations for the DRESS WebAssembly wrapper.
3
+ */
4
+
5
+ export declare const Variant: {
6
+ readonly UNDIRECTED: 0;
7
+ readonly DIRECTED: 1;
8
+ readonly FORWARD: 2;
9
+ readonly BACKWARD: 3;
10
+ };
11
+
12
+ export interface DressOptions {
13
+ /** Number of vertices (vertex ids must be in 0..numVertices-1) */
14
+ numVertices: number;
15
+ /** Edge source vertices (0-based) */
16
+ sources: Int32Array | number[];
17
+ /** Edge target vertices (0-based) */
18
+ targets: Int32Array | number[];
19
+ /** Optional edge weights (same length as sources) */
20
+ weights?: Float64Array | number[] | null;
21
+ /** Graph variant (default: Variant.UNDIRECTED) */
22
+ variant?: number;
23
+ /** Maximum fitting iterations (default: 100) */
24
+ maxIterations?: number;
25
+ /** Convergence threshold (default: 1e-6) */
26
+ epsilon?: number;
27
+ /** Pre-compute neighbourhood intercepts (default: true) */
28
+ precomputeIntercepts?: boolean;
29
+ }
30
+
31
+ export interface DressResult {
32
+ /** Edge source vertices */
33
+ sources: Int32Array;
34
+ /** Edge target vertices */
35
+ targets: Int32Array;
36
+ /** Per-edge variant-specific weights */
37
+ edgeWeight: Float64Array;
38
+ /** Per-edge dress similarity values */
39
+ edgeDress: Float64Array;
40
+ /** Per-node aggregated dress similarity */
41
+ nodeDress: Float64Array;
42
+ /** Number of iterations performed */
43
+ iterations: number;
44
+ /** Final maximum per-edge change */
45
+ delta: number;
46
+ }
47
+
48
+ /**
49
+ * Run the DRESS iterative fitting algorithm on an edge list.
50
+ */
51
+ export declare function dressFit(opts: DressOptions): Promise<DressResult>;
package/dress.js ADDED
@@ -0,0 +1,201 @@
1
+ /**
2
+ * dress.js — High-level JavaScript API for the DRESS WebAssembly module.
3
+ *
4
+ * Works in both browser and Node.js environments.
5
+ *
6
+ * Usage (ES module):
7
+ *
8
+ * import { dressFit } from './dress.js';
9
+ *
10
+ * const result = await dressFit({
11
+ * numVertices: 4,
12
+ * sources: [0, 1, 2, 0],
13
+ * targets: [1, 2, 3, 3],
14
+ * });
15
+ * console.log(result.edgeDress); // Float64Array
16
+ * console.log(result.iterations); // number
17
+ */
18
+
19
+ // ── Module loader ───────────────────────────────────────────────────
20
+
21
+ let _modulePromise = null;
22
+
23
+ /**
24
+ * Load (or return cached) the Emscripten WASM module.
25
+ * Auto-detects browser vs Node.js.
26
+ */
27
+ async function getModule() {
28
+ if (_modulePromise) return _modulePromise;
29
+
30
+ _modulePromise = (async () => {
31
+ // In Node.js, require the glue file. In the browser, it should
32
+ // be loaded via <script> before this module, or bundled.
33
+ let createDressModule;
34
+ if (typeof globalThis.createDressModule === 'function') {
35
+ createDressModule = globalThis.createDressModule;
36
+ } else {
37
+ // Node.js path — use createRequire for CJS interop
38
+ const { createRequire } = await import('module');
39
+ const require = createRequire(import.meta.url);
40
+ createDressModule = require('./dress_wasm.cjs');
41
+ }
42
+ return createDressModule();
43
+ })();
44
+
45
+ return _modulePromise;
46
+ }
47
+
48
+ // ── Variant enum ────────────────────────────────────────────────────
49
+
50
+ /** Graph variant constants matching dress.h */
51
+ export const Variant = Object.freeze({
52
+ UNDIRECTED: 0,
53
+ DIRECTED: 1,
54
+ FORWARD: 2,
55
+ BACKWARD: 3,
56
+ });
57
+
58
+ // ── Core API ────────────────────────────────────────────────────────
59
+
60
+ /**
61
+ * @typedef {Object} DressOptions
62
+ * @property {number} numVertices - Number of vertices (ids in 0..N-1)
63
+ * @property {Int32Array|number[]} sources - Edge source vertices (0-based)
64
+ * @property {Int32Array|number[]} targets - Edge target vertices (0-based)
65
+ * @property {Float64Array|number[]|null} [weights] - Optional edge weights
66
+ * @property {number} [variant=0] - Variant (see Variant enum)
67
+ * @property {number} [maxIterations=100] - Max fitting iterations
68
+ * @property {number} [epsilon=1e-6] - Convergence threshold
69
+ * @property {boolean} [precomputeIntercepts=true] - Pre-compute intercepts
70
+ */
71
+
72
+ /**
73
+ * @typedef {Object} DressResult
74
+ * @property {Int32Array} sources - Edge source vertices
75
+ * @property {Int32Array} targets - Edge target vertices
76
+ * @property {Float64Array} edgeWeight - Per-edge variant weights
77
+ * @property {Float64Array} edgeDress - Per-edge dress similarity
78
+ * @property {Float64Array} nodeDress - Per-node aggregated similarity
79
+ * @property {number} iterations - Iterations performed
80
+ * @property {number} delta - Final max per-edge change
81
+ */
82
+
83
+ /**
84
+ * Run the DRESS iterative fitting algorithm.
85
+ *
86
+ * @param {DressOptions} opts
87
+ * @returns {Promise<DressResult>}
88
+ */
89
+ export async function dressFit(opts) {
90
+ const M = await getModule();
91
+
92
+ const N = opts.numVertices;
93
+ const E = opts.sources.length;
94
+
95
+ if (opts.targets.length !== E) {
96
+ throw new Error(`sources (${E}) and targets (${opts.targets.length}) must have equal length`);
97
+ }
98
+ if (opts.weights && opts.weights.length !== E) {
99
+ throw new Error(`weights (${opts.weights.length}) must equal edge count (${E})`);
100
+ }
101
+
102
+ const variant = opts.variant ?? Variant.UNDIRECTED;
103
+ const maxIter = opts.maxIterations ?? 100;
104
+ const epsilon = opts.epsilon ?? 1e-6;
105
+ const precompute = (opts.precomputeIntercepts ?? true) ? 1 : 0;
106
+
107
+ // Allocate C arrays (ownership transfers to dress.c — freed by free_dress_graph)
108
+ const uPtr = M._malloc(E * 4);
109
+ const vPtr = M._malloc(E * 4);
110
+
111
+ // Write source/target data into WASM heap
112
+ const heap32 = M.HEAP32;
113
+ const src = opts.sources;
114
+ const tgt = opts.targets;
115
+ for (let i = 0; i < E; i++) {
116
+ heap32[(uPtr >> 2) + i] = src[i];
117
+ heap32[(vPtr >> 2) + i] = tgt[i];
118
+ }
119
+
120
+ // Weights (nullable)
121
+ let wPtr = 0; // NULL
122
+ if (opts.weights) {
123
+ wPtr = M._malloc(E * 8);
124
+ const heapF64 = M.HEAPF64;
125
+ const w = opts.weights;
126
+ for (let i = 0; i < E; i++) {
127
+ heapF64[(wPtr >> 3) + i] = w[i];
128
+ }
129
+ }
130
+
131
+ // Call init_dress_graph
132
+ const g = M._init_dress_graph(N, E, uPtr, vPtr, wPtr, variant, precompute);
133
+ if (g === 0) {
134
+ throw new Error('init_dress_graph returned NULL');
135
+ }
136
+
137
+ // Allocate output params
138
+ const iterPtr = M._malloc(4); // int*
139
+ const deltaPtr = M._malloc(8); // double*
140
+
141
+ // Call fit
142
+ M._fit(g, maxIter, epsilon, iterPtr, deltaPtr);
143
+
144
+ const iterations = M.getValue(iterPtr, 'i32');
145
+ const delta = M.getValue(deltaPtr, 'double');
146
+
147
+ // Read results from the C struct.
148
+ // Struct field offsets (wasm32 — all pointers are 4 bytes):
149
+ // offset 0: variant (i32)
150
+ // offset 4: N (i32)
151
+ // offset 8: E (i32)
152
+ // offset 12: *U (ptr32)
153
+ // offset 16: *V (ptr32)
154
+ // offset 20: *adj_offset (ptr32)
155
+ // offset 24: *adj_target (ptr32)
156
+ // offset 28: *adj_edge_idx (ptr32)
157
+ // offset 32: *edge_weight (ptr32)
158
+ // offset 36: *edge_dress (ptr32)
159
+ // offset 40: *edge_dress_next (ptr32)
160
+ // offset 44: *node_dress (ptr32)
161
+ const ewPtr = M.getValue(g + 32, 'i32'); // edge_weight pointer
162
+ const edPtr = M.getValue(g + 36, 'i32'); // edge_dress pointer
163
+ const ndPtr = M.getValue(g + 44, 'i32'); // node_dress pointer
164
+
165
+ // Copy results into JS-owned typed arrays
166
+ const edgeWeight = new Float64Array(E);
167
+ const edgeDress = new Float64Array(E);
168
+ const nodeDress = new Float64Array(N);
169
+
170
+ const heapF64 = M.HEAPF64;
171
+ for (let i = 0; i < E; i++) {
172
+ edgeWeight[i] = heapF64[(ewPtr >> 3) + i];
173
+ edgeDress[i] = heapF64[(edPtr >> 3) + i];
174
+ }
175
+ for (let i = 0; i < N; i++) {
176
+ nodeDress[i] = heapF64[(ndPtr >> 3) + i];
177
+ }
178
+
179
+ // Copy sources/targets before free
180
+ const sourcesOut = new Int32Array(E);
181
+ const targetsOut = new Int32Array(E);
182
+ for (let i = 0; i < E; i++) {
183
+ sourcesOut[i] = heap32[(uPtr >> 2) + i];
184
+ targetsOut[i] = heap32[(vPtr >> 2) + i];
185
+ }
186
+
187
+ // Clean up
188
+ M._free_dress_graph(g);
189
+ M._free(iterPtr);
190
+ M._free(deltaPtr);
191
+
192
+ return {
193
+ sources: sourcesOut,
194
+ targets: targetsOut,
195
+ edgeWeight: edgeWeight,
196
+ edgeDress: edgeDress,
197
+ nodeDress: nodeDress,
198
+ iterations: iterations,
199
+ delta: delta,
200
+ };
201
+ }
Binary file
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "dress-graph",
3
+ "version": "0.1.0",
4
+ "description": "DRESS — Diffusive Recursive Structural Similarity on Graphs (WebAssembly)",
5
+ "type": "module",
6
+ "main": "dress.js",
7
+ "types": "dress.d.ts",
8
+ "files": [
9
+ "dress.js",
10
+ "dress.d.ts",
11
+ "dress_wasm.js",
12
+ "dress_wasm.wasm"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "import": "./dress.js",
17
+ "types": "./dress.d.ts"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "build": "bash build.sh",
22
+ "build:debug": "bash build.sh --debug",
23
+ "test": "node test.mjs"
24
+ },
25
+ "keywords": [
26
+ "graph",
27
+ "similarity",
28
+ "edge-similarity",
29
+ "network",
30
+ "community-detection",
31
+ "wasm",
32
+ "webassembly"
33
+ ],
34
+ "license": "MIT",
35
+ "author": "Eduar Castrillo Velilla",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "https://github.com/velicast/dress-graph"
39
+ }
40
+ }