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 CHANGED
@@ -2,28 +2,19 @@
2
2
 
3
3
  **A Continuous Framework for Structural Graph Refinement**
4
4
 
5
- DRESS is a provably continuous relaxation of the Weisfeiler–Leman algorithm.
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
- const dress = require('./dress_wasm.cjs');
10
+ import { dressFit } from 'dress-graph';
22
11
 
23
- dress.onReady(() => {
24
- const result = dress.dressFit(4, [0, 1, 2, 0], [1, 2, 3, 3]);
25
- console.log(result.edgeDress);
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: true) */
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=true] - Pre-compute intercepts
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 ?? true) ? 1 : 0;
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 fit
142
- M._fit(g, maxIter, epsilon, iterPtr, deltaPtr);
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 delta_fit (returns int64_t* — pointer to histogram on heap)
292
- const histPtr = M._delta_fit(g, k, maxIter, epsilon, histSizePtr,
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.3.1",
4
- "description": "DRESS A Continuous Framework for Structural Graph Refinement (WebAssembly)",
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