graphwise 1.8.0 → 1.9.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/dist/adjacency-map-BtKzcuJq.js +229 -0
- package/dist/adjacency-map-BtKzcuJq.js.map +1 -0
- package/dist/adjacency-map-JqBnMNkF.cjs +234 -0
- package/dist/adjacency-map-JqBnMNkF.cjs.map +1 -0
- package/dist/async/index.cjs +15 -242
- package/dist/async/index.js +2 -229
- package/dist/expansion/index.cjs +43 -0
- package/dist/expansion/index.js +2 -0
- package/dist/expansion-ClDhlMK8.js +1704 -0
- package/dist/expansion-ClDhlMK8.js.map +1 -0
- package/dist/expansion-DaTroIyv.cjs +1949 -0
- package/dist/expansion-DaTroIyv.cjs.map +1 -0
- package/dist/extraction/index.cjs +630 -0
- package/dist/extraction/index.cjs.map +1 -0
- package/dist/extraction/index.js +621 -0
- package/dist/extraction/index.js.map +1 -0
- package/dist/gpu/csr.d.ts +29 -30
- package/dist/gpu/csr.d.ts.map +1 -1
- package/dist/gpu/dispatch.d.ts +31 -0
- package/dist/gpu/dispatch.d.ts.map +1 -0
- package/dist/gpu/dispatch.unit.test.d.ts +5 -0
- package/dist/gpu/dispatch.unit.test.d.ts.map +1 -0
- package/dist/gpu/index.cjs +15 -410
- package/dist/gpu/index.d.ts +3 -1
- package/dist/gpu/index.d.ts.map +1 -1
- package/dist/gpu/index.js +2 -400
- package/dist/gpu/kernels/bfs/kernel.d.ts +59 -0
- package/dist/gpu/kernels/bfs/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/bfs/logic.d.ts +47 -0
- package/dist/gpu/kernels/bfs/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/bfs/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/bfs/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/kernels/degree-histogram/kernel.d.ts +32 -0
- package/dist/gpu/kernels/degree-histogram/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/degree-histogram/logic.d.ts +45 -0
- package/dist/gpu/kernels/degree-histogram/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/degree-histogram/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/degree-histogram/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/kernels/jaccard/kernel.d.ts +40 -0
- package/dist/gpu/kernels/jaccard/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/jaccard/logic.d.ts +43 -0
- package/dist/gpu/kernels/jaccard/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/jaccard/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/jaccard/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/kernels/pagerank/kernel.d.ts +44 -0
- package/dist/gpu/kernels/pagerank/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/pagerank/logic.d.ts +50 -0
- package/dist/gpu/kernels/pagerank/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/pagerank/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/pagerank/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/kernels/spmv/kernel.d.ts +43 -0
- package/dist/gpu/kernels/spmv/kernel.d.ts.map +1 -0
- package/dist/gpu/kernels/spmv/logic.d.ts +31 -0
- package/dist/gpu/kernels/spmv/logic.d.ts.map +1 -0
- package/dist/gpu/kernels/spmv/logic.unit.test.d.ts +2 -0
- package/dist/gpu/kernels/spmv/logic.unit.test.d.ts.map +1 -0
- package/dist/gpu/operations.d.ts +76 -0
- package/dist/gpu/operations.d.ts.map +1 -0
- package/dist/gpu/operations.unit.test.d.ts +5 -0
- package/dist/gpu/operations.unit.test.d.ts.map +1 -0
- package/dist/gpu/root.d.ts +53 -0
- package/dist/gpu/root.d.ts.map +1 -0
- package/dist/gpu/root.unit.test.d.ts +2 -0
- package/dist/gpu/root.unit.test.d.ts.map +1 -0
- package/dist/gpu/types.d.ts +3 -8
- package/dist/gpu/types.d.ts.map +1 -1
- package/dist/gpu-CHiCN0wa.js +16945 -0
- package/dist/gpu-CHiCN0wa.js.map +1 -0
- package/dist/gpu-Y6owRVMi.cjs +17028 -0
- package/dist/gpu-Y6owRVMi.cjs.map +1 -0
- package/dist/graph/index.cjs +2 -229
- package/dist/graph/index.js +1 -228
- package/dist/index/index.cjs +141 -4040
- package/dist/index/index.js +15 -3917
- package/dist/jaccard-3rCdilwm.js +39 -0
- package/dist/jaccard-3rCdilwm.js.map +1 -0
- package/dist/jaccard-Bys9_dGW.cjs +50 -0
- package/dist/jaccard-Bys9_dGW.cjs.map +1 -0
- package/dist/{kmeans-BIgSyGKu.cjs → kmeans-B8x9D1kt.cjs} +1 -1
- package/dist/{kmeans-BIgSyGKu.cjs.map → kmeans-B8x9D1kt.cjs.map} +1 -1
- package/dist/{kmeans-87ExSUNZ.js → kmeans-DKkL9rAN.js} +1 -1
- package/dist/{kmeans-87ExSUNZ.js.map → kmeans-DKkL9rAN.js.map} +1 -1
- package/dist/ops-djAsQQSh.cjs +277 -0
- package/dist/ops-djAsQQSh.cjs.map +1 -0
- package/dist/ops-upIi6JIi.js +212 -0
- package/dist/ops-upIi6JIi.js.map +1 -0
- package/dist/priority-queue-BIiD1L0k.cjs +148 -0
- package/dist/priority-queue-BIiD1L0k.cjs.map +1 -0
- package/dist/priority-queue-CFDd5cBg.js +143 -0
- package/dist/priority-queue-CFDd5cBg.js.map +1 -0
- package/dist/ranking/index.cjs +43 -0
- package/dist/ranking/index.js +4 -0
- package/dist/ranking/mi/index.cjs +581 -0
- package/dist/ranking/mi/index.cjs.map +1 -0
- package/dist/ranking/mi/index.js +555 -0
- package/dist/ranking/mi/index.js.map +1 -0
- package/dist/ranking-3ez5m67U.js +1016 -0
- package/dist/ranking-3ez5m67U.js.map +1 -0
- package/dist/ranking-DVvajgUZ.cjs +1093 -0
- package/dist/ranking-DVvajgUZ.cjs.map +1 -0
- package/dist/seeds/index.cjs +1 -1
- package/dist/seeds/index.js +1 -1
- package/dist/structures/index.cjs +2 -143
- package/dist/structures/index.js +1 -142
- package/dist/utils/index.cjs +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils-BodeE2Mo.js +22 -0
- package/dist/utils-BodeE2Mo.js.map +1 -0
- package/dist/utils-CDtCcsyF.cjs +33 -0
- package/dist/utils-CDtCcsyF.cjs.map +1 -0
- package/package.json +3 -1
- package/dist/async/index.cjs.map +0 -1
- package/dist/async/index.js.map +0 -1
- package/dist/gpu/context.d.ts +0 -118
- package/dist/gpu/context.d.ts.map +0 -1
- package/dist/gpu/context.unit.test.d.ts +0 -2
- package/dist/gpu/context.unit.test.d.ts.map +0 -1
- package/dist/gpu/index.cjs.map +0 -1
- package/dist/gpu/index.js.map +0 -1
- package/dist/graph/index.cjs.map +0 -1
- package/dist/graph/index.js.map +0 -1
- package/dist/index/index.cjs.map +0 -1
- package/dist/index/index.js.map +0 -1
- package/dist/structures/index.cjs.map +0 -1
- package/dist/structures/index.js.map +0 -1
package/dist/async/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/async/utils.ts","../../src/async/runners.ts","../../src/async/ops.ts"],"sourcesContent":["/**\n * Async utility functions.\n *\n * @module async/utils\n */\n\n/** Collect an AsyncIterable into a readonly array. */\nexport async function collectAsyncIterable<T>(\n\titer: AsyncIterable<T>,\n): Promise<readonly T[]> {\n\tconst result: T[] = [];\n\tfor await (const item of iter) result.push(item);\n\treturn result;\n}\n\n/** Default yield strategy: setTimeout(0) to yield to the event loop. */\nexport function defaultYieldStrategy(): Promise<void> {\n\treturn new Promise((r) => {\n\t\tsetTimeout(r, 0);\n\t});\n}\n","/**\n * Sync and async runners for generator-based graph algorithms.\n *\n * The runner drives a generator that yields GraphOp objects, resolves each op\n * against the graph, and feeds the result back via gen.next(response). This\n * allows algorithm logic to be written once as a generator and executed\n * synchronously or asynchronously depending on the graph backing.\n *\n * @module async/runners\n */\n\nimport type { NodeData, EdgeData, ReadableGraph } from \"../graph\";\nimport type { AsyncReadableGraph } from \"../graph/async-interfaces\";\nimport type { GraphOp, GraphOpResponse } from \"./protocol\";\nimport type { AsyncRunnerOptions } from \"./types\";\nimport { collectAsyncIterable, defaultYieldStrategy } from \"./utils\";\n\n// ---------------------------------------------------------------------------\n// Sync runner\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a single GraphOp against a synchronous ReadableGraph.\n *\n * Returns a tagged GraphOpResponse so the receiving generator can narrow\n * the result type without type assertions.\n *\n * @param graph - The synchronous graph to query\n * @param op - The operation to resolve\n * @returns The tagged response\n */\nexport function resolveSyncOp<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\top: GraphOp,\n): GraphOpResponse<N, E> {\n\tswitch (op.tag) {\n\t\tcase \"neighbours\":\n\t\t\treturn {\n\t\t\t\ttag: \"neighbours\",\n\t\t\t\tvalue: Array.from(graph.neighbours(op.id, op.direction)),\n\t\t\t};\n\t\tcase \"degree\":\n\t\t\treturn { tag: \"degree\", value: graph.degree(op.id, op.direction) };\n\t\tcase \"getNode\":\n\t\t\treturn { tag: \"getNode\", value: graph.getNode(op.id) };\n\t\tcase \"getEdge\":\n\t\t\treturn { tag: \"getEdge\", value: graph.getEdge(op.source, op.target) };\n\t\tcase \"hasNode\":\n\t\t\treturn { tag: \"hasNode\", value: graph.hasNode(op.id) };\n\t\tcase \"yield\":\n\t\t\treturn { tag: \"yield\" };\n\t\tcase \"progress\":\n\t\t\treturn { tag: \"progress\" };\n\t}\n}\n\n/**\n * Drive a generator to completion using a synchronous graph.\n *\n * The generator yields GraphOp requests; each is resolved immediately\n * against the graph and the tagged response is fed back via gen.next().\n *\n * @param gen - The generator to drive\n * @param graph - The graph to resolve ops against\n * @returns The generator's return value\n */\nexport function runSync<N extends NodeData, E extends EdgeData, R>(\n\tgen: Generator<GraphOp, R, GraphOpResponse<N, E>>,\n\tgraph: ReadableGraph<N, E>,\n): R {\n\tlet step = gen.next();\n\twhile (step.done !== true) {\n\t\tconst response = resolveSyncOp(graph, step.value);\n\t\tstep = gen.next(response);\n\t}\n\treturn step.value;\n}\n\n// ---------------------------------------------------------------------------\n// Async runner\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a single GraphOp against an async ReadableGraph.\n *\n * AsyncIterables (neighbours) are collected into readonly arrays so the\n * generator receives the same value type as in sync mode. Returns a tagged\n * GraphOpResponse for type-safe narrowing without assertions.\n *\n * @param graph - The async graph to query\n * @param op - The operation to resolve\n * @returns A promise resolving to the tagged response\n */\nexport async function resolveAsyncOp<N extends NodeData, E extends EdgeData>(\n\tgraph: AsyncReadableGraph<N, E>,\n\top: GraphOp,\n): Promise<GraphOpResponse<N, E>> {\n\tswitch (op.tag) {\n\t\tcase \"neighbours\":\n\t\t\treturn {\n\t\t\t\ttag: \"neighbours\",\n\t\t\t\tvalue: await collectAsyncIterable(\n\t\t\t\t\tgraph.neighbours(op.id, op.direction),\n\t\t\t\t),\n\t\t\t};\n\t\tcase \"degree\":\n\t\t\treturn { tag: \"degree\", value: await graph.degree(op.id, op.direction) };\n\t\tcase \"getNode\":\n\t\t\treturn { tag: \"getNode\", value: await graph.getNode(op.id) };\n\t\tcase \"getEdge\":\n\t\t\treturn {\n\t\t\t\ttag: \"getEdge\",\n\t\t\t\tvalue: await graph.getEdge(op.source, op.target),\n\t\t\t};\n\t\tcase \"hasNode\":\n\t\t\treturn { tag: \"hasNode\", value: await graph.hasNode(op.id) };\n\t\tcase \"yield\":\n\t\t\treturn { tag: \"yield\" };\n\t\tcase \"progress\":\n\t\t\treturn { tag: \"progress\" };\n\t}\n}\n\n/**\n * Drive a generator to completion using an async graph.\n *\n * Extends sync semantics with:\n * - Cancellation via AbortSignal (throws DOMException \"AbortError\")\n * - Cooperative yielding at `yield` ops (calls yieldStrategy)\n * - Progress callbacks at `progress` ops (may be async for backpressure)\n * - Error propagation: graph errors are forwarded via gen.throw(); if the\n * generator does not handle them, they propagate to the caller\n *\n * @param gen - The generator to drive\n * @param graph - The async graph to resolve ops against\n * @param options - Runner configuration\n * @returns A promise resolving to the generator's return value\n */\nexport async function runAsync<N extends NodeData, E extends EdgeData, R>(\n\tgen: Generator<GraphOp, R, GraphOpResponse<N, E>>,\n\tgraph: AsyncReadableGraph<N, E>,\n\toptions?: AsyncRunnerOptions,\n): Promise<R> {\n\tconst signal = options?.signal;\n\tconst onProgress = options?.onProgress;\n\tconst yieldStrategy = options?.yieldStrategy ?? defaultYieldStrategy;\n\n\tlet step = gen.next();\n\n\twhile (step.done !== true) {\n\t\t// Check for cancellation before processing each op. Throw the error\n\t\t// into the generator so that any finally blocks in the algorithm run\n\t\t// before the error propagates to the caller.\n\t\tif (signal?.aborted === true) {\n\t\t\tconst abortError = new DOMException(\"Aborted\", \"AbortError\");\n\t\t\ttry {\n\t\t\t\tgen.throw(abortError);\n\t\t\t} catch {\n\t\t\t\t// Generator did not handle the error — propagate it\n\t\t\t\tthrow abortError;\n\t\t\t}\n\t\t\t// Generator handled the error but we still honour cancellation\n\t\t\tthrow abortError;\n\t\t}\n\n\t\tconst op = step.value;\n\n\t\t// Handle cooperative yield ops without hitting the graph\n\t\tif (op.tag === \"yield\") {\n\t\t\tawait yieldStrategy();\n\t\t\tstep = gen.next({ tag: \"yield\" });\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Handle progress ops: call the callback (awaiting if async)\n\t\tif (op.tag === \"progress\") {\n\t\t\tif (onProgress !== undefined) {\n\t\t\t\tconst maybePromise = onProgress(op.stats);\n\t\t\t\tif (maybePromise instanceof Promise) {\n\t\t\t\t\tawait maybePromise;\n\t\t\t\t}\n\t\t\t}\n\t\t\tstep = gen.next({ tag: \"progress\" });\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Resolve graph ops, forwarding any errors into the generator\n\t\tlet response: GraphOpResponse<N, E>;\n\t\ttry {\n\t\t\tresponse = await resolveAsyncOp(graph, op);\n\t\t} catch (error) {\n\t\t\t// Forward the error into the generator; if unhandled, it propagates\n\t\t\tstep = gen.throw(error);\n\t\t\tcontinue;\n\t\t}\n\n\t\tstep = gen.next(response);\n\t}\n\n\treturn step.value;\n}\n","/**\n * Type-safe yield helpers for graph operations.\n *\n * Each function is a sub-generator that yields one GraphOp and returns\n * the correctly-typed result. Narrowing is done via the tagged discriminated\n * union in GraphOpResponse — no type assertions needed.\n *\n * Use with `yield*` inside algorithm generators.\n *\n * @module async/ops\n */\n\nimport type { NodeId, NodeData, EdgeData, Direction } from \"../graph\";\nimport type { GraphOp, GraphOpResponse, ProgressStats } from \"./protocol\";\n\nexport function* opNeighbours<\n\tN extends NodeData = NodeData,\n\tE extends EdgeData = EdgeData,\n>(\n\tid: NodeId,\n\tdirection?: Direction,\n): Generator<GraphOp, readonly NodeId[], GraphOpResponse<N, E>> {\n\tconst op: GraphOp =\n\t\tdirection !== undefined\n\t\t\t? { tag: \"neighbours\", id, direction }\n\t\t\t: { tag: \"neighbours\", id };\n\tconst response: GraphOpResponse<N, E> = yield op;\n\tif (response.tag !== \"neighbours\") {\n\t\tthrow new TypeError(`Expected neighbours response, got ${response.tag}`);\n\t}\n\treturn response.value;\n}\n\nexport function* opDegree<\n\tN extends NodeData = NodeData,\n\tE extends EdgeData = EdgeData,\n>(\n\tid: NodeId,\n\tdirection?: Direction,\n): Generator<GraphOp, number, GraphOpResponse<N, E>> {\n\tconst op: GraphOp =\n\t\tdirection !== undefined\n\t\t\t? { tag: \"degree\", id, direction }\n\t\t\t: { tag: \"degree\", id };\n\tconst response: GraphOpResponse<N, E> = yield op;\n\tif (response.tag !== \"degree\") {\n\t\tthrow new TypeError(`Expected degree response, got ${response.tag}`);\n\t}\n\treturn response.value;\n}\n\nexport function* opGetNode<\n\tN extends NodeData = NodeData,\n\tE extends EdgeData = EdgeData,\n>(id: NodeId): Generator<GraphOp, N | undefined, GraphOpResponse<N, E>> {\n\tconst response: GraphOpResponse<N, E> = yield { tag: \"getNode\", id };\n\tif (response.tag !== \"getNode\") {\n\t\tthrow new TypeError(`Expected getNode response, got ${response.tag}`);\n\t}\n\treturn response.value;\n}\n\nexport function* opGetEdge<\n\tN extends NodeData = NodeData,\n\tE extends EdgeData = EdgeData,\n>(\n\tsource: NodeId,\n\ttarget: NodeId,\n): Generator<GraphOp, E | undefined, GraphOpResponse<N, E>> {\n\tconst response: GraphOpResponse<N, E> = yield {\n\t\ttag: \"getEdge\",\n\t\tsource,\n\t\ttarget,\n\t};\n\tif (response.tag !== \"getEdge\") {\n\t\tthrow new TypeError(`Expected getEdge response, got ${response.tag}`);\n\t}\n\treturn response.value;\n}\n\nexport function* opHasNode<\n\tN extends NodeData = NodeData,\n\tE extends EdgeData = EdgeData,\n>(id: NodeId): Generator<GraphOp, boolean, GraphOpResponse<N, E>> {\n\tconst response: GraphOpResponse<N, E> = yield { tag: \"hasNode\", id };\n\tif (response.tag !== \"hasNode\") {\n\t\tthrow new TypeError(`Expected hasNode response, got ${response.tag}`);\n\t}\n\treturn response.value;\n}\n\nexport function* opYield<\n\tN extends NodeData = NodeData,\n\tE extends EdgeData = EdgeData,\n>(): Generator<GraphOp, void, GraphOpResponse<N, E>> {\n\tyield { tag: \"yield\" };\n}\n\nexport function* opProgress<\n\tN extends NodeData = NodeData,\n\tE extends EdgeData = EdgeData,\n>(stats: ProgressStats): Generator<GraphOp, void, GraphOpResponse<N, E>> {\n\tyield { tag: \"progress\", stats };\n}\n"],"mappings":"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"}
|
package/dist/gpu/context.d.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GPU context management for WebGPU compute operations.
|
|
3
|
-
*
|
|
4
|
-
* Provides device acquisition, buffer pooling, and shader compilation
|
|
5
|
-
* with caching for efficient GPU resource management.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Options for creating a GPUContext.
|
|
9
|
-
*/
|
|
10
|
-
export interface GPUContextOptions {
|
|
11
|
-
/** Power preference for GPU adapter */
|
|
12
|
-
readonly powerPreference?: "low-power" | "high-performance";
|
|
13
|
-
/** Force create new context even if one exists */
|
|
14
|
-
readonly forceNew?: boolean;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Manages GPU device, buffers, and compiled shaders.
|
|
18
|
-
*
|
|
19
|
-
* Use the singleton pattern via getGPUContext() for most use cases,
|
|
20
|
-
* or create separate contexts for isolated GPU resource pools.
|
|
21
|
-
*/
|
|
22
|
-
export declare class GPUContext {
|
|
23
|
-
private device;
|
|
24
|
-
private readonly shaderCache;
|
|
25
|
-
private readonly bufferPool;
|
|
26
|
-
private readonly destroyedBuffers;
|
|
27
|
-
/**
|
|
28
|
-
* Check if this context has an acquired GPU device.
|
|
29
|
-
*/
|
|
30
|
-
get isReady(): boolean;
|
|
31
|
-
/**
|
|
32
|
-
* Get the GPU device, throwing if not acquired.
|
|
33
|
-
*/
|
|
34
|
-
getDevice(): GPUDevice;
|
|
35
|
-
/**
|
|
36
|
-
* Acquire a GPU device from the adapter.
|
|
37
|
-
*
|
|
38
|
-
* @param options - Context creation options
|
|
39
|
-
* @returns true if device was acquired successfully
|
|
40
|
-
* @throws Error if WebGPU is unavailable or device request fails
|
|
41
|
-
*/
|
|
42
|
-
acquireDevice(options?: GPUContextOptions): Promise<boolean>;
|
|
43
|
-
/**
|
|
44
|
-
* Compile a WGSL shader, using cache if available.
|
|
45
|
-
*
|
|
46
|
-
* @param code - WGSL shader code
|
|
47
|
-
* @param key - Optional cache key (defaults to code hash)
|
|
48
|
-
* @returns Compiled shader module
|
|
49
|
-
*/
|
|
50
|
-
compileShader(code: string, key?: string): GPUShaderModule;
|
|
51
|
-
/**
|
|
52
|
-
* Create a GPU buffer, optionally reusing from pool.
|
|
53
|
-
*
|
|
54
|
-
* @param size - Buffer size in bytes
|
|
55
|
-
* @param usage - Buffer usage flags
|
|
56
|
-
* @returns GPU buffer
|
|
57
|
-
*/
|
|
58
|
-
createBuffer(size: number, usage: GPUBufferUsageFlags): GPUBuffer;
|
|
59
|
-
/**
|
|
60
|
-
* Return a buffer to the pool for reuse.
|
|
61
|
-
*
|
|
62
|
-
* @param buffer - Buffer to recycle
|
|
63
|
-
*/
|
|
64
|
-
recycleBuffer(buffer: GPUBuffer): void;
|
|
65
|
-
/**
|
|
66
|
-
* Mark a buffer as destroyed (call before GPUBuffer.destroy()).
|
|
67
|
-
*
|
|
68
|
-
* @param buffer - Buffer to mark as destroyed
|
|
69
|
-
*/
|
|
70
|
-
markDestroyed(buffer: GPUBuffer): void;
|
|
71
|
-
/**
|
|
72
|
-
* Create a compute pipeline from shader code.
|
|
73
|
-
*
|
|
74
|
-
* @param code - WGSL compute shader code
|
|
75
|
-
* @param entryPoint - Entry point function name
|
|
76
|
-
* @returns Compute pipeline
|
|
77
|
-
*/
|
|
78
|
-
createComputePipeline(code: string, entryPoint?: string): GPUComputePipeline;
|
|
79
|
-
/**
|
|
80
|
-
* Create a compute pipeline asynchronously (preferred for performance).
|
|
81
|
-
*
|
|
82
|
-
* @param code - WGSL compute shader code
|
|
83
|
-
* @param entryPoint - Entry point function name
|
|
84
|
-
* @returns Promise resolving to compute pipeline
|
|
85
|
-
*/
|
|
86
|
-
createComputePipelineAsync(code: string, entryPoint?: string): Promise<GPUComputePipeline>;
|
|
87
|
-
/**
|
|
88
|
-
* Clear all cached resources.
|
|
89
|
-
*/
|
|
90
|
-
clearCache(): void;
|
|
91
|
-
/**
|
|
92
|
-
* Destroy the context and release all resources.
|
|
93
|
-
*/
|
|
94
|
-
destroy(): void;
|
|
95
|
-
/**
|
|
96
|
-
* Simple string hash for cache keys.
|
|
97
|
-
*/
|
|
98
|
-
private hashCode;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Get or create the default GPU context.
|
|
102
|
-
*
|
|
103
|
-
* This is a lazy singleton - the device is not acquired until
|
|
104
|
-
* acquireDevice() is called.
|
|
105
|
-
*
|
|
106
|
-
* @param options - Context creation options
|
|
107
|
-
* @returns GPU context instance
|
|
108
|
-
*/
|
|
109
|
-
export declare function getGPUContext(options?: GPUContextOptions): GPUContext;
|
|
110
|
-
/**
|
|
111
|
-
* Create a new isolated GPU context.
|
|
112
|
-
*
|
|
113
|
-
* Use this when you need separate resource pools or device management.
|
|
114
|
-
*
|
|
115
|
-
* @returns New GPU context instance
|
|
116
|
-
*/
|
|
117
|
-
export declare function createGPUContext(): GPUContext;
|
|
118
|
-
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/gpu/context.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuBH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IACjC,uCAAuC;IACvC,QAAQ,CAAC,eAAe,CAAC,EAAE,WAAW,GAAG,kBAAkB,CAAC;IAC5D,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC5B;AAUD;;;;;GAKG;AACH,qBAAa,UAAU;IACtB,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAmB;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA4B;IAE7D;;OAEG;IACH,IAAW,OAAO,IAAI,OAAO,CAE5B;IAED;;OAEG;IACI,SAAS,IAAI,SAAS;IAS7B;;;;;;OAMG;IACU,aAAa,CACzB,OAAO,GAAE,iBAAsB,GAC7B,OAAO,CAAC,OAAO,CAAC;IAuDnB;;;;;;OAMG;IACI,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,eAAe;IAejE;;;;;;OAMG;IACI,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,SAAS;IAoBxE;;;;OAIG;IACI,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAO7C;;;;OAIG;IACI,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAI7C;;;;;;OAMG;IACI,qBAAqB,CAC3B,IAAI,EAAE,MAAM,EACZ,UAAU,SAAS,GACjB,kBAAkB;IAarB;;;;;;OAMG;IACU,0BAA0B,CACtC,IAAI,EAAE,MAAM,EACZ,UAAU,SAAS,GACjB,OAAO,CAAC,kBAAkB,CAAC;IAa9B;;OAEG;IACI,UAAU,IAAI,IAAI;IAQzB;;OAEG;IACI,OAAO,IAAI,IAAI;IAQtB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAQhB;AAKD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,UAAU,CAKrE;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context.unit.test.d.ts","sourceRoot":"","sources":["../../src/gpu/context.unit.test.ts"],"names":[],"mappings":""}
|
package/dist/gpu/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/gpu/types.ts","../../src/gpu/csr.ts","../../src/gpu/detect.ts","../../src/gpu/context.ts"],"sourcesContent":["/**\n * Type definitions for the WebGPU compute backend.\n *\n * These types define the backend selection options and result types\n * for GPU-accelerated graph operations.\n */\n\n/**\n * Backend selection for compute operations.\n *\n * - 'cpu': Synchronous CPU implementation (default, always available)\n * - 'gpu': Async GPU implementation (throws if WebGPU unavailable)\n * - 'auto': Async, detect WebGPU at runtime, fall back to CPU silently\n */\nexport type ComputeBackend = \"auto\" | \"gpu\" | \"cpu\";\n\n/**\n * Result of WebGPU availability detection.\n */\nexport interface GPUDetectionResult {\n\t/** Whether WebGPU is available in the current environment */\n\treadonly available: boolean;\n\t/** Human-readable reason if unavailable */\n\treadonly reason?: string;\n}\n\n/**\n * Error thrown when GPU backend is requested but unavailable.\n */\nexport class GPUNotAvailableError extends Error {\n\tpublic constructor(reason: string) {\n\t\tsuper(`WebGPU not available: ${reason}`);\n\t\tthis.name = \"GPUNotAvailableError\";\n\t}\n}\n\n/**\n * Result wrapper for GPU operations that may fall back to CPU.\n */\nexport interface ComputeResult<T> {\n\t/** The computed result value */\n\treadonly value: T;\n\t/** Which backend was actually used */\n\treadonly backend: \"gpu\" | \"cpu\";\n\t/** Time taken in milliseconds (optional, for profiling) */\n\treadonly elapsedMs?: number;\n}\n\n/**\n * Configuration options for GPU compute operations.\n */\nexport interface GPUComputeOptions {\n\t/** Backend selection: 'auto', 'gpu', or 'cpu' */\n\treadonly backend?: ComputeBackend;\n\t/** Optional GPU device to reuse (avoids device acquisition overhead) */\n\treadonly device?: GPUDevice;\n}\n","/**\n * Compressed Sparse Row (CSR) matrix representation for GPU computation.\n *\n * CSR format is memory-efficient for sparse graphs and maps well to\n * GPU parallel operations. The format stores adjacency information\n * in three arrays: row offsets, column indices, and optional values.\n */\n\nimport type { NodeId, Direction, NodeData, EdgeData } from \"../graph/types\";\nimport type { ReadableGraph } from \"../graph\";\n\n/**\n * CSR matrix representation of a graph adjacency structure.\n *\n * The rowOffsets array has length nodeCount + 1, where rowOffsets[i]\n * gives the start index in colIndices for node i's neighbours.\n * The neighbours of node i are colIndices[rowOffsets[i] : rowOffsets[i+1]].\n */\nexport interface CSRMatrix {\n\t/** Row offsets array (length: nodeCount + 1) */\n\treadonly rowOffsets: Uint32Array;\n\t/** Column indices array (length: edgeCount for directed, 2*edgeCount for undirected) */\n\treadonly colIndices: Uint32Array;\n\t/** Optional edge weights aligned with colIndices */\n\treadonly values?: Float32Array;\n\t/** Number of nodes in the graph */\n\treadonly nodeCount: number;\n\t/** Number of directed edges (undirected edges counted once) */\n\treadonly edgeCount: number;\n}\n\n/**\n * Mapping from node IDs to CSR indices.\n *\n * Required because CSR uses dense integer indices while graphs\n * may use arbitrary string identifiers.\n */\nexport interface CSRIndexMap {\n\t/** Map from NodeId to CSR row index */\n\treadonly nodeToIndex: ReadonlyMap<NodeId, number>;\n\t/** Map from CSR row index to NodeId */\n\treadonly indexToNode: readonly NodeId[];\n}\n\n/**\n * Combined CSR matrix with index mapping.\n */\nexport interface CSRGraph {\n\treadonly csr: CSRMatrix;\n\treadonly indexMap: CSRIndexMap;\n}\n\n/**\n * Group of GPU buffers holding a CSR matrix.\n *\n * Each buffer is created with appropriate usage flags for compute operations.\n */\nexport interface GPUBufferGroup {\n\t/** Buffer containing rowOffsets data */\n\treadonly rowOffsets: GPUBuffer;\n\t/** Buffer containing colIndices data */\n\treadonly colIndices: GPUBuffer;\n\t/** Buffer containing values data (optional) */\n\treadonly values?: GPUBuffer;\n\t/** Number of nodes */\n\treadonly nodeCount: number;\n\t/** Number of edges */\n\treadonly edgeCount: number;\n}\n\n/**\n * Convert a ReadableGraph to CSR format.\n *\n * For undirected graphs, each edge is stored twice (once in each direction).\n * For directed graphs, edges are stored in the out-direction by default.\n *\n * @param graph - The graph to convert\n * @param direction - Edge direction to include (default: 'out' for directed, 'both' for undirected)\n * @returns CSR representation with index mapping\n */\nexport function graphToCSR<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tdirection: Direction = graph.directed ? \"out\" : \"both\",\n): CSRGraph {\n\t// Build node index mapping\n\tconst nodeToIndex = new Map<NodeId, number>();\n\tconst indexToNode: NodeId[] = [];\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst index = indexToNode.length;\n\t\tnodeToIndex.set(nodeId, index);\n\t\tindexToNode.push(nodeId);\n\t}\n\n\tconst nodeCount = indexToNode.length;\n\n\t// Count edges per node to build row offsets\n\tconst degrees = new Uint32Array(nodeCount);\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst srcIndex = nodeToIndex.get(nodeId);\n\t\tif (srcIndex === undefined) continue;\n\t\tdegrees[srcIndex] = graph.degree(nodeId, direction);\n\t}\n\n\t// Calculate total edge count\n\tlet totalEdges = 0;\n\tfor (let i = 0; i < nodeCount; i++) {\n\t\ttotalEdges += degrees[i] ?? 0;\n\t}\n\n\t// Build rowOffsets array\n\tconst rowOffsets = new Uint32Array(nodeCount + 1);\n\tfor (let i = 0; i < nodeCount; i++) {\n\t\trowOffsets[i + 1] = (rowOffsets[i] ?? 0) + (degrees[i] ?? 0);\n\t}\n\n\t// Build colIndices and values arrays\n\tconst colIndices = new Uint32Array(totalEdges);\n\tconst values = new Float32Array(totalEdges);\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst srcIndex = nodeToIndex.get(nodeId);\n\t\tif (srcIndex === undefined) continue;\n\n\t\tconst baseOffset = rowOffsets[srcIndex] ?? 0;\n\t\tlet localOffset = 0;\n\n\t\tfor (const neighbourId of graph.neighbours(nodeId, direction)) {\n\t\t\tconst dstIndex = nodeToIndex.get(neighbourId);\n\t\t\tif (dstIndex === undefined) continue;\n\n\t\t\tconst edgeIdx = baseOffset + localOffset;\n\t\t\tcolIndices[edgeIdx] = dstIndex;\n\n\t\t\t// Get edge weight if available\n\t\t\tconst edge = graph.getEdge(nodeId, neighbourId);\n\t\t\tvalues[edgeIdx] = edge?.weight ?? 1.0;\n\n\t\t\tlocalOffset++;\n\t\t}\n\t}\n\n\tconst csr: CSRMatrix = {\n\t\trowOffsets,\n\t\tcolIndices,\n\t\tvalues,\n\t\tnodeCount,\n\t\tedgeCount: graph.directed ? graph.edgeCount : graph.edgeCount * 2,\n\t};\n\n\tconst indexMap: CSRIndexMap = {\n\t\tnodeToIndex,\n\t\tindexToNode,\n\t};\n\n\treturn { csr, indexMap };\n}\n\n/**\n * Create GPU buffers from a CSR matrix.\n *\n * Buffers are created with:\n * - rowOffsets/colIndices: STORAGE | COPY_DST\n * - values: STORAGE | COPY_DST (if present)\n *\n * @param device - GPU device to create buffers on\n * @param csr - CSR matrix to upload\n * @returns GPU buffer group\n */\nexport function csrToGPUBuffers(\n\tdevice: GPUDevice,\n\tcsr: CSRMatrix,\n): GPUBufferGroup {\n\t// Row offsets buffer\n\tconst rowOffsetsBuffer = device.createBuffer({\n\t\tsize: csr.rowOffsets.byteLength,\n\t\tusage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\n\t\tmappedAtCreation: false,\n\t});\n\tdevice.queue.writeBuffer(rowOffsetsBuffer, 0, csr.rowOffsets);\n\n\t// Column indices buffer\n\tconst colIndicesBuffer = device.createBuffer({\n\t\tsize: csr.colIndices.byteLength,\n\t\tusage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\n\t\tmappedAtCreation: false,\n\t});\n\tdevice.queue.writeBuffer(colIndicesBuffer, 0, csr.colIndices);\n\n\t// Values buffer (optional)\n\tlet valuesBuffer: GPUBuffer | undefined;\n\tif (csr.values !== undefined) {\n\t\tvaluesBuffer = device.createBuffer({\n\t\t\tsize: csr.values.byteLength,\n\t\t\tusage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\n\t\t\tmappedAtCreation: false,\n\t\t});\n\t\tdevice.queue.writeBuffer(valuesBuffer, 0, csr.values);\n\t}\n\n\tif (valuesBuffer !== undefined) {\n\t\treturn {\n\t\t\trowOffsets: rowOffsetsBuffer,\n\t\t\tcolIndices: colIndicesBuffer,\n\t\t\tvalues: valuesBuffer,\n\t\t\tnodeCount: csr.nodeCount,\n\t\t\tedgeCount: csr.edgeCount,\n\t\t};\n\t}\n\treturn {\n\t\trowOffsets: rowOffsetsBuffer,\n\t\tcolIndices: colIndicesBuffer,\n\t\tnodeCount: csr.nodeCount,\n\t\tedgeCount: csr.edgeCount,\n\t};\n}\n\n/**\n * Create a result buffer for reading compute output.\n *\n * @param device - GPU device\n * @param byteLength - Size of the buffer in bytes\n * @returns GPU buffer configured for map reading\n */\nexport function createResultBuffer(\n\tdevice: GPUDevice,\n\tbyteLength: number,\n): GPUBuffer {\n\treturn device.createBuffer({\n\t\tsize: byteLength,\n\t\tusage:\n\t\t\tGPUBufferUsage.STORAGE |\n\t\t\tGPUBufferUsage.COPY_SRC |\n\t\t\tGPUBufferUsage.MAP_READ,\n\t});\n}\n\n/**\n * Read data from a GPU buffer to CPU.\n *\n * @param device - GPU device\n * @param buffer - Buffer to read from\n * @returns ArrayBuffer containing the buffer data\n */\nexport async function readBufferToCPU(\n\tdevice: GPUDevice,\n\tbuffer: GPUBuffer,\n): Promise<ArrayBuffer> {\n\tawait buffer.mapAsync(GPUMapMode.READ);\n\tconst data = buffer.getMappedRange().slice(0);\n\tbuffer.unmap();\n\treturn data;\n}\n","/**\n * WebGPU availability detection for browser and Node.js environments.\n *\n * This module provides runtime detection of WebGPU support without\n * requiring any polyfills or runtime dependencies.\n */\n\nimport type { GPUDetectionResult } from \"./types\";\n\n/**\n * Detect WebGPU availability in the current environment.\n *\n * Checks for:\n * - Browser: navigator.gpu\n * - Node.js: global GPU constructor (Node.js 21+ with --experimental-webgpu)\n * - Deno: global GPU constructor\n *\n * @returns Detection result with availability status and reason\n */\nexport function detectWebGPU(): GPUDetectionResult {\n\t// Check for browser WebGPU API\n\tif (typeof navigator !== \"undefined\" && \"gpu\" in navigator) {\n\t\treturn { available: true };\n\t}\n\n\t// Check for Node.js / Deno global GPU\n\tif (typeof globalThis !== \"undefined\" && \"GPU\" in globalThis) {\n\t\treturn { available: true };\n\t}\n\n\t// WebGPU not available\n\tconst reasons: string[] = [];\n\n\tif (typeof navigator === \"undefined\" && typeof globalThis === \"undefined\") {\n\t\treasons.push(\"no global scope detected\");\n\t} else if (typeof navigator !== \"undefined\" && !(\"gpu\" in navigator)) {\n\t\treasons.push(\"navigator.gpu not present (browser may not support WebGPU)\");\n\t} else if (typeof globalThis !== \"undefined\" && !(\"GPU\" in globalThis)) {\n\t\treasons.push(\n\t\t\t\"global GPU not present (Node.js requires v21+ with --experimental-webgpu flag)\",\n\t\t);\n\t}\n\n\treturn {\n\t\tavailable: false,\n\t\treason: reasons.length > 0 ? reasons.join(\"; \") : \"unknown environment\",\n\t};\n}\n\n/**\n * Check if WebGPU is available (convenience function).\n *\n * @returns true if WebGPU is available, false otherwise\n */\nexport function isWebGPUAvailable(): boolean {\n\treturn detectWebGPU().available;\n}\n\n/**\n * Assert that WebGPU is available, throwing an error if not.\n *\n * @throws Error if WebGPU is not available\n */\nexport function assertWebGPUAvailable(): void {\n\tconst result = detectWebGPU();\n\tif (!result.available) {\n\t\tthrow new Error(\n\t\t\t`WebGPU required but not available: ${result.reason ?? \"unknown reason\"}`,\n\t\t);\n\t}\n}\n","/**\n * GPU context management for WebGPU compute operations.\n *\n * Provides device acquisition, buffer pooling, and shader compilation\n * with caching for efficient GPU resource management.\n */\n\nimport { detectWebGPU } from \"./detect\";\n\n/**\n * Type guard to check if an object has a WebGPU-compatible requestAdapter method.\n */\nfunction hasRequestAdapter(obj: unknown): obj is {\n\trequestAdapter: (\n\t\topts?: GPURequestAdapterOptions,\n\t) => Promise<GPUAdapter | null>;\n} {\n\tif (typeof obj !== \"object\" || obj === null) {\n\t\treturn false;\n\t}\n\tif (!(\"requestAdapter\" in obj)) {\n\t\treturn false;\n\t}\n\t// Use Object.getOwnPropertyDescriptor to safely check property type\n\tconst descriptor = Object.getOwnPropertyDescriptor(obj, \"requestAdapter\");\n\treturn descriptor !== undefined && typeof descriptor.value === \"function\";\n}\n\n/**\n * Options for creating a GPUContext.\n */\nexport interface GPUContextOptions {\n\t/** Power preference for GPU adapter */\n\treadonly powerPreference?: \"low-power\" | \"high-performance\";\n\t/** Force create new context even if one exists */\n\treadonly forceNew?: boolean;\n}\n\n/**\n * Shader module cache entry.\n */\ninterface CachedShader {\n\treadonly module: GPUShaderModule;\n\treadonly code: string;\n}\n\n/**\n * Manages GPU device, buffers, and compiled shaders.\n *\n * Use the singleton pattern via getGPUContext() for most use cases,\n * or create separate contexts for isolated GPU resource pools.\n */\nexport class GPUContext {\n\tprivate device: GPUDevice | null = null;\n\tprivate readonly shaderCache = new Map<string, CachedShader>();\n\tprivate readonly bufferPool: GPUBuffer[] = [];\n\tprivate readonly destroyedBuffers = new WeakSet<GPUBuffer>();\n\n\t/**\n\t * Check if this context has an acquired GPU device.\n\t */\n\tpublic get isReady(): boolean {\n\t\treturn this.device !== null;\n\t}\n\n\t/**\n\t * Get the GPU device, throwing if not acquired.\n\t */\n\tpublic getDevice(): GPUDevice {\n\t\tif (this.device === null) {\n\t\t\tthrow new Error(\n\t\t\t\t\"GPUContext not initialised. Call acquireDevice() first.\",\n\t\t\t);\n\t\t}\n\t\treturn this.device;\n\t}\n\n\t/**\n\t * Acquire a GPU device from the adapter.\n\t *\n\t * @param options - Context creation options\n\t * @returns true if device was acquired successfully\n\t * @throws Error if WebGPU is unavailable or device request fails\n\t */\n\tpublic async acquireDevice(\n\t\toptions: GPUContextOptions = {},\n\t): Promise<boolean> {\n\t\tconst detection = detectWebGPU();\n\t\tif (!detection.available) {\n\t\t\tthrow new Error(\n\t\t\t\t`WebGPU unavailable: ${detection.reason ?? \"unknown reason\"}`,\n\t\t\t);\n\t\t}\n\n\t\t// Get adapter\n\t\tlet adapter: GPUAdapter | null = null;\n\n\t\t// Build adapter options, only including powerPreference if defined\n\t\tconst adapterOpts: GPURequestAdapterOptions =\n\t\t\toptions.powerPreference !== undefined\n\t\t\t\t? { powerPreference: options.powerPreference }\n\t\t\t\t: {};\n\n\t\tif (typeof navigator !== \"undefined\" && \"gpu\" in navigator) {\n\t\t\tadapter = await navigator.gpu.requestAdapter(adapterOpts);\n\t\t}\n\n\t\t// Node.js / Deno fallback via global GPU\n\t\tif (\n\t\t\tadapter === null &&\n\t\t\ttypeof globalThis !== \"undefined\" &&\n\t\t\t\"GPU\" in globalThis\n\t\t) {\n\t\t\t// Access gpu property via Object.getOwnPropertyDescriptor to avoid type assertion\n\t\t\tconst gpuDescriptor = Object.getOwnPropertyDescriptor(globalThis, \"gpu\");\n\t\t\t// Pass descriptor value directly to type guard to avoid unsafe assignment\n\t\t\tif (\n\t\t\t\tgpuDescriptor !== undefined &&\n\t\t\t\thasRequestAdapter(gpuDescriptor.value)\n\t\t\t) {\n\t\t\t\tadapter = await gpuDescriptor.value.requestAdapter(adapterOpts);\n\t\t\t}\n\t\t}\n\n\t\tif (adapter === null) {\n\t\t\tthrow new Error(\"No GPU adapter found\");\n\t\t}\n\n\t\t// Request device\n\t\tthis.device = await adapter.requestDevice();\n\n\t\t// Handle device loss\n\t\tvoid this.device.lost.then((info: GPUDeviceLostInfo): void => {\n\t\t\tconsole.error(`GPU device lost: ${info.message}`);\n\t\t\tthis.device = null;\n\t\t\tthis.clearCache();\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Compile a WGSL shader, using cache if available.\n\t *\n\t * @param code - WGSL shader code\n\t * @param key - Optional cache key (defaults to code hash)\n\t * @returns Compiled shader module\n\t */\n\tpublic compileShader(code: string, key?: string): GPUShaderModule {\n\t\tconst cacheKey = key ?? this.hashCode(code);\n\t\tconst cached = this.shaderCache.get(cacheKey);\n\n\t\tif (cached?.code === code) {\n\t\t\treturn cached.module;\n\t\t}\n\n\t\tconst device = this.getDevice();\n\t\tconst module = device.createShaderModule({ code });\n\n\t\tthis.shaderCache.set(cacheKey, { module, code });\n\t\treturn module;\n\t}\n\n\t/**\n\t * Create a GPU buffer, optionally reusing from pool.\n\t *\n\t * @param size - Buffer size in bytes\n\t * @param usage - Buffer usage flags\n\t * @returns GPU buffer\n\t */\n\tpublic createBuffer(size: number, usage: GPUBufferUsageFlags): GPUBuffer {\n\t\t// Try to find a suitable buffer in the pool\n\t\tfor (let i = 0; i < this.bufferPool.length; i++) {\n\t\t\tconst buffer = this.bufferPool[i];\n\t\t\tif (\n\t\t\t\tbuffer !== undefined &&\n\t\t\t\t!this.destroyedBuffers.has(buffer) &&\n\t\t\t\tbuffer.size >= size &&\n\t\t\t\t(buffer.usage & usage) === usage\n\t\t\t) {\n\t\t\t\tthis.bufferPool.splice(i, 1);\n\t\t\t\treturn buffer;\n\t\t\t}\n\t\t}\n\n\t\t// Create new buffer\n\t\tconst device = this.getDevice();\n\t\treturn device.createBuffer({ size, usage });\n\t}\n\n\t/**\n\t * Return a buffer to the pool for reuse.\n\t *\n\t * @param buffer - Buffer to recycle\n\t */\n\tpublic recycleBuffer(buffer: GPUBuffer): void {\n\t\t// Only pool if buffer is not destroyed\n\t\tif (!this.destroyedBuffers.has(buffer)) {\n\t\t\tthis.bufferPool.push(buffer);\n\t\t}\n\t}\n\n\t/**\n\t * Mark a buffer as destroyed (call before GPUBuffer.destroy()).\n\t *\n\t * @param buffer - Buffer to mark as destroyed\n\t */\n\tpublic markDestroyed(buffer: GPUBuffer): void {\n\t\tthis.destroyedBuffers.add(buffer);\n\t}\n\n\t/**\n\t * Create a compute pipeline from shader code.\n\t *\n\t * @param code - WGSL compute shader code\n\t * @param entryPoint - Entry point function name\n\t * @returns Compute pipeline\n\t */\n\tpublic createComputePipeline(\n\t\tcode: string,\n\t\tentryPoint = \"main\",\n\t): GPUComputePipeline {\n\t\tconst device = this.getDevice();\n\t\tconst module = this.compileShader(code);\n\n\t\treturn device.createComputePipeline({\n\t\t\tlayout: \"auto\",\n\t\t\tcompute: {\n\t\t\t\tmodule,\n\t\t\t\tentryPoint,\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Create a compute pipeline asynchronously (preferred for performance).\n\t *\n\t * @param code - WGSL compute shader code\n\t * @param entryPoint - Entry point function name\n\t * @returns Promise resolving to compute pipeline\n\t */\n\tpublic async createComputePipelineAsync(\n\t\tcode: string,\n\t\tentryPoint = \"main\",\n\t): Promise<GPUComputePipeline> {\n\t\tconst device = this.getDevice();\n\t\tconst module = this.compileShader(code);\n\n\t\treturn device.createComputePipelineAsync({\n\t\t\tlayout: \"auto\",\n\t\t\tcompute: {\n\t\t\t\tmodule,\n\t\t\t\tentryPoint,\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Clear all cached resources.\n\t */\n\tpublic clearCache(): void {\n\t\tthis.shaderCache.clear();\n\t\tfor (const buffer of this.bufferPool) {\n\t\t\tbuffer.destroy();\n\t\t}\n\t\tthis.bufferPool.length = 0;\n\t}\n\n\t/**\n\t * Destroy the context and release all resources.\n\t */\n\tpublic destroy(): void {\n\t\tthis.clearCache();\n\t\tif (this.device !== null) {\n\t\t\tthis.device.destroy();\n\t\t\tthis.device = null;\n\t\t}\n\t}\n\n\t/**\n\t * Simple string hash for cache keys.\n\t */\n\tprivate hashCode(str: string): string {\n\t\tlet hash = 0;\n\t\tfor (let i = 0; i < str.length; i++) {\n\t\t\tconst char = str.charCodeAt(i);\n\t\t\thash = ((hash << 5) - hash + char) | 0;\n\t\t}\n\t\treturn hash.toString(16);\n\t}\n}\n\n// Singleton instance for default context\nlet defaultContext: GPUContext | null = null;\n\n/**\n * Get or create the default GPU context.\n *\n * This is a lazy singleton - the device is not acquired until\n * acquireDevice() is called.\n *\n * @param options - Context creation options\n * @returns GPU context instance\n */\nexport function getGPUContext(options?: GPUContextOptions): GPUContext {\n\tif (defaultContext === null || (options?.forceNew ?? false)) {\n\t\tdefaultContext = new GPUContext();\n\t}\n\treturn defaultContext;\n}\n\n/**\n * Create a new isolated GPU context.\n *\n * Use this when you need separate resource pools or device management.\n *\n * @returns New GPU context instance\n */\nexport function createGPUContext(): GPUContext {\n\treturn new GPUContext();\n}\n"],"mappings":"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"}
|
package/dist/gpu/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/gpu/types.ts","../../src/gpu/csr.ts","../../src/gpu/detect.ts","../../src/gpu/context.ts"],"sourcesContent":["/**\n * Type definitions for the WebGPU compute backend.\n *\n * These types define the backend selection options and result types\n * for GPU-accelerated graph operations.\n */\n\n/**\n * Backend selection for compute operations.\n *\n * - 'cpu': Synchronous CPU implementation (default, always available)\n * - 'gpu': Async GPU implementation (throws if WebGPU unavailable)\n * - 'auto': Async, detect WebGPU at runtime, fall back to CPU silently\n */\nexport type ComputeBackend = \"auto\" | \"gpu\" | \"cpu\";\n\n/**\n * Result of WebGPU availability detection.\n */\nexport interface GPUDetectionResult {\n\t/** Whether WebGPU is available in the current environment */\n\treadonly available: boolean;\n\t/** Human-readable reason if unavailable */\n\treadonly reason?: string;\n}\n\n/**\n * Error thrown when GPU backend is requested but unavailable.\n */\nexport class GPUNotAvailableError extends Error {\n\tpublic constructor(reason: string) {\n\t\tsuper(`WebGPU not available: ${reason}`);\n\t\tthis.name = \"GPUNotAvailableError\";\n\t}\n}\n\n/**\n * Result wrapper for GPU operations that may fall back to CPU.\n */\nexport interface ComputeResult<T> {\n\t/** The computed result value */\n\treadonly value: T;\n\t/** Which backend was actually used */\n\treadonly backend: \"gpu\" | \"cpu\";\n\t/** Time taken in milliseconds (optional, for profiling) */\n\treadonly elapsedMs?: number;\n}\n\n/**\n * Configuration options for GPU compute operations.\n */\nexport interface GPUComputeOptions {\n\t/** Backend selection: 'auto', 'gpu', or 'cpu' */\n\treadonly backend?: ComputeBackend;\n\t/** Optional GPU device to reuse (avoids device acquisition overhead) */\n\treadonly device?: GPUDevice;\n}\n","/**\n * Compressed Sparse Row (CSR) matrix representation for GPU computation.\n *\n * CSR format is memory-efficient for sparse graphs and maps well to\n * GPU parallel operations. The format stores adjacency information\n * in three arrays: row offsets, column indices, and optional values.\n */\n\nimport type { NodeId, Direction, NodeData, EdgeData } from \"../graph/types\";\nimport type { ReadableGraph } from \"../graph\";\n\n/**\n * CSR matrix representation of a graph adjacency structure.\n *\n * The rowOffsets array has length nodeCount + 1, where rowOffsets[i]\n * gives the start index in colIndices for node i's neighbours.\n * The neighbours of node i are colIndices[rowOffsets[i] : rowOffsets[i+1]].\n */\nexport interface CSRMatrix {\n\t/** Row offsets array (length: nodeCount + 1) */\n\treadonly rowOffsets: Uint32Array;\n\t/** Column indices array (length: edgeCount for directed, 2*edgeCount for undirected) */\n\treadonly colIndices: Uint32Array;\n\t/** Optional edge weights aligned with colIndices */\n\treadonly values?: Float32Array;\n\t/** Number of nodes in the graph */\n\treadonly nodeCount: number;\n\t/** Number of directed edges (undirected edges counted once) */\n\treadonly edgeCount: number;\n}\n\n/**\n * Mapping from node IDs to CSR indices.\n *\n * Required because CSR uses dense integer indices while graphs\n * may use arbitrary string identifiers.\n */\nexport interface CSRIndexMap {\n\t/** Map from NodeId to CSR row index */\n\treadonly nodeToIndex: ReadonlyMap<NodeId, number>;\n\t/** Map from CSR row index to NodeId */\n\treadonly indexToNode: readonly NodeId[];\n}\n\n/**\n * Combined CSR matrix with index mapping.\n */\nexport interface CSRGraph {\n\treadonly csr: CSRMatrix;\n\treadonly indexMap: CSRIndexMap;\n}\n\n/**\n * Group of GPU buffers holding a CSR matrix.\n *\n * Each buffer is created with appropriate usage flags for compute operations.\n */\nexport interface GPUBufferGroup {\n\t/** Buffer containing rowOffsets data */\n\treadonly rowOffsets: GPUBuffer;\n\t/** Buffer containing colIndices data */\n\treadonly colIndices: GPUBuffer;\n\t/** Buffer containing values data (optional) */\n\treadonly values?: GPUBuffer;\n\t/** Number of nodes */\n\treadonly nodeCount: number;\n\t/** Number of edges */\n\treadonly edgeCount: number;\n}\n\n/**\n * Convert a ReadableGraph to CSR format.\n *\n * For undirected graphs, each edge is stored twice (once in each direction).\n * For directed graphs, edges are stored in the out-direction by default.\n *\n * @param graph - The graph to convert\n * @param direction - Edge direction to include (default: 'out' for directed, 'both' for undirected)\n * @returns CSR representation with index mapping\n */\nexport function graphToCSR<N extends NodeData, E extends EdgeData>(\n\tgraph: ReadableGraph<N, E>,\n\tdirection: Direction = graph.directed ? \"out\" : \"both\",\n): CSRGraph {\n\t// Build node index mapping\n\tconst nodeToIndex = new Map<NodeId, number>();\n\tconst indexToNode: NodeId[] = [];\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst index = indexToNode.length;\n\t\tnodeToIndex.set(nodeId, index);\n\t\tindexToNode.push(nodeId);\n\t}\n\n\tconst nodeCount = indexToNode.length;\n\n\t// Count edges per node to build row offsets\n\tconst degrees = new Uint32Array(nodeCount);\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst srcIndex = nodeToIndex.get(nodeId);\n\t\tif (srcIndex === undefined) continue;\n\t\tdegrees[srcIndex] = graph.degree(nodeId, direction);\n\t}\n\n\t// Calculate total edge count\n\tlet totalEdges = 0;\n\tfor (let i = 0; i < nodeCount; i++) {\n\t\ttotalEdges += degrees[i] ?? 0;\n\t}\n\n\t// Build rowOffsets array\n\tconst rowOffsets = new Uint32Array(nodeCount + 1);\n\tfor (let i = 0; i < nodeCount; i++) {\n\t\trowOffsets[i + 1] = (rowOffsets[i] ?? 0) + (degrees[i] ?? 0);\n\t}\n\n\t// Build colIndices and values arrays\n\tconst colIndices = new Uint32Array(totalEdges);\n\tconst values = new Float32Array(totalEdges);\n\n\tfor (const nodeId of graph.nodeIds()) {\n\t\tconst srcIndex = nodeToIndex.get(nodeId);\n\t\tif (srcIndex === undefined) continue;\n\n\t\tconst baseOffset = rowOffsets[srcIndex] ?? 0;\n\t\tlet localOffset = 0;\n\n\t\tfor (const neighbourId of graph.neighbours(nodeId, direction)) {\n\t\t\tconst dstIndex = nodeToIndex.get(neighbourId);\n\t\t\tif (dstIndex === undefined) continue;\n\n\t\t\tconst edgeIdx = baseOffset + localOffset;\n\t\t\tcolIndices[edgeIdx] = dstIndex;\n\n\t\t\t// Get edge weight if available\n\t\t\tconst edge = graph.getEdge(nodeId, neighbourId);\n\t\t\tvalues[edgeIdx] = edge?.weight ?? 1.0;\n\n\t\t\tlocalOffset++;\n\t\t}\n\t}\n\n\tconst csr: CSRMatrix = {\n\t\trowOffsets,\n\t\tcolIndices,\n\t\tvalues,\n\t\tnodeCount,\n\t\tedgeCount: graph.directed ? graph.edgeCount : graph.edgeCount * 2,\n\t};\n\n\tconst indexMap: CSRIndexMap = {\n\t\tnodeToIndex,\n\t\tindexToNode,\n\t};\n\n\treturn { csr, indexMap };\n}\n\n/**\n * Create GPU buffers from a CSR matrix.\n *\n * Buffers are created with:\n * - rowOffsets/colIndices: STORAGE | COPY_DST\n * - values: STORAGE | COPY_DST (if present)\n *\n * @param device - GPU device to create buffers on\n * @param csr - CSR matrix to upload\n * @returns GPU buffer group\n */\nexport function csrToGPUBuffers(\n\tdevice: GPUDevice,\n\tcsr: CSRMatrix,\n): GPUBufferGroup {\n\t// Row offsets buffer\n\tconst rowOffsetsBuffer = device.createBuffer({\n\t\tsize: csr.rowOffsets.byteLength,\n\t\tusage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\n\t\tmappedAtCreation: false,\n\t});\n\tdevice.queue.writeBuffer(rowOffsetsBuffer, 0, csr.rowOffsets);\n\n\t// Column indices buffer\n\tconst colIndicesBuffer = device.createBuffer({\n\t\tsize: csr.colIndices.byteLength,\n\t\tusage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\n\t\tmappedAtCreation: false,\n\t});\n\tdevice.queue.writeBuffer(colIndicesBuffer, 0, csr.colIndices);\n\n\t// Values buffer (optional)\n\tlet valuesBuffer: GPUBuffer | undefined;\n\tif (csr.values !== undefined) {\n\t\tvaluesBuffer = device.createBuffer({\n\t\t\tsize: csr.values.byteLength,\n\t\t\tusage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\n\t\t\tmappedAtCreation: false,\n\t\t});\n\t\tdevice.queue.writeBuffer(valuesBuffer, 0, csr.values);\n\t}\n\n\tif (valuesBuffer !== undefined) {\n\t\treturn {\n\t\t\trowOffsets: rowOffsetsBuffer,\n\t\t\tcolIndices: colIndicesBuffer,\n\t\t\tvalues: valuesBuffer,\n\t\t\tnodeCount: csr.nodeCount,\n\t\t\tedgeCount: csr.edgeCount,\n\t\t};\n\t}\n\treturn {\n\t\trowOffsets: rowOffsetsBuffer,\n\t\tcolIndices: colIndicesBuffer,\n\t\tnodeCount: csr.nodeCount,\n\t\tedgeCount: csr.edgeCount,\n\t};\n}\n\n/**\n * Create a result buffer for reading compute output.\n *\n * @param device - GPU device\n * @param byteLength - Size of the buffer in bytes\n * @returns GPU buffer configured for map reading\n */\nexport function createResultBuffer(\n\tdevice: GPUDevice,\n\tbyteLength: number,\n): GPUBuffer {\n\treturn device.createBuffer({\n\t\tsize: byteLength,\n\t\tusage:\n\t\t\tGPUBufferUsage.STORAGE |\n\t\t\tGPUBufferUsage.COPY_SRC |\n\t\t\tGPUBufferUsage.MAP_READ,\n\t});\n}\n\n/**\n * Read data from a GPU buffer to CPU.\n *\n * @param device - GPU device\n * @param buffer - Buffer to read from\n * @returns ArrayBuffer containing the buffer data\n */\nexport async function readBufferToCPU(\n\tdevice: GPUDevice,\n\tbuffer: GPUBuffer,\n): Promise<ArrayBuffer> {\n\tawait buffer.mapAsync(GPUMapMode.READ);\n\tconst data = buffer.getMappedRange().slice(0);\n\tbuffer.unmap();\n\treturn data;\n}\n","/**\n * WebGPU availability detection for browser and Node.js environments.\n *\n * This module provides runtime detection of WebGPU support without\n * requiring any polyfills or runtime dependencies.\n */\n\nimport type { GPUDetectionResult } from \"./types\";\n\n/**\n * Detect WebGPU availability in the current environment.\n *\n * Checks for:\n * - Browser: navigator.gpu\n * - Node.js: global GPU constructor (Node.js 21+ with --experimental-webgpu)\n * - Deno: global GPU constructor\n *\n * @returns Detection result with availability status and reason\n */\nexport function detectWebGPU(): GPUDetectionResult {\n\t// Check for browser WebGPU API\n\tif (typeof navigator !== \"undefined\" && \"gpu\" in navigator) {\n\t\treturn { available: true };\n\t}\n\n\t// Check for Node.js / Deno global GPU\n\tif (typeof globalThis !== \"undefined\" && \"GPU\" in globalThis) {\n\t\treturn { available: true };\n\t}\n\n\t// WebGPU not available\n\tconst reasons: string[] = [];\n\n\tif (typeof navigator === \"undefined\" && typeof globalThis === \"undefined\") {\n\t\treasons.push(\"no global scope detected\");\n\t} else if (typeof navigator !== \"undefined\" && !(\"gpu\" in navigator)) {\n\t\treasons.push(\"navigator.gpu not present (browser may not support WebGPU)\");\n\t} else if (typeof globalThis !== \"undefined\" && !(\"GPU\" in globalThis)) {\n\t\treasons.push(\n\t\t\t\"global GPU not present (Node.js requires v21+ with --experimental-webgpu flag)\",\n\t\t);\n\t}\n\n\treturn {\n\t\tavailable: false,\n\t\treason: reasons.length > 0 ? reasons.join(\"; \") : \"unknown environment\",\n\t};\n}\n\n/**\n * Check if WebGPU is available (convenience function).\n *\n * @returns true if WebGPU is available, false otherwise\n */\nexport function isWebGPUAvailable(): boolean {\n\treturn detectWebGPU().available;\n}\n\n/**\n * Assert that WebGPU is available, throwing an error if not.\n *\n * @throws Error if WebGPU is not available\n */\nexport function assertWebGPUAvailable(): void {\n\tconst result = detectWebGPU();\n\tif (!result.available) {\n\t\tthrow new Error(\n\t\t\t`WebGPU required but not available: ${result.reason ?? \"unknown reason\"}`,\n\t\t);\n\t}\n}\n","/**\n * GPU context management for WebGPU compute operations.\n *\n * Provides device acquisition, buffer pooling, and shader compilation\n * with caching for efficient GPU resource management.\n */\n\nimport { detectWebGPU } from \"./detect\";\n\n/**\n * Type guard to check if an object has a WebGPU-compatible requestAdapter method.\n */\nfunction hasRequestAdapter(obj: unknown): obj is {\n\trequestAdapter: (\n\t\topts?: GPURequestAdapterOptions,\n\t) => Promise<GPUAdapter | null>;\n} {\n\tif (typeof obj !== \"object\" || obj === null) {\n\t\treturn false;\n\t}\n\tif (!(\"requestAdapter\" in obj)) {\n\t\treturn false;\n\t}\n\t// Use Object.getOwnPropertyDescriptor to safely check property type\n\tconst descriptor = Object.getOwnPropertyDescriptor(obj, \"requestAdapter\");\n\treturn descriptor !== undefined && typeof descriptor.value === \"function\";\n}\n\n/**\n * Options for creating a GPUContext.\n */\nexport interface GPUContextOptions {\n\t/** Power preference for GPU adapter */\n\treadonly powerPreference?: \"low-power\" | \"high-performance\";\n\t/** Force create new context even if one exists */\n\treadonly forceNew?: boolean;\n}\n\n/**\n * Shader module cache entry.\n */\ninterface CachedShader {\n\treadonly module: GPUShaderModule;\n\treadonly code: string;\n}\n\n/**\n * Manages GPU device, buffers, and compiled shaders.\n *\n * Use the singleton pattern via getGPUContext() for most use cases,\n * or create separate contexts for isolated GPU resource pools.\n */\nexport class GPUContext {\n\tprivate device: GPUDevice | null = null;\n\tprivate readonly shaderCache = new Map<string, CachedShader>();\n\tprivate readonly bufferPool: GPUBuffer[] = [];\n\tprivate readonly destroyedBuffers = new WeakSet<GPUBuffer>();\n\n\t/**\n\t * Check if this context has an acquired GPU device.\n\t */\n\tpublic get isReady(): boolean {\n\t\treturn this.device !== null;\n\t}\n\n\t/**\n\t * Get the GPU device, throwing if not acquired.\n\t */\n\tpublic getDevice(): GPUDevice {\n\t\tif (this.device === null) {\n\t\t\tthrow new Error(\n\t\t\t\t\"GPUContext not initialised. Call acquireDevice() first.\",\n\t\t\t);\n\t\t}\n\t\treturn this.device;\n\t}\n\n\t/**\n\t * Acquire a GPU device from the adapter.\n\t *\n\t * @param options - Context creation options\n\t * @returns true if device was acquired successfully\n\t * @throws Error if WebGPU is unavailable or device request fails\n\t */\n\tpublic async acquireDevice(\n\t\toptions: GPUContextOptions = {},\n\t): Promise<boolean> {\n\t\tconst detection = detectWebGPU();\n\t\tif (!detection.available) {\n\t\t\tthrow new Error(\n\t\t\t\t`WebGPU unavailable: ${detection.reason ?? \"unknown reason\"}`,\n\t\t\t);\n\t\t}\n\n\t\t// Get adapter\n\t\tlet adapter: GPUAdapter | null = null;\n\n\t\t// Build adapter options, only including powerPreference if defined\n\t\tconst adapterOpts: GPURequestAdapterOptions =\n\t\t\toptions.powerPreference !== undefined\n\t\t\t\t? { powerPreference: options.powerPreference }\n\t\t\t\t: {};\n\n\t\tif (typeof navigator !== \"undefined\" && \"gpu\" in navigator) {\n\t\t\tadapter = await navigator.gpu.requestAdapter(adapterOpts);\n\t\t}\n\n\t\t// Node.js / Deno fallback via global GPU\n\t\tif (\n\t\t\tadapter === null &&\n\t\t\ttypeof globalThis !== \"undefined\" &&\n\t\t\t\"GPU\" in globalThis\n\t\t) {\n\t\t\t// Access gpu property via Object.getOwnPropertyDescriptor to avoid type assertion\n\t\t\tconst gpuDescriptor = Object.getOwnPropertyDescriptor(globalThis, \"gpu\");\n\t\t\t// Pass descriptor value directly to type guard to avoid unsafe assignment\n\t\t\tif (\n\t\t\t\tgpuDescriptor !== undefined &&\n\t\t\t\thasRequestAdapter(gpuDescriptor.value)\n\t\t\t) {\n\t\t\t\tadapter = await gpuDescriptor.value.requestAdapter(adapterOpts);\n\t\t\t}\n\t\t}\n\n\t\tif (adapter === null) {\n\t\t\tthrow new Error(\"No GPU adapter found\");\n\t\t}\n\n\t\t// Request device\n\t\tthis.device = await adapter.requestDevice();\n\n\t\t// Handle device loss\n\t\tvoid this.device.lost.then((info: GPUDeviceLostInfo): void => {\n\t\t\tconsole.error(`GPU device lost: ${info.message}`);\n\t\t\tthis.device = null;\n\t\t\tthis.clearCache();\n\t\t});\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Compile a WGSL shader, using cache if available.\n\t *\n\t * @param code - WGSL shader code\n\t * @param key - Optional cache key (defaults to code hash)\n\t * @returns Compiled shader module\n\t */\n\tpublic compileShader(code: string, key?: string): GPUShaderModule {\n\t\tconst cacheKey = key ?? this.hashCode(code);\n\t\tconst cached = this.shaderCache.get(cacheKey);\n\n\t\tif (cached?.code === code) {\n\t\t\treturn cached.module;\n\t\t}\n\n\t\tconst device = this.getDevice();\n\t\tconst module = device.createShaderModule({ code });\n\n\t\tthis.shaderCache.set(cacheKey, { module, code });\n\t\treturn module;\n\t}\n\n\t/**\n\t * Create a GPU buffer, optionally reusing from pool.\n\t *\n\t * @param size - Buffer size in bytes\n\t * @param usage - Buffer usage flags\n\t * @returns GPU buffer\n\t */\n\tpublic createBuffer(size: number, usage: GPUBufferUsageFlags): GPUBuffer {\n\t\t// Try to find a suitable buffer in the pool\n\t\tfor (let i = 0; i < this.bufferPool.length; i++) {\n\t\t\tconst buffer = this.bufferPool[i];\n\t\t\tif (\n\t\t\t\tbuffer !== undefined &&\n\t\t\t\t!this.destroyedBuffers.has(buffer) &&\n\t\t\t\tbuffer.size >= size &&\n\t\t\t\t(buffer.usage & usage) === usage\n\t\t\t) {\n\t\t\t\tthis.bufferPool.splice(i, 1);\n\t\t\t\treturn buffer;\n\t\t\t}\n\t\t}\n\n\t\t// Create new buffer\n\t\tconst device = this.getDevice();\n\t\treturn device.createBuffer({ size, usage });\n\t}\n\n\t/**\n\t * Return a buffer to the pool for reuse.\n\t *\n\t * @param buffer - Buffer to recycle\n\t */\n\tpublic recycleBuffer(buffer: GPUBuffer): void {\n\t\t// Only pool if buffer is not destroyed\n\t\tif (!this.destroyedBuffers.has(buffer)) {\n\t\t\tthis.bufferPool.push(buffer);\n\t\t}\n\t}\n\n\t/**\n\t * Mark a buffer as destroyed (call before GPUBuffer.destroy()).\n\t *\n\t * @param buffer - Buffer to mark as destroyed\n\t */\n\tpublic markDestroyed(buffer: GPUBuffer): void {\n\t\tthis.destroyedBuffers.add(buffer);\n\t}\n\n\t/**\n\t * Create a compute pipeline from shader code.\n\t *\n\t * @param code - WGSL compute shader code\n\t * @param entryPoint - Entry point function name\n\t * @returns Compute pipeline\n\t */\n\tpublic createComputePipeline(\n\t\tcode: string,\n\t\tentryPoint = \"main\",\n\t): GPUComputePipeline {\n\t\tconst device = this.getDevice();\n\t\tconst module = this.compileShader(code);\n\n\t\treturn device.createComputePipeline({\n\t\t\tlayout: \"auto\",\n\t\t\tcompute: {\n\t\t\t\tmodule,\n\t\t\t\tentryPoint,\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Create a compute pipeline asynchronously (preferred for performance).\n\t *\n\t * @param code - WGSL compute shader code\n\t * @param entryPoint - Entry point function name\n\t * @returns Promise resolving to compute pipeline\n\t */\n\tpublic async createComputePipelineAsync(\n\t\tcode: string,\n\t\tentryPoint = \"main\",\n\t): Promise<GPUComputePipeline> {\n\t\tconst device = this.getDevice();\n\t\tconst module = this.compileShader(code);\n\n\t\treturn device.createComputePipelineAsync({\n\t\t\tlayout: \"auto\",\n\t\t\tcompute: {\n\t\t\t\tmodule,\n\t\t\t\tentryPoint,\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Clear all cached resources.\n\t */\n\tpublic clearCache(): void {\n\t\tthis.shaderCache.clear();\n\t\tfor (const buffer of this.bufferPool) {\n\t\t\tbuffer.destroy();\n\t\t}\n\t\tthis.bufferPool.length = 0;\n\t}\n\n\t/**\n\t * Destroy the context and release all resources.\n\t */\n\tpublic destroy(): void {\n\t\tthis.clearCache();\n\t\tif (this.device !== null) {\n\t\t\tthis.device.destroy();\n\t\t\tthis.device = null;\n\t\t}\n\t}\n\n\t/**\n\t * Simple string hash for cache keys.\n\t */\n\tprivate hashCode(str: string): string {\n\t\tlet hash = 0;\n\t\tfor (let i = 0; i < str.length; i++) {\n\t\t\tconst char = str.charCodeAt(i);\n\t\t\thash = ((hash << 5) - hash + char) | 0;\n\t\t}\n\t\treturn hash.toString(16);\n\t}\n}\n\n// Singleton instance for default context\nlet defaultContext: GPUContext | null = null;\n\n/**\n * Get or create the default GPU context.\n *\n * This is a lazy singleton - the device is not acquired until\n * acquireDevice() is called.\n *\n * @param options - Context creation options\n * @returns GPU context instance\n */\nexport function getGPUContext(options?: GPUContextOptions): GPUContext {\n\tif (defaultContext === null || (options?.forceNew ?? false)) {\n\t\tdefaultContext = new GPUContext();\n\t}\n\treturn defaultContext;\n}\n\n/**\n * Create a new isolated GPU context.\n *\n * Use this when you need separate resource pools or device management.\n *\n * @returns New GPU context instance\n */\nexport function createGPUContext(): GPUContext {\n\treturn new GPUContext();\n}\n"],"mappings":"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"}
|
package/dist/graph/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/graph/adjacency-map.ts"],"sourcesContent":["/**\n * Adjacency map graph implementation.\n *\n * This module provides a flexible graph implementation using nested Maps\n * for efficient adjacency list representation. It supports both directed\n * and undirected graphs with builder pattern for convenient construction.\n */\n\nimport type { NodeId, NodeData, EdgeData, Direction } from \"./types\";\nimport type { MutableGraph } from \"./interfaces\";\n\n/**\n * Graph implementation using adjacency map data structure.\n *\n * Uses Map<NodeId, N> for node storage and Map<NodeId, Map<NodeId, E>>\n * for adjacency representation. This provides O(1) average-case lookup\n * for nodes and edges, with memory proportional to V + E.\n *\n * @typeParam N - Node data type, must extend NodeData\n * @typeParam E - Edge data type, must extend EdgeData\n *\n * @example\n * ```typescript\n * // Create a directed citation graph\n * const graph = AdjacencyMapGraph.directed<AuthorNode, CitationEdge>()\n * .addNode({ id: 'A1', name: 'Alice' })\n * .addNode({ id: 'B1', name: 'Bob' })\n * .addEdge({ source: 'A1', target: 'B1', year: 2024 });\n * ```\n */\nexport class AdjacencyMapGraph<\n\tN extends NodeData = NodeData,\n\tE extends EdgeData = EdgeData,\n> implements MutableGraph<N, E> {\n\treadonly directed: boolean;\n\n\tprivate readonly nodes: Map<NodeId, N>;\n\tprivate readonly adjacency: Map<NodeId, Map<NodeId, E>>;\n\tprivate readonly reverseAdjacency: Map<NodeId, Map<NodeId, E>> | null;\n\tprivate _edgeCount: number;\n\n\tprivate constructor(directed: boolean) {\n\t\tthis.directed = directed;\n\t\tthis.nodes = new Map();\n\t\tthis.adjacency = new Map();\n\t\tthis.reverseAdjacency = directed ? new Map() : null;\n\t\tthis._edgeCount = 0;\n\t}\n\n\t/**\n\t * Create a new directed graph.\n\t *\n\t * In a directed graph, edges have direction from source to target.\n\t * The `neighbours` method with direction 'out' returns successors,\n\t * and direction 'in' returns predecessors.\n\t *\n\t * @typeParam N - Node data type\n\t * @typeParam E - Edge data type\n\t * @returns A new empty directed graph\n\t */\n\tstatic directed<\n\t\tN extends NodeData = NodeData,\n\t\tE extends EdgeData = EdgeData,\n\t>(): AdjacencyMapGraph<N, E> {\n\t\treturn new AdjacencyMapGraph<N, E>(true);\n\t}\n\n\t/**\n\t * Create a new undirected graph.\n\t *\n\t * In an undirected graph, edges have no direction. Adding an edge\n\t * from A to B automatically creates the connection from B to A.\n\t *\n\t * @typeParam N - Node data type\n\t * @typeParam E - Edge data type\n\t * @returns A new empty undirected graph\n\t */\n\tstatic undirected<\n\t\tN extends NodeData = NodeData,\n\t\tE extends EdgeData = EdgeData,\n\t>(): AdjacencyMapGraph<N, E> {\n\t\treturn new AdjacencyMapGraph<N, E>(false);\n\t}\n\n\tget nodeCount(): number {\n\t\treturn this.nodes.size;\n\t}\n\n\tget edgeCount(): number {\n\t\treturn this._edgeCount;\n\t}\n\n\thasNode(id: NodeId): boolean {\n\t\treturn this.nodes.has(id);\n\t}\n\n\tgetNode(id: NodeId): N | undefined {\n\t\treturn this.nodes.get(id);\n\t}\n\n\t/**\n\t * Iterate over all node identifiers in the graph.\n\t *\n\t * @returns An iterable of all node IDs\n\t */\n\t*nodeIds(): Iterable<NodeId> {\n\t\tyield* this.nodes.keys();\n\t}\n\n\tneighbours(id: NodeId, direction: Direction = \"out\"): Iterable<NodeId> {\n\t\tif (!this.nodes.has(id)) {\n\t\t\treturn [];\n\t\t}\n\n\t\tif (this.directed) {\n\t\t\tif (direction === \"out\") {\n\t\t\t\treturn this.adjacency.get(id)?.keys() ?? [];\n\t\t\t}\n\t\t\tif (direction === \"in\") {\n\t\t\t\treturn this.reverseAdjacency?.get(id)?.keys() ?? [];\n\t\t\t}\n\t\t\t// direction === 'both'\n\t\t\treturn this.iterateBothDirections(id);\n\t\t}\n\n\t\t// Undirected: all neighbours are in adjacency\n\t\treturn this.adjacency.get(id)?.keys() ?? [];\n\t}\n\n\tprivate *iterateBothDirections(id: NodeId): Iterable<NodeId> {\n\t\tconst seen = new Set<NodeId>();\n\n\t\t// Yield outgoing neighbours\n\t\tconst outNeighbours = this.adjacency.get(id);\n\t\tif (outNeighbours !== undefined) {\n\t\t\tfor (const neighbour of outNeighbours.keys()) {\n\t\t\t\tif (!seen.has(neighbour)) {\n\t\t\t\t\tseen.add(neighbour);\n\t\t\t\t\tyield neighbour;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Yield incoming neighbours\n\t\tconst inNeighbours = this.reverseAdjacency?.get(id);\n\t\tif (inNeighbours !== undefined) {\n\t\t\tfor (const neighbour of inNeighbours.keys()) {\n\t\t\t\tif (!seen.has(neighbour)) {\n\t\t\t\t\tseen.add(neighbour);\n\t\t\t\t\tyield neighbour;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdegree(id: NodeId, direction: Direction = \"out\"): number {\n\t\tif (!this.nodes.has(id)) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (this.directed) {\n\t\t\tif (direction === \"out\") {\n\t\t\t\treturn this.adjacency.get(id)?.size ?? 0;\n\t\t\t}\n\t\t\tif (direction === \"in\") {\n\t\t\t\treturn this.reverseAdjacency?.get(id)?.size ?? 0;\n\t\t\t}\n\t\t\t// direction === 'both': count unique neighbours\n\t\t\tconst outSize = this.adjacency.get(id)?.size ?? 0;\n\t\t\tconst inSize = this.reverseAdjacency?.get(id)?.size ?? 0;\n\t\t\t// Simple sum is sufficient as edges are stored separately\n\t\t\treturn outSize + inSize;\n\t\t}\n\n\t\t// Undirected\n\t\treturn this.adjacency.get(id)?.size ?? 0;\n\t}\n\n\tgetEdge(source: NodeId, target: NodeId): E | undefined {\n\t\t// For undirected, try both orders\n\t\tconst forward = this.adjacency.get(source)?.get(target);\n\t\tif (forward !== undefined) {\n\t\t\treturn forward;\n\t\t}\n\n\t\tif (!this.directed) {\n\t\t\treturn this.adjacency.get(target)?.get(source);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t*edges(): Iterable<E> {\n\t\tconst emitted = new Set<string>();\n\n\t\tfor (const [, neighbours] of this.adjacency) {\n\t\t\tfor (const [, edge] of neighbours) {\n\t\t\t\tif (this.directed) {\n\t\t\t\t\tyield edge;\n\t\t\t\t} else {\n\t\t\t\t\t// For undirected, avoid emitting duplicate edges\n\t\t\t\t\tconst key = this.edgeKey(edge.source, edge.target);\n\t\t\t\t\tif (!emitted.has(key)) {\n\t\t\t\t\t\temitted.add(key);\n\t\t\t\t\t\tyield edge;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate edgeKey(source: NodeId, target: NodeId): string {\n\t\t// Create a canonical key for undirected edges\n\t\tconst [a, b] = source < target ? [source, target] : [target, source];\n\t\treturn `${a}::${b}`;\n\t}\n\n\t/**\n\t * Add a node to the graph (builder pattern).\n\t *\n\t * If a node with the same ID already exists, it is not replaced.\n\t *\n\t * @param node - The node data to add\n\t * @returns this (for method chaining)\n\t */\n\taddNode(node: N): this {\n\t\tif (!this.nodes.has(node.id)) {\n\t\t\tthis.nodes.set(node.id, node);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add an edge to the graph (builder pattern).\n\t *\n\t * @param edge - The edge data to add\n\t * @returns this (for method chaining)\n\t * @throws Error if either endpoint node does not exist\n\t */\n\taddEdge(edge: E): this {\n\t\t// Ensure both nodes exist\n\t\tif (!this.nodes.has(edge.source) || !this.nodes.has(edge.target)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot add edge: nodes ${edge.source} and/or ${edge.target} do not exist`,\n\t\t\t);\n\t\t}\n\n\t\tif (!this.directed) {\n\t\t\t// Canonical direction: source < target (prevents duplicate storage)\n\t\t\tconst [cSource, cTarget] =\n\t\t\t\tedge.source < edge.target\n\t\t\t\t\t? [edge.source, edge.target]\n\t\t\t\t\t: [edge.target, edge.source];\n\t\t\t// Check if edge already exists before incrementing edgeCount\n\t\t\tconst existingEdge = this.adjacency.get(cSource)?.get(cTarget);\n\t\t\tif (existingEdge !== undefined) {\n\t\t\t\t// Edge already exists — update data but don't increment count\n\t\t\t\tthis.adjacency.get(cSource)?.set(cTarget, edge);\n\t\t\t\treturn this;\n\t\t\t}\n\t\t}\n\n\t\t// Store in forward adjacency\n\t\tlet forwardMap = this.adjacency.get(edge.source);\n\t\tif (forwardMap === undefined) {\n\t\t\tforwardMap = new Map();\n\t\t\tthis.adjacency.set(edge.source, forwardMap);\n\t\t}\n\n\t\tconst isNewEdge = !forwardMap.has(edge.target);\n\t\tforwardMap.set(edge.target, edge);\n\n\t\tif (this.directed) {\n\t\t\t// Store reverse reference for efficient predecessor lookup\n\t\t\tlet reverseMap = this.reverseAdjacency?.get(edge.target);\n\t\t\tif (reverseMap === undefined) {\n\t\t\t\treverseMap = new Map();\n\t\t\t\tthis.reverseAdjacency?.set(edge.target, reverseMap);\n\t\t\t}\n\t\t\treverseMap.set(edge.source, edge);\n\t\t} else {\n\t\t\t// For undirected, also store in reverse direction\n\t\t\tlet reverseMap = this.adjacency.get(edge.target);\n\t\t\tif (reverseMap === undefined) {\n\t\t\t\treverseMap = new Map();\n\t\t\t\tthis.adjacency.set(edge.target, reverseMap);\n\t\t\t}\n\t\t\treverseMap.set(edge.source, edge);\n\t\t}\n\n\t\tif (isNewEdge) {\n\t\t\tthis._edgeCount++;\n\t\t}\n\t\treturn this;\n\t}\n\n\tremoveNode(id: NodeId): boolean {\n\t\tif (!this.nodes.has(id)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Remove all outgoing edges from this node\n\t\tconst outNeighbours = [...(this.adjacency.get(id)?.keys() ?? [])];\n\t\tfor (const neighbour of outNeighbours) {\n\t\t\tthis.removeEdgeInternal(id, neighbour);\n\t\t}\n\n\t\t// For directed graphs, also remove incoming edges to this node\n\t\tif (this.directed && this.reverseAdjacency !== null) {\n\t\t\tconst inNeighbours = [...(this.reverseAdjacency.get(id)?.keys() ?? [])];\n\t\t\tfor (const neighbour of inNeighbours) {\n\t\t\t\t// Remove the edge from neighbour -> id\n\t\t\t\tthis.removeEdgeFromDirected(neighbour, id);\n\t\t\t}\n\t\t}\n\n\t\t// Remove the node itself\n\t\tthis.nodes.delete(id);\n\t\tthis.adjacency.delete(id);\n\t\tthis.reverseAdjacency?.delete(id);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Remove an edge from a directed graph, updating both adjacency maps.\n\t * This handles the case where we're removing an edge that points TO the removed node.\n\t */\n\tprivate removeEdgeFromDirected(source: NodeId, target: NodeId): void {\n\t\t// Remove from forward adjacency (source -> target)\n\t\tconst forwardMap = this.adjacency.get(source);\n\t\tif (forwardMap?.delete(target) === true) {\n\t\t\tthis._edgeCount--;\n\t\t}\n\n\t\t// Remove from reverse adjacency\n\t\tthis.reverseAdjacency?.get(target)?.delete(source);\n\t}\n\n\tprivate removeEdgeInternal(source: NodeId, target: NodeId): void {\n\t\t// Remove from forward adjacency\n\t\tconst forwardMap = this.adjacency.get(source);\n\t\tif (forwardMap?.delete(target) === true) {\n\t\t\tthis._edgeCount--;\n\t\t}\n\n\t\tif (this.directed) {\n\t\t\t// Remove from reverse adjacency\n\t\t\tthis.reverseAdjacency?.get(target)?.delete(source);\n\t\t} else {\n\t\t\t// For undirected, remove both directions\n\t\t\tthis.adjacency.get(target)?.delete(source);\n\t\t}\n\t}\n\n\tremoveEdge(source: NodeId, target: NodeId): boolean {\n\t\t// Check if edge exists\n\t\tif (!this.hasEdgeInternal(source, target)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.removeEdgeInternal(source, target);\n\t\treturn true;\n\t}\n\n\tprivate hasEdgeInternal(source: NodeId, target: NodeId): boolean {\n\t\tconst forward = this.adjacency.get(source)?.has(target) === true;\n\t\tif (forward) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!this.directed) {\n\t\t\treturn this.adjacency.get(target)?.has(source) === true;\n\t\t}\n\n\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
|
package/dist/graph/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/graph/adjacency-map.ts"],"sourcesContent":["/**\n * Adjacency map graph implementation.\n *\n * This module provides a flexible graph implementation using nested Maps\n * for efficient adjacency list representation. It supports both directed\n * and undirected graphs with builder pattern for convenient construction.\n */\n\nimport type { NodeId, NodeData, EdgeData, Direction } from \"./types\";\nimport type { MutableGraph } from \"./interfaces\";\n\n/**\n * Graph implementation using adjacency map data structure.\n *\n * Uses Map<NodeId, N> for node storage and Map<NodeId, Map<NodeId, E>>\n * for adjacency representation. This provides O(1) average-case lookup\n * for nodes and edges, with memory proportional to V + E.\n *\n * @typeParam N - Node data type, must extend NodeData\n * @typeParam E - Edge data type, must extend EdgeData\n *\n * @example\n * ```typescript\n * // Create a directed citation graph\n * const graph = AdjacencyMapGraph.directed<AuthorNode, CitationEdge>()\n * .addNode({ id: 'A1', name: 'Alice' })\n * .addNode({ id: 'B1', name: 'Bob' })\n * .addEdge({ source: 'A1', target: 'B1', year: 2024 });\n * ```\n */\nexport class AdjacencyMapGraph<\n\tN extends NodeData = NodeData,\n\tE extends EdgeData = EdgeData,\n> implements MutableGraph<N, E> {\n\treadonly directed: boolean;\n\n\tprivate readonly nodes: Map<NodeId, N>;\n\tprivate readonly adjacency: Map<NodeId, Map<NodeId, E>>;\n\tprivate readonly reverseAdjacency: Map<NodeId, Map<NodeId, E>> | null;\n\tprivate _edgeCount: number;\n\n\tprivate constructor(directed: boolean) {\n\t\tthis.directed = directed;\n\t\tthis.nodes = new Map();\n\t\tthis.adjacency = new Map();\n\t\tthis.reverseAdjacency = directed ? new Map() : null;\n\t\tthis._edgeCount = 0;\n\t}\n\n\t/**\n\t * Create a new directed graph.\n\t *\n\t * In a directed graph, edges have direction from source to target.\n\t * The `neighbours` method with direction 'out' returns successors,\n\t * and direction 'in' returns predecessors.\n\t *\n\t * @typeParam N - Node data type\n\t * @typeParam E - Edge data type\n\t * @returns A new empty directed graph\n\t */\n\tstatic directed<\n\t\tN extends NodeData = NodeData,\n\t\tE extends EdgeData = EdgeData,\n\t>(): AdjacencyMapGraph<N, E> {\n\t\treturn new AdjacencyMapGraph<N, E>(true);\n\t}\n\n\t/**\n\t * Create a new undirected graph.\n\t *\n\t * In an undirected graph, edges have no direction. Adding an edge\n\t * from A to B automatically creates the connection from B to A.\n\t *\n\t * @typeParam N - Node data type\n\t * @typeParam E - Edge data type\n\t * @returns A new empty undirected graph\n\t */\n\tstatic undirected<\n\t\tN extends NodeData = NodeData,\n\t\tE extends EdgeData = EdgeData,\n\t>(): AdjacencyMapGraph<N, E> {\n\t\treturn new AdjacencyMapGraph<N, E>(false);\n\t}\n\n\tget nodeCount(): number {\n\t\treturn this.nodes.size;\n\t}\n\n\tget edgeCount(): number {\n\t\treturn this._edgeCount;\n\t}\n\n\thasNode(id: NodeId): boolean {\n\t\treturn this.nodes.has(id);\n\t}\n\n\tgetNode(id: NodeId): N | undefined {\n\t\treturn this.nodes.get(id);\n\t}\n\n\t/**\n\t * Iterate over all node identifiers in the graph.\n\t *\n\t * @returns An iterable of all node IDs\n\t */\n\t*nodeIds(): Iterable<NodeId> {\n\t\tyield* this.nodes.keys();\n\t}\n\n\tneighbours(id: NodeId, direction: Direction = \"out\"): Iterable<NodeId> {\n\t\tif (!this.nodes.has(id)) {\n\t\t\treturn [];\n\t\t}\n\n\t\tif (this.directed) {\n\t\t\tif (direction === \"out\") {\n\t\t\t\treturn this.adjacency.get(id)?.keys() ?? [];\n\t\t\t}\n\t\t\tif (direction === \"in\") {\n\t\t\t\treturn this.reverseAdjacency?.get(id)?.keys() ?? [];\n\t\t\t}\n\t\t\t// direction === 'both'\n\t\t\treturn this.iterateBothDirections(id);\n\t\t}\n\n\t\t// Undirected: all neighbours are in adjacency\n\t\treturn this.adjacency.get(id)?.keys() ?? [];\n\t}\n\n\tprivate *iterateBothDirections(id: NodeId): Iterable<NodeId> {\n\t\tconst seen = new Set<NodeId>();\n\n\t\t// Yield outgoing neighbours\n\t\tconst outNeighbours = this.adjacency.get(id);\n\t\tif (outNeighbours !== undefined) {\n\t\t\tfor (const neighbour of outNeighbours.keys()) {\n\t\t\t\tif (!seen.has(neighbour)) {\n\t\t\t\t\tseen.add(neighbour);\n\t\t\t\t\tyield neighbour;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Yield incoming neighbours\n\t\tconst inNeighbours = this.reverseAdjacency?.get(id);\n\t\tif (inNeighbours !== undefined) {\n\t\t\tfor (const neighbour of inNeighbours.keys()) {\n\t\t\t\tif (!seen.has(neighbour)) {\n\t\t\t\t\tseen.add(neighbour);\n\t\t\t\t\tyield neighbour;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdegree(id: NodeId, direction: Direction = \"out\"): number {\n\t\tif (!this.nodes.has(id)) {\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (this.directed) {\n\t\t\tif (direction === \"out\") {\n\t\t\t\treturn this.adjacency.get(id)?.size ?? 0;\n\t\t\t}\n\t\t\tif (direction === \"in\") {\n\t\t\t\treturn this.reverseAdjacency?.get(id)?.size ?? 0;\n\t\t\t}\n\t\t\t// direction === 'both': count unique neighbours\n\t\t\tconst outSize = this.adjacency.get(id)?.size ?? 0;\n\t\t\tconst inSize = this.reverseAdjacency?.get(id)?.size ?? 0;\n\t\t\t// Simple sum is sufficient as edges are stored separately\n\t\t\treturn outSize + inSize;\n\t\t}\n\n\t\t// Undirected\n\t\treturn this.adjacency.get(id)?.size ?? 0;\n\t}\n\n\tgetEdge(source: NodeId, target: NodeId): E | undefined {\n\t\t// For undirected, try both orders\n\t\tconst forward = this.adjacency.get(source)?.get(target);\n\t\tif (forward !== undefined) {\n\t\t\treturn forward;\n\t\t}\n\n\t\tif (!this.directed) {\n\t\t\treturn this.adjacency.get(target)?.get(source);\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\t*edges(): Iterable<E> {\n\t\tconst emitted = new Set<string>();\n\n\t\tfor (const [, neighbours] of this.adjacency) {\n\t\t\tfor (const [, edge] of neighbours) {\n\t\t\t\tif (this.directed) {\n\t\t\t\t\tyield edge;\n\t\t\t\t} else {\n\t\t\t\t\t// For undirected, avoid emitting duplicate edges\n\t\t\t\t\tconst key = this.edgeKey(edge.source, edge.target);\n\t\t\t\t\tif (!emitted.has(key)) {\n\t\t\t\t\t\temitted.add(key);\n\t\t\t\t\t\tyield edge;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate edgeKey(source: NodeId, target: NodeId): string {\n\t\t// Create a canonical key for undirected edges\n\t\tconst [a, b] = source < target ? [source, target] : [target, source];\n\t\treturn `${a}::${b}`;\n\t}\n\n\t/**\n\t * Add a node to the graph (builder pattern).\n\t *\n\t * If a node with the same ID already exists, it is not replaced.\n\t *\n\t * @param node - The node data to add\n\t * @returns this (for method chaining)\n\t */\n\taddNode(node: N): this {\n\t\tif (!this.nodes.has(node.id)) {\n\t\t\tthis.nodes.set(node.id, node);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Add an edge to the graph (builder pattern).\n\t *\n\t * @param edge - The edge data to add\n\t * @returns this (for method chaining)\n\t * @throws Error if either endpoint node does not exist\n\t */\n\taddEdge(edge: E): this {\n\t\t// Ensure both nodes exist\n\t\tif (!this.nodes.has(edge.source) || !this.nodes.has(edge.target)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot add edge: nodes ${edge.source} and/or ${edge.target} do not exist`,\n\t\t\t);\n\t\t}\n\n\t\tif (!this.directed) {\n\t\t\t// Canonical direction: source < target (prevents duplicate storage)\n\t\t\tconst [cSource, cTarget] =\n\t\t\t\tedge.source < edge.target\n\t\t\t\t\t? [edge.source, edge.target]\n\t\t\t\t\t: [edge.target, edge.source];\n\t\t\t// Check if edge already exists before incrementing edgeCount\n\t\t\tconst existingEdge = this.adjacency.get(cSource)?.get(cTarget);\n\t\t\tif (existingEdge !== undefined) {\n\t\t\t\t// Edge already exists — update data but don't increment count\n\t\t\t\tthis.adjacency.get(cSource)?.set(cTarget, edge);\n\t\t\t\treturn this;\n\t\t\t}\n\t\t}\n\n\t\t// Store in forward adjacency\n\t\tlet forwardMap = this.adjacency.get(edge.source);\n\t\tif (forwardMap === undefined) {\n\t\t\tforwardMap = new Map();\n\t\t\tthis.adjacency.set(edge.source, forwardMap);\n\t\t}\n\n\t\tconst isNewEdge = !forwardMap.has(edge.target);\n\t\tforwardMap.set(edge.target, edge);\n\n\t\tif (this.directed) {\n\t\t\t// Store reverse reference for efficient predecessor lookup\n\t\t\tlet reverseMap = this.reverseAdjacency?.get(edge.target);\n\t\t\tif (reverseMap === undefined) {\n\t\t\t\treverseMap = new Map();\n\t\t\t\tthis.reverseAdjacency?.set(edge.target, reverseMap);\n\t\t\t}\n\t\t\treverseMap.set(edge.source, edge);\n\t\t} else {\n\t\t\t// For undirected, also store in reverse direction\n\t\t\tlet reverseMap = this.adjacency.get(edge.target);\n\t\t\tif (reverseMap === undefined) {\n\t\t\t\treverseMap = new Map();\n\t\t\t\tthis.adjacency.set(edge.target, reverseMap);\n\t\t\t}\n\t\t\treverseMap.set(edge.source, edge);\n\t\t}\n\n\t\tif (isNewEdge) {\n\t\t\tthis._edgeCount++;\n\t\t}\n\t\treturn this;\n\t}\n\n\tremoveNode(id: NodeId): boolean {\n\t\tif (!this.nodes.has(id)) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// Remove all outgoing edges from this node\n\t\tconst outNeighbours = [...(this.adjacency.get(id)?.keys() ?? [])];\n\t\tfor (const neighbour of outNeighbours) {\n\t\t\tthis.removeEdgeInternal(id, neighbour);\n\t\t}\n\n\t\t// For directed graphs, also remove incoming edges to this node\n\t\tif (this.directed && this.reverseAdjacency !== null) {\n\t\t\tconst inNeighbours = [...(this.reverseAdjacency.get(id)?.keys() ?? [])];\n\t\t\tfor (const neighbour of inNeighbours) {\n\t\t\t\t// Remove the edge from neighbour -> id\n\t\t\t\tthis.removeEdgeFromDirected(neighbour, id);\n\t\t\t}\n\t\t}\n\n\t\t// Remove the node itself\n\t\tthis.nodes.delete(id);\n\t\tthis.adjacency.delete(id);\n\t\tthis.reverseAdjacency?.delete(id);\n\n\t\treturn true;\n\t}\n\n\t/**\n\t * Remove an edge from a directed graph, updating both adjacency maps.\n\t * This handles the case where we're removing an edge that points TO the removed node.\n\t */\n\tprivate removeEdgeFromDirected(source: NodeId, target: NodeId): void {\n\t\t// Remove from forward adjacency (source -> target)\n\t\tconst forwardMap = this.adjacency.get(source);\n\t\tif (forwardMap?.delete(target) === true) {\n\t\t\tthis._edgeCount--;\n\t\t}\n\n\t\t// Remove from reverse adjacency\n\t\tthis.reverseAdjacency?.get(target)?.delete(source);\n\t}\n\n\tprivate removeEdgeInternal(source: NodeId, target: NodeId): void {\n\t\t// Remove from forward adjacency\n\t\tconst forwardMap = this.adjacency.get(source);\n\t\tif (forwardMap?.delete(target) === true) {\n\t\t\tthis._edgeCount--;\n\t\t}\n\n\t\tif (this.directed) {\n\t\t\t// Remove from reverse adjacency\n\t\t\tthis.reverseAdjacency?.get(target)?.delete(source);\n\t\t} else {\n\t\t\t// For undirected, remove both directions\n\t\t\tthis.adjacency.get(target)?.delete(source);\n\t\t}\n\t}\n\n\tremoveEdge(source: NodeId, target: NodeId): boolean {\n\t\t// Check if edge exists\n\t\tif (!this.hasEdgeInternal(source, target)) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.removeEdgeInternal(source, target);\n\t\treturn true;\n\t}\n\n\tprivate hasEdgeInternal(source: NodeId, target: NodeId): boolean {\n\t\tconst forward = this.adjacency.get(source)?.has(target) === true;\n\t\tif (forward) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (!this.directed) {\n\t\t\treturn this.adjacency.get(target)?.has(source) === true;\n\t\t}\n\n\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
|