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 +51 -0
- package/dress.js +201 -0
- package/dress_wasm.wasm +0 -0
- package/package.json +40 -0
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
|
+
}
|
package/dress_wasm.wasm
ADDED
|
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
|
+
}
|