dress-graph 0.3.1 → 0.5.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/README.md +7 -16
- package/dress.d.ts +29 -1
- package/dress.js +166 -7
- package/package.json +2 -2
- package/dress_wasm.wasm +0 -0
package/README.md
CHANGED
|
@@ -2,28 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
**A Continuous Framework for Structural Graph Refinement**
|
|
4
4
|
|
|
5
|
-
DRESS is a
|
|
6
|
-
At depth k, higher-order DRESS is **provably at least as powerful as (k+2)-WL**
|
|
7
|
-
in expressiveness — the base algorithm (k=0) already matches 2-WL, and each
|
|
8
|
-
level adds one WL dimension.
|
|
9
|
-
Yet it is dramatically cheaper to compute: a single DRESS run costs
|
|
10
|
-
O(I · m · d_max) where I is the number of iterations, and depth-k requires
|
|
11
|
-
C(n,k) independent runs — a total of O(C(n,k) · I · m · d_max), compared to
|
|
12
|
-
O(n^(k+3)) for (k+2)-WL. Space complexity is O(n + m), compared to
|
|
13
|
-
O(n^(k+2)) for (k+2)-WL.
|
|
14
|
-
The algorithm is embarrassingly parallel in two orthogonal ways — across the
|
|
15
|
-
C(n,k) subproblems and across edge updates within each iteration — enabling
|
|
16
|
-
distributed/cloud and multi-core/GPU/SIMD implementations.
|
|
5
|
+
DRESS is a deterministic, parameter-free framework that iteratively refines the structural similarity of edges in a graph to produce a canonical fingerprint: a real-valued edge vector, obtained by converging a non-linear dynamical system to its unique fixed point. The fingerprint is isomorphism-invariant by construction, numerically stable (no overflow, no error amplification, no undefined behavior), fast and embarrassingly parallel to compute: DRESS total runtime is O(I * m * d_max) for I iterations to convergence, and convergence is guaranteed by Birkhoff contraction.
|
|
17
6
|
|
|
18
7
|
## Quick start
|
|
19
8
|
|
|
20
9
|
```javascript
|
|
21
|
-
|
|
10
|
+
import { dressFit } from 'dress-graph';
|
|
22
11
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
12
|
+
const result = await dressFit({
|
|
13
|
+
numVertices: 4,
|
|
14
|
+
sources: [0, 1, 2, 0],
|
|
15
|
+
targets: [1, 2, 3, 3],
|
|
26
16
|
});
|
|
17
|
+
console.log(result.edgeDress);
|
|
27
18
|
```
|
|
28
19
|
|
|
29
20
|
For the full API and documentation, see the [main repository](https://github.com/velicast/dress-graph).
|
package/dress.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ export interface DressOptions {
|
|
|
24
24
|
maxIterations?: number;
|
|
25
25
|
/** Convergence threshold (default: 1e-6) */
|
|
26
26
|
epsilon?: number;
|
|
27
|
-
/** Pre-compute neighbourhood intercepts (default:
|
|
27
|
+
/** Pre-compute neighbourhood intercepts (default: false) */
|
|
28
28
|
precomputeIntercepts?: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -50,6 +50,33 @@ export interface DressResult {
|
|
|
50
50
|
*/
|
|
51
51
|
export declare function dressFit(opts: DressOptions): Promise<DressResult>;
|
|
52
52
|
|
|
53
|
+
export interface DressGraphOptions {
|
|
54
|
+
/** Number of vertices (vertex ids must be in 0..numVertices-1) */
|
|
55
|
+
numVertices: number;
|
|
56
|
+
/** Edge source vertices (0-based) */
|
|
57
|
+
sources: Int32Array | number[];
|
|
58
|
+
/** Edge target vertices (0-based) */
|
|
59
|
+
targets: Int32Array | number[];
|
|
60
|
+
/** Optional edge weights (same length as sources) */
|
|
61
|
+
weights?: Float64Array | number[] | null;
|
|
62
|
+
/** Graph variant (default: Variant.UNDIRECTED) */
|
|
63
|
+
variant?: number;
|
|
64
|
+
/** Pre-compute neighbourhood intercepts (default: false) */
|
|
65
|
+
precomputeIntercepts?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A persistent DRESS graph supporting repeated fit / get calls.
|
|
70
|
+
*/
|
|
71
|
+
export declare class DressGraph {
|
|
72
|
+
private constructor();
|
|
73
|
+
static create(opts: DressGraphOptions): Promise<DressGraph>;
|
|
74
|
+
fit(maxIterations?: number, epsilon?: number): { iterations: number; delta: number };
|
|
75
|
+
get(u: number, v: number, maxIterations?: number, epsilon?: number, edgeWeight?: number): number;
|
|
76
|
+
result(): DressResult;
|
|
77
|
+
free(): void;
|
|
78
|
+
}
|
|
79
|
+
|
|
53
80
|
export interface DeltaDressOptions {
|
|
54
81
|
/** Number of vertices (vertex ids must be in 0..numVertices-1) */
|
|
55
82
|
numVertices: number;
|
|
@@ -89,3 +116,4 @@ export interface DeltaDressResult {
|
|
|
89
116
|
* all k-vertex subsets and measuring edge similarity changes.
|
|
90
117
|
*/
|
|
91
118
|
export declare function deltaDressFit(opts: DeltaDressOptions): Promise<DeltaDressResult>;
|
|
119
|
+
|
package/dress.js
CHANGED
|
@@ -66,7 +66,7 @@ export const Variant = Object.freeze({
|
|
|
66
66
|
* @property {number} [variant=0] - Variant (see Variant enum)
|
|
67
67
|
* @property {number} [maxIterations=100] - Max fitting iterations
|
|
68
68
|
* @property {number} [epsilon=1e-6] - Convergence threshold
|
|
69
|
-
* @property {boolean} [precomputeIntercepts=
|
|
69
|
+
* @property {boolean} [precomputeIntercepts=false] - Pre-compute intercepts
|
|
70
70
|
*/
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -102,7 +102,7 @@ export async function dressFit(opts) {
|
|
|
102
102
|
const variant = opts.variant ?? Variant.UNDIRECTED;
|
|
103
103
|
const maxIter = opts.maxIterations ?? 100;
|
|
104
104
|
const epsilon = opts.epsilon ?? 1e-6;
|
|
105
|
-
const precompute = (opts.precomputeIntercepts ??
|
|
105
|
+
const precompute = (opts.precomputeIntercepts ?? false) ? 1 : 0;
|
|
106
106
|
|
|
107
107
|
// Allocate C arrays (ownership transfers to dress.c — freed by free_dress_graph)
|
|
108
108
|
const uPtr = M._malloc(E * 4);
|
|
@@ -138,8 +138,8 @@ export async function dressFit(opts) {
|
|
|
138
138
|
const iterPtr = M._malloc(4); // int*
|
|
139
139
|
const deltaPtr = M._malloc(8); // double*
|
|
140
140
|
|
|
141
|
-
// Call
|
|
142
|
-
M.
|
|
141
|
+
// Call dress_fit
|
|
142
|
+
M._dress_fit(g, maxIter, epsilon, iterPtr, deltaPtr);
|
|
143
143
|
|
|
144
144
|
const iterations = M.getValue(iterPtr, 'i32');
|
|
145
145
|
const delta = M.getValue(deltaPtr, 'double');
|
|
@@ -201,6 +201,162 @@ export async function dressFit(opts) {
|
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
// ── Persistent DressGraph class ─────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* A persistent DRESS graph that supports repeated fit and get calls.
|
|
208
|
+
*
|
|
209
|
+
* Usage:
|
|
210
|
+
* const g = await DressGraph.create({
|
|
211
|
+
* numVertices: 4,
|
|
212
|
+
* sources: [0,1,2,0],
|
|
213
|
+
* targets: [1,2,3,3],
|
|
214
|
+
* });
|
|
215
|
+
* g.fit(); // fit with defaults
|
|
216
|
+
* const d = g.get(0, 2); // virtual edge query
|
|
217
|
+
* const res = g.result(); // snapshot of current results
|
|
218
|
+
* g.free(); // explicitly free C graph
|
|
219
|
+
*/
|
|
220
|
+
export class DressGraph {
|
|
221
|
+
/** @private */
|
|
222
|
+
constructor(module, gPtr, n, e, sources, targets) {
|
|
223
|
+
this._M = module;
|
|
224
|
+
this._g = gPtr;
|
|
225
|
+
this._n = n;
|
|
226
|
+
this._e = e;
|
|
227
|
+
this._sources = new Int32Array(sources);
|
|
228
|
+
this._targets = new Int32Array(targets);
|
|
229
|
+
this._iterPtr = module._malloc(4);
|
|
230
|
+
this._deltaPtr = module._malloc(8);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Create a persistent DressGraph.
|
|
235
|
+
*
|
|
236
|
+
* @param {Object} opts
|
|
237
|
+
* @param {number} opts.numVertices
|
|
238
|
+
* @param {Int32Array|number[]} opts.sources
|
|
239
|
+
* @param {Int32Array|number[]} opts.targets
|
|
240
|
+
* @param {Float64Array|number[]|null} [opts.weights]
|
|
241
|
+
* @param {number} [opts.variant=0]
|
|
242
|
+
* @param {boolean} [opts.precomputeIntercepts=false]
|
|
243
|
+
* @returns {Promise<DressGraph>}
|
|
244
|
+
*/
|
|
245
|
+
static async create(opts) {
|
|
246
|
+
const M = await getModule();
|
|
247
|
+
|
|
248
|
+
const N = opts.numVertices;
|
|
249
|
+
const E = opts.sources.length;
|
|
250
|
+
|
|
251
|
+
if (opts.targets.length !== E)
|
|
252
|
+
throw new Error('sources and targets must have equal length');
|
|
253
|
+
|
|
254
|
+
const variant = opts.variant ?? Variant.UNDIRECTED;
|
|
255
|
+
const precompute = (opts.precomputeIntercepts ?? false) ? 1 : 0;
|
|
256
|
+
|
|
257
|
+
const uPtr = M._malloc(E * 4);
|
|
258
|
+
const vPtr = M._malloc(E * 4);
|
|
259
|
+
for (let i = 0; i < E; i++) {
|
|
260
|
+
M.HEAP32[(uPtr >> 2) + i] = opts.sources[i];
|
|
261
|
+
M.HEAP32[(vPtr >> 2) + i] = opts.targets[i];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
let wPtr = 0;
|
|
265
|
+
if (opts.weights && opts.weights.length === E) {
|
|
266
|
+
wPtr = M._malloc(E * 8);
|
|
267
|
+
for (let i = 0; i < E; i++) {
|
|
268
|
+
M.HEAPF64[(wPtr >> 3) + i] = opts.weights[i];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const g = M._init_dress_graph(N, E, uPtr, vPtr, wPtr, variant, precompute);
|
|
273
|
+
if (g === 0) throw new Error('init_dress_graph returned NULL');
|
|
274
|
+
|
|
275
|
+
return new DressGraph(M, g, N, E, opts.sources, opts.targets);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Fit the DRESS model.
|
|
280
|
+
* @param {number} [maxIterations=100]
|
|
281
|
+
* @param {number} [epsilon=1e-6]
|
|
282
|
+
* @returns {{iterations: number, delta: number}}
|
|
283
|
+
*/
|
|
284
|
+
fit(maxIterations = 100, epsilon = 1e-6) {
|
|
285
|
+
if (!this._g) throw new Error('DressGraph already freed');
|
|
286
|
+
const M = this._M;
|
|
287
|
+
M._dress_fit(this._g, maxIterations, epsilon, this._iterPtr, this._deltaPtr);
|
|
288
|
+
return {
|
|
289
|
+
iterations: M.getValue(this._iterPtr, 'i32'),
|
|
290
|
+
delta: M.getValue(this._deltaPtr, 'double'),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Query the DRESS value for an edge (existing or virtual).
|
|
296
|
+
* @param {number} u - source vertex (0-based)
|
|
297
|
+
* @param {number} v - target vertex (0-based)
|
|
298
|
+
* @param {number} [maxIterations=100]
|
|
299
|
+
* @param {number} [epsilon=1e-6]
|
|
300
|
+
* @param {number} [edgeWeight=1.0]
|
|
301
|
+
* @returns {number}
|
|
302
|
+
*/
|
|
303
|
+
get(u, v, maxIterations = 100, epsilon = 1e-6, edgeWeight = 1.0) {
|
|
304
|
+
if (!this._g) throw new Error('DressGraph already freed');
|
|
305
|
+
return this._M._dress_get(this._g, u, v, maxIterations, epsilon, edgeWeight);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Extract a snapshot of the current results.
|
|
310
|
+
* @returns {DressResult}
|
|
311
|
+
*/
|
|
312
|
+
result() {
|
|
313
|
+
if (!this._g) throw new Error('DressGraph already freed');
|
|
314
|
+
const M = this._M;
|
|
315
|
+
const E = this._e;
|
|
316
|
+
const N = this._n;
|
|
317
|
+
|
|
318
|
+
// WASM32 offsets
|
|
319
|
+
const ewPtr = M.getValue(this._g + 36, 'i32');
|
|
320
|
+
const edPtr = M.getValue(this._g + 40, 'i32');
|
|
321
|
+
const ndPtr = M.getValue(this._g + 48, 'i32');
|
|
322
|
+
|
|
323
|
+
const edgeWeight = new Float64Array(E);
|
|
324
|
+
const edgeDress = new Float64Array(E);
|
|
325
|
+
const nodeDress = new Float64Array(N);
|
|
326
|
+
|
|
327
|
+
for (let i = 0; i < E; i++) {
|
|
328
|
+
edgeWeight[i] = M.HEAPF64[(ewPtr >> 3) + i];
|
|
329
|
+
edgeDress[i] = M.HEAPF64[(edPtr >> 3) + i];
|
|
330
|
+
}
|
|
331
|
+
for (let i = 0; i < N; i++) {
|
|
332
|
+
nodeDress[i] = M.HEAPF64[(ndPtr >> 3) + i];
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
sources: new Int32Array(this._sources),
|
|
337
|
+
targets: new Int32Array(this._targets),
|
|
338
|
+
edgeWeight,
|
|
339
|
+
edgeDress,
|
|
340
|
+
nodeDress,
|
|
341
|
+
iterations: 0,
|
|
342
|
+
delta: 0,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Free the underlying C graph.
|
|
348
|
+
*/
|
|
349
|
+
free() {
|
|
350
|
+
if (this._g) {
|
|
351
|
+
const M = this._M;
|
|
352
|
+
M._free_dress_graph(this._g);
|
|
353
|
+
M._free(this._iterPtr);
|
|
354
|
+
M._free(this._deltaPtr);
|
|
355
|
+
this._g = 0;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
204
360
|
// ── Delta-k-DRESS API ───────────────────────────────────────────────
|
|
205
361
|
|
|
206
362
|
/**
|
|
@@ -246,6 +402,8 @@ export async function deltaDressFit(opts) {
|
|
|
246
402
|
const epsilon = opts.epsilon ?? 1e-6;
|
|
247
403
|
const precompute = (opts.precompute ?? false) ? 1 : 0;
|
|
248
404
|
const keepMS = (opts.keepMultisets ?? false) ? 1 : 0;
|
|
405
|
+
const offset = opts.offset ?? 0;
|
|
406
|
+
const stride = opts.stride ?? 1;
|
|
249
407
|
|
|
250
408
|
// Allocate C arrays (ownership transfers to init_dress_graph)
|
|
251
409
|
const uPtr = M._malloc(E * 4);
|
|
@@ -288,9 +446,9 @@ export async function deltaDressFit(opts) {
|
|
|
288
446
|
M.setValue(numSubPtr + 4, 0, 'i32');
|
|
289
447
|
}
|
|
290
448
|
|
|
291
|
-
// Call
|
|
292
|
-
const histPtr = M.
|
|
293
|
-
keepMS, msPtrPtr, numSubPtr);
|
|
449
|
+
// Call delta_dress_fit_strided (returns int64_t* — pointer to histogram on heap)
|
|
450
|
+
const histPtr = M._delta_dress_fit_strided(g, k, maxIter, epsilon, histSizePtr,
|
|
451
|
+
keepMS, msPtrPtr, numSubPtr, offset, stride);
|
|
294
452
|
|
|
295
453
|
const histSize = M.getValue(histSizePtr, 'i32');
|
|
296
454
|
|
|
@@ -337,3 +495,4 @@ export async function deltaDressFit(opts) {
|
|
|
337
495
|
numSubgraphs: numSubgraphs,
|
|
338
496
|
};
|
|
339
497
|
}
|
|
498
|
+
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dress-graph",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "DRESS
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "DRESS is a deterministic, parameter-free framework that iteratively refines the structural similarity of edges in a graph to produce a canonical fingerprint: a real-valued edge vector, obtained by converging a non-linear dynamical system to its unique fixed point. The fingerprint is isomorphism-invariant by construction, numerically stable (no overflow, no error amplification, no undefined behavior), fast and embarrassingly parallel to compute: DRESS total runtime is O(I * m * d_max) for I iterations to convergence, and convergence is guaranteed by Birkhoff contraction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dress.js",
|
|
7
7
|
"types": "dress.d.ts",
|
package/dress_wasm.wasm
DELETED
|
Binary file
|