@graphty/cytoscape-extensions 0.0.28 → 0.0.29
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/algorithm-options.d.ts +5 -0
- package/dist/algorithm-options.d.ts.map +1 -0
- package/dist/algorithm-options.js +285 -0
- package/dist/algorithm-options.js.map +1 -0
- package/dist/algorithms.d.ts +489 -0
- package/dist/algorithms.d.ts.map +1 -0
- package/dist/algorithms.js +1184 -0
- package/dist/algorithms.js.map +1 -0
- package/dist/cdn/chunks/elements-DkrLtNP8.js +1 -0
- package/dist/cdn/chunks/gpu-platform-browser-VBah74fl.js +2825 -0
- package/dist/cdn/chunks/index-C4RZkds_.js +10 -0
- package/dist/cdn/chunks/index-CAhmhhmb.js +1 -0
- package/dist/cdn/chunks/io-Da5POp5D.js +268 -0
- package/dist/cdn/chunks/preload-helper-DEpZAvig.js +1 -0
- package/dist/cdn/chunks/samples-BkLzaDJ3.js +1 -0
- package/dist/cdn/chunks/sigv4-EokFic1V-CWJiFeEe.js +3 -0
- package/dist/cdn/chunks/sources-cypher-BGGD4kJ9.js +5 -0
- package/dist/cdn/chunks/sources-ndex-CxnPOYff.js +1 -0
- package/dist/cdn/chunks/sources-rest-BPNV8mX-.js +1 -0
- package/dist/cdn/chunks/sources-sparql-BiqRQuHD.js +5 -0
- package/dist/cdn/cytoscape-extensions.js +1 -0
- package/dist/cdn/io.js +1 -0
- package/dist/cytoscape-extensions.bundle.js +2825 -0
- package/dist/elements.d.ts +69 -0
- package/dist/elements.d.ts.map +1 -0
- package/dist/elements.js +343 -0
- package/dist/elements.js.map +1 -0
- package/dist/gpu-platform-browser.d.ts +11 -0
- package/dist/gpu-platform-browser.d.ts.map +1 -0
- package/dist/gpu-platform-browser.js +22 -0
- package/dist/gpu-platform-browser.js.map +1 -0
- package/dist/gpu-platform-node.d.ts +15 -0
- package/dist/gpu-platform-node.d.ts.map +1 -0
- package/dist/gpu-platform-node.js +27 -0
- package/dist/gpu-platform-node.js.map +1 -0
- package/dist/gpu.d.ts +149 -0
- package/dist/gpu.d.ts.map +1 -0
- package/dist/gpu.js +312 -0
- package/dist/gpu.js.map +1 -0
- package/dist/graph-data.d.ts +101 -0
- package/dist/graph-data.d.ts.map +1 -0
- package/dist/graph-data.js +147 -0
- package/dist/graph-data.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/io.d.ts +111 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +258 -0
- package/dist/io.js.map +1 -0
- package/dist/layouts.d.ts +207 -0
- package/dist/layouts.d.ts.map +1 -0
- package/dist/layouts.js +615 -0
- package/dist/layouts.js.map +1 -0
- package/dist/lazy.d.ts +21 -0
- package/dist/lazy.d.ts.map +1 -0
- package/dist/lazy.js +32 -0
- package/dist/lazy.js.map +1 -0
- package/dist/samples.d.ts +115 -0
- package/dist/samples.d.ts.map +1 -0
- package/dist/samples.js +237 -0
- package/dist/samples.js.map +1 -0
- package/dist/snapshot.d.ts +81 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +225 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/webgpu-provider.d.ts +25 -0
- package/dist/webgpu-provider.d.ts.map +1 -0
- package/dist/webgpu-provider.js +53 -0
- package/dist/webgpu-provider.js.map +1 -0
- package/package.json +13 -9
|
@@ -0,0 +1,2825 @@
|
|
|
1
|
+
import{I as dt,d as _n,u as No,b as Bo,m as oi,q as $o}from"./preload-helper-DEpZAvig.js";import{a as ai}from"./index-CAhmhhmb.js";const Os=256,Ct=65535,Zr=Ct*Os,ye=4294967295,kr=256,Mt=64,Ar=256,Ye=256,Pn=32768,Co=3,Mo=256,Sn=16*1024*1024,Go=4,zo=4*1024,Fo=64*1024*1024,ci=16*1024*1024,li=2,Fe=256,qt=32,He=256,ui=64,At=Object.freeze({maxIter:100,jitterTolerance:1,scalingRatio:2,gravity:1,strongGravity:!1,distributedAction:!1,linlog:!1,dissuadeHubs:!1,dim:2,scale:1,settleThreshold:.001,settleWindow:10,iterationsPerStep:1,maxInFlight:2}),mt=Object.freeze({repulsion:"auto",exactMaxNodes:Pn,nearMax:64,deterministic:!0,gridMax2D:512,gridMax3D:128,extentFactor:6,compat:"paper"}),Wo=.01,Uo=1e-4,Vo=1e-8,En=128,ys=1,In=2,yr=.1,jo=.7,qo=.9,Ko=5,Ho=1e4,Yo=Object.freeze({k:null,iterations:50,fixed:null,dim:2,scale:1,settleThreshold:.001,settleWindow:10,iterationsPerStep:1,maxInFlight:2,cooling:"linear"}),Xo=300,_s=Object.freeze({springLength:10,springCoefficient:.8,gravity:-12,dragCoefficient:.9,timeStep:.5,dim:2,scale:1,settleThreshold:.001,settleWindow:10,iterationsPerStep:1,maxInFlight:2}),kn=Object.freeze({springElectrical:.003,fruchtermanReingold:.002}),Qo=2e3,Zo=2**128-2**104,di=8,fi=4,An=1024,xn=1e-6,Rn=1.01,Jo=24,ue=32,ea=4096,ta=2,ra=24,sa=32,Gt=2139095040,ia=.25,na=256,oa=2,aa=100,ca=256,Sr=64,Ns=32,la=4096,hi=8,It=4,ua=2147483648,da=32,Jr=128,es=65e3,Ln=2,fa=32,ha=Object.freeze({});class h extends Error{constructor(e,t,s){super(t),this.name="WebGpuGraphError",this.code=e,this.details=s===void 0?ha:Object.freeze({...s})}}function Nt(r){if(typeof r!="object"||r===null)return!1;const e=r;return e.name==="WebGpuGraphError"&&typeof e.code=="string"}function Er(r,e){return Nt(r)&&r.code===e}const pi=Object.freeze(["maxBufferSize","maxStorageBufferBindingSize","maxStorageBuffersPerShaderStage","maxComputeWorkgroupStorageSize","maxComputeInvocationsPerWorkgroup","maxComputeWorkgroupSizeX"]),pa=["maxTextureDimension1D","maxTextureDimension2D","maxTextureDimension3D","maxTextureArrayLayers","maxBindGroups","maxBindGroupsPlusVertexBuffers","maxBindingsPerBindGroup","maxDynamicUniformBuffersPerPipelineLayout","maxDynamicStorageBuffersPerPipelineLayout","maxSampledTexturesPerShaderStage","maxSamplersPerShaderStage","maxStorageBuffersPerShaderStage","maxStorageBuffersInVertexStage","maxStorageBuffersInFragmentStage","maxStorageTexturesPerShaderStage","maxStorageTexturesInVertexStage","maxStorageTexturesInFragmentStage","maxUniformBuffersPerShaderStage","maxUniformBufferBindingSize","maxStorageBufferBindingSize","minUniformBufferOffsetAlignment","minStorageBufferOffsetAlignment","maxVertexBuffers","maxBufferSize","maxVertexAttributes","maxVertexBufferArrayStride","maxInterStageShaderVariables","maxColorAttachments","maxColorAttachmentBytesPerSample","maxComputeWorkgroupStorageSize","maxComputeInvocationsPerWorkgroup","maxComputeWorkgroupSizeX","maxComputeWorkgroupSizeY","maxComputeWorkgroupSizeZ","maxComputeWorkgroupsPerDimension","maxImmediateSize"],gi=new WeakSet;function Dn(r){return r instanceof Error?r.message:String(r)}function Tn(r){return r.architecture==="software"||r.architecture==="swiftshader"||r.architecture==="warp"||r.isFallbackAdapter===!0}function Kt(r){const{info:e}=r,t=r.limits,s={};for(const i of pa){const n=t[i];typeof n=="number"&&(s[i]=n)}return Object.freeze({vendor:e.vendor,architecture:e.architecture,device:e.device,description:e.description,software:Tn(e),subgroupMinSize:e.subgroupMinSize??0,subgroupMaxSize:e.subgroupMaxSize??0,features:Object.freeze(Array.from(r.features).sort()),limits:Object.freeze(s)})}async function On(r,e){let t;try{t=await r.requestAdapter({powerPreference:e})}catch(s){const i=`requestAdapter() threw: ${Dn(s)}`;throw new h("E_NO_ADAPTER",i,{reason:i})}if(t===null){const s="requestAdapter() returned null: no usable WebGPU adapter";throw new h("E_NO_ADAPTER",s,{reason:s})}return t}function ga(r,e){const t={};if(e==="default")return t;if(e==="raise"){for(const s of pi)t[s]=r.limits[s];return t}for(const s of pi){const i=e[s];if(i===void 0)continue;const n=r.limits[s];if(i>n)throw new h("E_NO_DEVICE",`requested ${s} = ${i} exceeds the adapter's ${n}`,{reason:"limit",adapter:Kt(r),limit:s,requested:i,available:n});t[s]=i}return t}function ma(r,e,t){const s=[];for(const i of e){if(!r.features.has(i))throw new h("E_NO_DEVICE",`required feature "${i}" is not supported by the adapter`,{reason:"feature",adapter:Kt(r),requested:i});s.includes(i)||s.push(i)}for(const i of t)r.features.has(i)&&!s.includes(i)&&s.push(i);return s}function mi(r,e){return new h("E_NO_DEVICE",`the adapter already created a device (an adapter is consumed by one requestDevice, spec 2.2 step 1): ${e}`,{reason:"consumed",adapter:Kt(r)})}async function wa(r,e){if(gi.has(r))throw mi(r,"recorded by this package");let t;try{t=await r.requestDevice(e)}catch(s){const i=Dn(s);throw/consumed|already been used/i.test(i)?mi(r,i):new h("E_NO_DEVICE",`requestDevice() rejected: ${i}`,{reason:"requestDevice",adapter:Kt(r)})}return gi.add(r),t}function ba(r,e,t,s){const i=new Set(r.features),n=i.has("subgroups");return{limits:r.limits,features:i,wgslFeatures:new Set(s),subgroupMinSize:n?e.subgroupMinSize??0:0,subgroupMaxSize:n?e.subgroupMaxSize??0:0,software:Tn(e),runtime:t,vendor:e.vendor,architecture:e.architecture,device:e.device,description:e.description}}function wi(r,e){const t=new Set(r.features),s=t.has("subgroups");return{limits:r.limits,features:t,wgslFeatures:e?.wgslFeatures??new Set,subgroupMinSize:s?e?.subgroupMinSize??0:0,subgroupMaxSize:s?e?.subgroupMaxSize??0:0,software:e?.software??!1,runtime:e?.runtime??"unknown",vendor:e?.vendor??"",architecture:e?.architecture??"",device:e?.device??"",description:e?.description??""}}function Bs(r){const e=Math.min(Os,r.limits.maxComputeInvocationsPerWorkgroup);let t=1;for(;t*2<=e;)t*=2;return t}function ts(r){const e=r.limits.maxComputeWorkgroupsPerDimension;if(e!==Ct)throw new h("E_NO_DEVICE",`maxComputeWorkgroupsPerDimension is ${e}; the dispatch planner and the WGSL prelude bake ${Ct}`,{reason:"maxComputeWorkgroupsPerDimension",adapter:null,requested:Ct,available:e});const t=Bs(r);if(t<64)throw new h("E_NO_DEVICE",`the device allows a workgroup of ${t} invocations; the kernels need a power of two >= 64`,{reason:"limit",adapter:null,limit:"maxComputeInvocationsPerWorkgroup",requested:64,available:t})}function va(r,e){return r.messages.map(t=>{if(t.lineNum===0)return`${t.type} ${t.message}`;const s=t.lineNum-e;return s>=1?`${t.type} ${s}:${t.linePos} ${t.message}`:`${t.type} prelude:${t.lineNum}:${t.linePos} ${t.message}`})}class ya{constructor(e,t){this.live=new Map,this.residentBytes=0,this.pending=[],this.failure=null,this.serial=0,this.device=e,this.threshold=t??Sn}createBuffer(e){this.serial+=1;const t=e.label===void 0||e.label===""?`buffer-${this.serial}`:e.label,{size:s}=e,i=s>=this.threshold;i&&this.device.pushErrorScope("out-of-memory");const n=this.device.createBuffer({...e,label:t});if(this.live.set(n,s),this.residentBytes+=s,i){const o=this.device.popErrorScope().then(a=>{a!==null&&(this.live.has(n)&&(this.live.delete(n),this.residentBytes-=s),this.failure===null&&(this.failure=new h("E_OUT_OF_MEMORY",`out of memory creating "${t}" (${s} bytes, ${this.residentBytes} resident): ${a.message}`,{requested:s,resident:this.residentBytes,label:t})))},()=>{});this.pending.push(o)}return n}destroy(e){const t=this.live.get(e);t!==void 0&&(this.live.delete(e),this.residentBytes-=t),e.destroy()}async check(){for(;this.pending.length>0;){const e=this.pending.splice(0);await Promise.all(e)}if(this.failure!==null)throw this.failure}reset(){this.failure=null}get resident(){return this.residentBytes}get liveBuffers(){return this.live.size}}class _a{constructor(){this.queue=[]}set(e){this.queue.push(e)}take(){if(this.queue.length===0)return null;const e=this.queue.splice(0);let t=null;for(let s=e.length-1;s>=0;s-=1){const i=e[s];t=t===null?i:new h(i.code,i.message,{...i.details,next:t})}return t}get pending(){return this.queue.length>0}}function Pa(r,e){const{message:t}=r;return r.constructor.name==="GPUOutOfMemoryError"?new h("E_OUT_OF_MEMORY",`uncaptured out-of-memory error on "${e}": ${t}`,{requested:0,resident:0,label:e}):new h("E_VALIDATION",`uncaptured validation error on "${e}": ${t}`,{label:e,message:t})}function Sa(r,e,t){let s=!0;const i=r.label??"",n=o=>{if(!s)return;const a=Pa(o.error,i);t!==null?t(a):e.set(a)};return r.addEventListener("uncapturederror",n),()=>{s=!1,typeof r.removeEventListener=="function"&&r.removeEventListener("uncapturederror",n)}}function Ea(r,e){let t=!1;return r.lost.then(s=>(t||(t=!0,e(s)),s))}function Nn(r){return new h("E_DEVICE_LOST",`device lost (${r.reason}): ${r.message}`,{reason:r.reason,message:r.message})}const rs=16,Ia=32,bi=new WeakMap;let vi=1;function ka(r){let e=bi.get(r);return e===void 0&&(e=vi,vi+=1,bi.set(r,e)),e}function Aa(r,e){return r.offset<e.offset+e.size&&e.offset<r.offset+r.size}class xa{constructor(e,t,s,i,n){this.cache=new Map,this.device=e,this.spec=t,this.pipeline=i,this.layouts=n,this.workgroupSize=s.overrides.WG,this.entryPoint=s.entryPoint;const o=n.map(()=>[]);for(const a of t.bindings)o[a.group].push(a);for(const a of o)a.sort((c,l)=>c.binding-l.binding);this.groups=o,this.dynamicCounts=o.map(a=>a.filter(c=>c.kind==="uniform").length)}bind(e){const{id:t}=this.spec,s=new Map(this.spec.bindings.map(f=>[f.name,f]));for(const f of Object.keys(e))if(!s.has(f))throw new h("E_INVALID_ARGUMENT",`${t}: no binding named "${f}"`,{argument:"bindings",value:f,expected:[...s.keys()].join(", ")});const i=[];for(const f of this.spec.bindings){const p=e[f.name];if(p===void 0)throw new h("E_INVALID_ARGUMENT",`${t}: binding "${f.name}" is missing`,{argument:"bindings",value:f.name,expected:"a Binding for every declared name"});if(!(Number.isInteger(p.size)&&p.size>0))throw new h("E_INVALID_ARGUMENT",`${t}: binding "${f.name}" has size ${p.size}`,{argument:f.name,value:p.size,expected:"size > 0 (a zero-length buffer is never bound)"});if(!(Number.isInteger(p.offset)&&p.offset>=0&&p.offset%Ar===0))throw new h("E_INVALID_ARGUMENT",`${t}: binding "${f.name}" has offset ${p.offset}`,{argument:f.name,value:p.offset,expected:`a multiple of ${Ar} (spec 2.6: the offset alignment the package always honours)`});i.push({decl:f,binding:p})}for(let f=0;f<i.length;f++)for(let p=f+1;p<i.length;p++){const g=i[f],m=i[p];if(!(g.binding.buffer!==m.binding.buffer||!(g.decl.kind==="storage"||m.decl.kind==="storage"))){if(g.decl.kind!==m.decl.kind)throw new h("E_INVALID_ARGUMENT",`${t}: "${g.decl.name}" and "${m.decl.name}" bind one buffer with different access modes (writable usage next to another usage)`,{argument:"aliasing",value:[g.decl.name,m.decl.name],expected:"one access mode per buffer when a slot is read_write",reason:"usage"});if(Aa(g.binding,m.binding))throw new h("E_INVALID_ARGUMENT",`${t}: "${g.decl.name}" and "${m.decl.name}" bind intersecting ranges of one read_write buffer`,{argument:"aliasing",value:[g.decl.name,m.decl.name],expected:"disjoint ranges",reason:"range"})}}const n=i.map(({binding:f})=>`${ka(f.buffer)}:${f.offset}:${f.size}`).join("|"),o=this.cache.get(n);if(o!==void 0)return this.cache.delete(n),this.cache.set(n,o),o;const a=new Map(i.map(({decl:f,binding:p})=>[f.name,p])),c=this.layouts.map((f,p)=>this.device.createBindGroup({label:`${t}/${p}`,layout:f,entries:this.groups[p].map(g=>{const m=a.get(g.name);return{binding:g.binding,resource:{buffer:m.buffer,offset:m.offset,size:m.size}}})})),l=[];this.dynamicCounts.forEach((f,p)=>{f>0&&l.push(p)});const d=Object.freeze({kernel:this,bindGroups:Object.freeze(c),dynamicGroups:Object.freeze(l)});return this.cache.set(n,d),this.cache.size>Ia&&this.cache.delete(this.cache.keys().next().value),d}dispatch(e,t,s,i){this.check(t,i),s.x!==0&&(this.setUp(e,t,i),e.dispatchWorkgroups(s.x,s.y,s.z))}dispatchIndirect(e,t,s,i,n){if(this.check(t,n),!Number.isInteger(i)||i<0||(i+1)*rs>s.size)throw new h("E_INVALID_ARGUMENT",`${this.spec.id}: args slot ${i} is outside the binding`,{argument:"slot",value:i,expected:`0 <= slot < ${Math.floor(s.size/rs)}`});this.setUp(e,t,n),e.dispatchWorkgroupsIndirect(s.buffer,s.offset+rs*i)}check(e,t){const{id:s}=this.spec;if(e.kernel!==this)throw new h("E_INVALID_ARGUMENT",`${s}: the bound kernel belongs to "${e.kernel.spec.id}"`,{argument:"bound",value:e.kernel.spec.id,expected:s});if(t!==void 0&&t.length!==e.dynamicGroups.length)throw new h("E_INVALID_ARGUMENT",`${s}: ${t.length} dynamic offsets for ${e.dynamicGroups.length} dynamic groups`,{argument:"dynamicOffsets",value:t.length,expected:e.dynamicGroups.length})}setUp(e,t,s){e.setPipeline(this.pipeline);let i=0;t.bindGroups.forEach((n,o)=>{const a=this.dynamicCounts[o];if(a>0){const c=s?.[i]??0;i+=1;const l=[];for(let d=0;d<a;d++)l.push(c);e.setBindGroup(o,n,l)}else e.setBindGroup(o,n)})}invalidate(){this.cache.clear()}}const ie=Object.freeze({MAP_READ:1,MAP_WRITE:2,COPY_SRC:4,COPY_DST:8,INDEX:16,VERTEX:32,UNIFORM:64,STORAGE:128,INDIRECT:256,QUERY_RESOLVE:512}),yi=Object.freeze({READ:1,WRITE:2}),_i=Object.freeze({VERTEX:1,FRAGMENT:2,COMPUTE:4});function kt(r){if(!Number.isFinite(r))throw new h("E_INVALID_ARGUMENT",`wgslF32Literal: ${r} is not a finite number`,{argument:"value",value:r,expected:"a finite number"});const e=String(r);return/[.e]/.test(e)?e:`${e}.0`}const Bn=`// ---- prelude: constants, standard overrides, helpers (every module receives this text first)
|
|
2
|
+
const INVALID_INDEX: u32 = ${dt}u;
|
|
3
|
+
const U32_MAX: u32 = ${ye}u;
|
|
4
|
+
const F32_INF_BITS: u32 = ${Gt}u;
|
|
5
|
+
const F32_SIGN_BIT: u32 = ${ua}u;
|
|
6
|
+
const MAX_WORKGROUPS_PER_DIM: u32 = ${Ct}u;
|
|
7
|
+
const EXACT_TILES_PER_PASS: u32 = ${En}u;
|
|
8
|
+
const FA2_DIST_FLOOR: f32 = ${kt(Wo)};
|
|
9
|
+
const FA2_DIST_FLOOR_SQ: f32 = ${kt(Uo)};
|
|
10
|
+
const FA2_COINCIDENT_SQ: f32 = ${kt(Vo)};
|
|
11
|
+
const FA2_FLAG_FIRST: u32 = ${ys}u;
|
|
12
|
+
const FA2_FLAG_ADAPTIVE: u32 = ${In}u;
|
|
13
|
+
const FR_COOLING_STEP: f32 = ${kt(qo)};
|
|
14
|
+
const FR_COOLING_PATIENCE: u32 = ${Ko}u;
|
|
15
|
+
const GRID_HUB_CELL: u32 = ${An}u;
|
|
16
|
+
const GRID_EXTENT_FLOOR: f32 = ${kt(xn)};
|
|
17
|
+
const GRID_BBOX_MARGIN: f32 = ${kt(Rn)};
|
|
18
|
+
const RADIX_BINS: u32 = ${kr}u;
|
|
19
|
+
const RADIX_DIGIT_MASK: u32 = ${kr-1}u;
|
|
20
|
+
const APSP_TILE: u32 = ${Ns}u;
|
|
21
|
+
const GROUP_HASH_LOAD_FACTOR: u32 = ${Ln}u;
|
|
22
|
+
const TRIANGLE_BINARY_SEARCH_RATIO: u32 = ${fa}u;
|
|
23
|
+
const BC_SIGMA_EXPONENT_CAP: i32 = ${aa}i;
|
|
24
|
+
const F32_MAX: f32 = 0x1.fffffep+127;
|
|
25
|
+
override WG: u32 = ${Os}u;
|
|
26
|
+
override USE_PERM: bool = false;
|
|
27
|
+
override HAS_WEIGHTS: bool = false;
|
|
28
|
+
override SUBGROUP_MIN: u32 = 4u;
|
|
29
|
+
override SUBGROUP_MAX: u32 = 0u;
|
|
30
|
+
|
|
31
|
+
fn linear_id(wid: vec3<u32>, lid: u32) -> u32 { return (wid.x + wid.y * MAX_WORKGROUPS_PER_DIM) * WG + lid; }
|
|
32
|
+
fn group_id(wid: vec3<u32>) -> u32 { return wid.x + wid.y * MAX_WORKGROUPS_PER_DIM; }
|
|
33
|
+
// betweenness: the power of two the counts of depth L + 1 are divided by, from the f32 bits of depth L's largest count
|
|
34
|
+
fn sigma_shift(maxBits: u32) -> i32 { return max(0i, i32((maxBits >> 23u) & 0xffu) - 127i - BC_SIGMA_EXPONENT_CAP); }
|
|
35
|
+
fn lowbias32(x0: u32) -> u32 {
|
|
36
|
+
var x = x0;
|
|
37
|
+
x = x ^ (x >> 16u);
|
|
38
|
+
x = x * 0x7feb352du;
|
|
39
|
+
x = x ^ (x >> 15u);
|
|
40
|
+
x = x * 0x846ca68bu;
|
|
41
|
+
x = x ^ (x >> 16u);
|
|
42
|
+
return x;
|
|
43
|
+
}
|
|
44
|
+
fn mask_bit(w: u32, i: u32) -> bool { return ((w >> (i & 31u)) & 1u) == 1u; }
|
|
45
|
+
fn unpack_u8(w: u32, i: u32) -> u32 { return (w >> (8u * (i & 3u))) & 0xFFu; }
|
|
46
|
+
fn pair_hash(i: u32, j: u32) -> u32 { return lowbias32((min(i, j) * 0x9E3779B9u) ^ max(i, j)); }
|
|
47
|
+
fn order_key(w: f32) -> u32 {
|
|
48
|
+
let raw = bitcast<u32>(w);
|
|
49
|
+
let bits = select(raw, 0u, raw == F32_SIGN_BIT);
|
|
50
|
+
return select(bits | F32_SIGN_BIT, ~bits, (bits & F32_SIGN_BIT) != 0u);
|
|
51
|
+
}
|
|
52
|
+
fn hash_unit(h: u32) -> f32 { return f32(h >> 8u) * (1.0 / 16777216.0); }
|
|
53
|
+
fn hash_dir(h: u32, dim: u32) -> vec3f {
|
|
54
|
+
let phi = 6.283185307179586 * hash_unit(h);
|
|
55
|
+
if (dim == 2u) { return vec3f(cos(phi), sin(phi), 0.0); }
|
|
56
|
+
let z = 2.0 * hash_unit(lowbias32(h ^ 0x5bd1e995u)) - 1.0;
|
|
57
|
+
let r = sqrt(max(0.0, 1.0 - z * z));
|
|
58
|
+
return vec3f(r * cos(phi), r * sin(phi), z);
|
|
59
|
+
}
|
|
60
|
+
fn kick_dir(i: u32, j: u32, dim: u32) -> vec3f {
|
|
61
|
+
let d = hash_dir(pair_hash(i, j), dim);
|
|
62
|
+
return select(d, -d, i > j);
|
|
63
|
+
}`;Bn.split(`
|
|
64
|
+
`).length;const Ra=`var<workgroup> wg_scratch_v: array<vec4f, WG>;
|
|
65
|
+
var<workgroup> wg_scratch_u: array<u32, WG>;
|
|
66
|
+
fn combine_v(a: vec4f, b: vec4f, op: u32) -> vec4f {
|
|
67
|
+
if (op == 1u) { return min(a, b); }
|
|
68
|
+
if (op == 2u) { return max(a, b); }
|
|
69
|
+
return a + b;
|
|
70
|
+
}
|
|
71
|
+
fn combine_u(a: u32, b: u32, op: u32) -> u32 {
|
|
72
|
+
if (op == 1u) { return min(a, b); }
|
|
73
|
+
if (op == 2u) { return max(a, b); }
|
|
74
|
+
return a + b;
|
|
75
|
+
}
|
|
76
|
+
fn wg_reduce_vec4(v: vec4f, lid: u32, op: u32) -> vec4f {
|
|
77
|
+
workgroupBarrier();
|
|
78
|
+
wg_scratch_v[lid] = v;
|
|
79
|
+
workgroupBarrier();
|
|
80
|
+
for (var s = WG / 2u; s > 0u; s = s >> 1u) {
|
|
81
|
+
if (lid < s) { wg_scratch_v[lid] = combine_v(wg_scratch_v[lid], wg_scratch_v[lid + s], op); }
|
|
82
|
+
workgroupBarrier();
|
|
83
|
+
}
|
|
84
|
+
let total = wg_scratch_v[0];
|
|
85
|
+
workgroupBarrier();
|
|
86
|
+
return total;
|
|
87
|
+
}
|
|
88
|
+
fn wg_reduce_u32(v: u32, lid: u32, op: u32) -> u32 {
|
|
89
|
+
workgroupBarrier();
|
|
90
|
+
wg_scratch_u[lid] = v;
|
|
91
|
+
workgroupBarrier();
|
|
92
|
+
for (var s = WG / 2u; s > 0u; s = s >> 1u) {
|
|
93
|
+
if (lid < s) { wg_scratch_u[lid] = combine_u(wg_scratch_u[lid], wg_scratch_u[lid + s], op); }
|
|
94
|
+
workgroupBarrier();
|
|
95
|
+
}
|
|
96
|
+
let total = wg_scratch_u[0];
|
|
97
|
+
workgroupBarrier();
|
|
98
|
+
return total;
|
|
99
|
+
}
|
|
100
|
+
fn wg_scan_u32(v: u32, lid: u32) -> u32 {
|
|
101
|
+
workgroupBarrier();
|
|
102
|
+
wg_scratch_u[lid] = v;
|
|
103
|
+
workgroupBarrier();
|
|
104
|
+
for (var s = 1u; s < WG; s = s * 2u) { // Hillis-Steele inclusive scan (scan-block's body); uniform: every lane runs every round
|
|
105
|
+
var t = 0u;
|
|
106
|
+
if (lid >= s) { t = wg_scratch_u[lid - s]; }
|
|
107
|
+
workgroupBarrier();
|
|
108
|
+
wg_scratch_u[lid] = wg_scratch_u[lid] + t;
|
|
109
|
+
workgroupBarrier();
|
|
110
|
+
}
|
|
111
|
+
let inclusive = wg_scratch_u[lid];
|
|
112
|
+
workgroupBarrier(); // the scratch is free for the next call
|
|
113
|
+
return inclusive;
|
|
114
|
+
}
|
|
115
|
+
fn wg_reduce_f32(v: f32, lid: u32, op: u32) -> f32 { return wg_reduce_vec4(vec4f(v, 0.0, 0.0, 0.0), lid, op).x; }`,La=`override SG_SLOTS: u32 = (WG + SUBGROUP_MIN - 1u) / SUBGROUP_MIN; // one slot per subgroup; the count is largest when the compiler picks the SMALLEST size
|
|
116
|
+
var<workgroup> sg_counter: atomic<u32>;
|
|
117
|
+
var<workgroup> sg_val_v: array<vec4f, SG_SLOTS>;
|
|
118
|
+
var<workgroup> sg_val_u: array<u32, SG_SLOTS>;
|
|
119
|
+
var<workgroup> sg_key: array<u32, SG_SLOTS>;
|
|
120
|
+
var<workgroup> sg_sorted_v: array<vec4f, SG_SLOTS>;
|
|
121
|
+
var<workgroup> sg_sorted_u: array<u32, SG_SLOTS>;
|
|
122
|
+
fn combine_v(a: vec4f, b: vec4f, op: u32) -> vec4f {
|
|
123
|
+
if (op == 1u) { return min(a, b); }
|
|
124
|
+
if (op == 2u) { return max(a, b); }
|
|
125
|
+
return a + b;
|
|
126
|
+
}
|
|
127
|
+
fn combine_u(a: u32, b: u32, op: u32) -> u32 {
|
|
128
|
+
if (op == 1u) { return min(a, b); }
|
|
129
|
+
if (op == 2u) { return max(a, b); }
|
|
130
|
+
return a + b;
|
|
131
|
+
}
|
|
132
|
+
fn wg_reduce_vec4(v: vec4f, lid: u32, op: u32) -> vec4f {
|
|
133
|
+
workgroupBarrier();
|
|
134
|
+
if (lid == 0u) { atomicStore(&sg_counter, 0u); }
|
|
135
|
+
workgroupBarrier();
|
|
136
|
+
let s_add = subgroupAdd(v);
|
|
137
|
+
let s_min = subgroupMin(v);
|
|
138
|
+
let s_max = subgroupMax(v);
|
|
139
|
+
var partial = s_add;
|
|
140
|
+
if (op == 1u) { partial = s_min; }
|
|
141
|
+
if (op == 2u) { partial = s_max; }
|
|
142
|
+
let key = subgroupMin(lid); // the smallest local id of this subgroup: a stable identity without @builtin(subgroup_id)
|
|
143
|
+
var slot = 0u;
|
|
144
|
+
if (subgroupElect()) { slot = atomicAdd(&sg_counter, 1u); } // D16: the elected lane takes a slot from the counter
|
|
145
|
+
slot = subgroupBroadcast(slot, 0u); // and broadcasts it (lane 0 is the elected lane in uniform control flow)
|
|
146
|
+
if (subgroupElect()) { sg_val_v[slot] = partial; sg_key[slot] = key; }
|
|
147
|
+
workgroupBarrier();
|
|
148
|
+
let count = atomicLoad(&sg_counter);
|
|
149
|
+
if (lid < count) { // rank the slots by key so the final sum has a fixed order (11.9 item 4)
|
|
150
|
+
let mine = sg_key[lid];
|
|
151
|
+
var rank = 0u;
|
|
152
|
+
for (var k = 0u; k < count; k = k + 1u) { if (sg_key[k] < mine) { rank = rank + 1u; } }
|
|
153
|
+
sg_sorted_v[rank] = sg_val_v[lid];
|
|
154
|
+
}
|
|
155
|
+
workgroupBarrier();
|
|
156
|
+
var total = sg_sorted_v[0];
|
|
157
|
+
for (var k = 1u; k < count; k = k + 1u) { total = combine_v(total, sg_sorted_v[k], op); }
|
|
158
|
+
workgroupBarrier();
|
|
159
|
+
return total;
|
|
160
|
+
}
|
|
161
|
+
fn wg_reduce_u32(v: u32, lid: u32, op: u32) -> u32 {
|
|
162
|
+
workgroupBarrier();
|
|
163
|
+
if (lid == 0u) { atomicStore(&sg_counter, 0u); }
|
|
164
|
+
workgroupBarrier();
|
|
165
|
+
let s_add = subgroupAdd(v);
|
|
166
|
+
let s_min = subgroupMin(v);
|
|
167
|
+
let s_max = subgroupMax(v);
|
|
168
|
+
var partial = s_add;
|
|
169
|
+
if (op == 1u) { partial = s_min; }
|
|
170
|
+
if (op == 2u) { partial = s_max; }
|
|
171
|
+
let key = subgroupMin(lid);
|
|
172
|
+
var slot = 0u;
|
|
173
|
+
if (subgroupElect()) { slot = atomicAdd(&sg_counter, 1u); }
|
|
174
|
+
slot = subgroupBroadcast(slot, 0u);
|
|
175
|
+
if (subgroupElect()) { sg_val_u[slot] = partial; sg_key[slot] = key; }
|
|
176
|
+
workgroupBarrier();
|
|
177
|
+
let count = atomicLoad(&sg_counter);
|
|
178
|
+
if (lid < count) {
|
|
179
|
+
let mine = sg_key[lid];
|
|
180
|
+
var rank = 0u;
|
|
181
|
+
for (var k = 0u; k < count; k = k + 1u) { if (sg_key[k] < mine) { rank = rank + 1u; } }
|
|
182
|
+
sg_sorted_u[rank] = sg_val_u[lid];
|
|
183
|
+
}
|
|
184
|
+
workgroupBarrier();
|
|
185
|
+
var total = sg_sorted_u[0];
|
|
186
|
+
for (var k = 1u; k < count; k = k + 1u) { total = combine_u(total, sg_sorted_u[k], op); }
|
|
187
|
+
workgroupBarrier();
|
|
188
|
+
return total;
|
|
189
|
+
}
|
|
190
|
+
fn wg_scan_u32(v: u32, lid: u32) -> u32 {
|
|
191
|
+
workgroupBarrier();
|
|
192
|
+
if (lid == 0u) { atomicStore(&sg_counter, 0u); }
|
|
193
|
+
workgroupBarrier();
|
|
194
|
+
let inSub = subgroupExclusiveAdd(v) + v; // the lane's inclusive sum within its subgroup
|
|
195
|
+
let total = subgroupAdd(v);
|
|
196
|
+
let key = subgroupMin(lid); // the subgroup's identity: its smallest local id (D16)
|
|
197
|
+
var slot = 0u;
|
|
198
|
+
if (subgroupElect()) { slot = atomicAdd(&sg_counter, 1u); }
|
|
199
|
+
slot = subgroupBroadcast(slot, 0u);
|
|
200
|
+
if (subgroupElect()) { sg_val_u[slot] = total; sg_key[slot] = key; }
|
|
201
|
+
workgroupBarrier();
|
|
202
|
+
let count = atomicLoad(&sg_counter);
|
|
203
|
+
var carry = 0u; // the totals of every subgroup whose key is below this lane's: LANE order, whatever order the slots were taken in
|
|
204
|
+
for (var k = 0u; k < count; k = k + 1u) { if (sg_key[k] < key) { carry = carry + sg_val_u[k]; } }
|
|
205
|
+
workgroupBarrier(); // the slots are free for the next call
|
|
206
|
+
return inSub + carry;
|
|
207
|
+
}
|
|
208
|
+
fn wg_reduce_f32(v: f32, lid: u32, op: u32) -> f32 { return wg_reduce_vec4(vec4f(v, 0.0, 0.0, 0.0), lid, op).x; }`,Da=["wg_reduce_f32","wg_reduce_u32","wg_reduce_vec4","wg_scan_u32"],$n=Object.freeze(`NULL Self abstract active alignas alignof as asm asm_fragment async attribute auto await become cast catch class
|
|
209
|
+
co_await co_return co_yield coherent column_major common compile compile_fragment concept const_cast consteval
|
|
210
|
+
constexpr constinit crate debugger decltype delete demote demote_to_helper do dynamic_cast enum explicit export
|
|
211
|
+
extends extern external fallthrough filter final finally friend from fxgroup get goto groupshared highp impl
|
|
212
|
+
implements import inline instanceof interface layout lowp macro macro_rules match mediump meta mod module move
|
|
213
|
+
mut mutable namespace new nil noexcept noinline nointerpolation non_coherent noncoherent noperspective null
|
|
214
|
+
nullptr of operator package packoffset partition pass patch pixelfragment precise precision premerge priv
|
|
215
|
+
protected pub public readonly ref regardless register reinterpret_cast require resource restrict self set shared
|
|
216
|
+
sizeof smooth snorm static static_assert static_cast std subroutine super target template this thread_local
|
|
217
|
+
throw trait try type typedef typeid typename typeof union unless unorm unsafe unsized use using varying virtual
|
|
218
|
+
volatile wgsl where with writeonly yield`.trim().split(/\s+/)),Ta=["WG","USE_PERM","HAS_WEIGHTS","SUBGROUP_MIN","SUBGROUP_MAX"],Oa=Object.freeze({WG:"u32",USE_PERM:"bool",HAS_WEIGHTS:"bool",SUBGROUP_MIN:"u32",SUBGROUP_MAX:"u32"}),Na=Object.freeze(["subgroups"]),Ba=4,$a=new Set(["u32","i32","f32","f16","bool","vec2f","vec3f","vec4f","vec2u","vec3u","vec4u","vec2i","vec3i","vec4i","vec2h","vec3h","vec4h"]),Ca=/^(vec[234]|mat[234]x[234]|atomic|array)</,Pi=/^[A-Za-z_]\w*$/,Ma=Object.freeze({storage:"var<storage, read_write>","storage-ro":"var<storage, read>",uniform:"var<uniform>"}),Ps=new Set($n),Ga=/\/\/@@(\w+)@@/,za=new RegExp("(?<!\\w)[A-Za-z_]\\w*","g"),Fa=/@compute\b/g,Wa=/\bfn\s+([A-Za-z_]\w*)/;function Be(r,e,t){return new h("E_SHADER_COMPILE",`${r}: ${t}`,{id:r,stage:"compose",slot:e})}function Cn(r){return r.replace(/\/\*[\s\S]*?\*\//g," ").replace(/\/\/[^\n]*/g,"")}function Ua(r){const e=r.trim(),t=e.startsWith("array<")?e.slice(6).replace(/\s*(,[^>]*)?>\s*$/,""):e;return $a.has(t)||Ca.test(t)?null:t}function Si(r,e,t,s){if(!(t==="bool"?typeof s=="boolean":typeof s=="number"&&Number.isFinite(s)&&(t==="f32"||Number.isInteger(s)&&s>=0)))throw Be(r,`override:${e}`,`override ${e} (${t}) cannot take the value ${String(s)}`)}function Va(r){if(r.type==="bool")return r.default?"true":"false";const e=r.default;return r.type==="u32"?`${e}u`:kt(e)}function ja(r){const e=Ma[r.kind];return`@group(${r.group}) @binding(${r.binding}) ${e} ${r.name}: ${r.wgslType};`}function qa(r){const e=Cn(r),t=[...e.matchAll(Fa)];if(t.length!==1||t[0].index===void 0)return null;const s=Wa.exec(e.slice(t[0].index));return s===null?null:s[1]}function Ka(r){const e=r.bindings.reduce((i,n)=>Math.max(i,n.group),-1),t=new Map(r.uniforms.map(i=>[i.name,i])),s=[];for(let i=0;i<=e;i++){const n=r.bindings.filter(o=>o.group===i).sort((o,a)=>o.binding-a.binding).map(o=>{if(o.kind==="uniform"){const a=t.get(o.wgslType),c={type:"uniform",hasDynamicOffset:!0};return a!==void 0&&(c.minBindingSize=a.byteLength),{binding:o.binding,visibility:_i.COMPUTE,buffer:c}}return{binding:o.binding,visibility:_i.COMPUTE,buffer:{type:o.kind==="storage"?"storage":"read-only-storage"}}});s.push({entries:n})}return s}function Ha(r){const{id:e}=r;for(const o of r.needs)if(!Na.includes(o))throw Be(e,`needs:${o}`,`unknown feature "${o}" in needs`);const t=new Map;for(const o of r.overrideDecls){if(!Pi.test(o.name)||Ps.has(o.name))throw Be(e,`override:${o.name}`,`override name "${o.name}" is not a usable identifier`);if(Ta.includes(o.name)||t.has(o.name))throw Be(e,`override:${o.name}`,`override "${o.name}" is declared twice (the prelude declares the standard ones)`);Si(e,o.name,o.type,o.default),t.set(o.name,o.type)}for(const[o,a]of Object.entries(r.overrides)){const l=Oa[o]??t.get(o);if(l===void 0)throw Be(e,`override:${o}`,`override "${o}" is neither a standard override nor declared in overrideDecls`);Si(e,o,l,a)}const s=new Set,i=new Set,n=new Set(r.uniforms.map(o=>o.name));for(const o of r.bindings){if(!Pi.test(o.name)||Ps.has(o.name))throw Be(e,`binding:${o.name}`,`binding name "${o.name}" is not a usable identifier`);const a=`${o.group}.${o.binding}`;if(s.has(a))throw Be(e,`binding:${o.name}`,`two bindings share group ${o.group} binding ${o.binding}`);if(i.has(o.name))throw Be(e,`binding:${o.name}`,`two bindings share the name "${o.name}"`);s.add(a),i.add(o.name);const c=Ua(o.wgslType);if(c!==null&&!n.has(c))throw Be(e,`type:${c}`,`binding "${o.name}" names struct "${c}" but no block in spec.uniforms declares it`)}}function Ya(r){const{id:e}=r;let{body:t}=r;for(const[i,n]of Object.entries(r.snippets??{})){const o=`//@@${i}@@`;if(!t.includes(o))throw Be(e,`snippet:${i}`,`snippet "${i}" has no ${o} marker in the body`);t=t.split(o).join(n)}const s=Ga.exec(t);if(s!==null)throw Be(e,`marker:${s[1]}`,`marker //@@${s[1]}@@ is not filled by any snippet`);return t}function Xa(r,e){const{id:t}=r;for(const n of["@group(","override "])if(e.includes(n))throw Be(t,`body:${n}`,`a body must not contain "${n}" (the composer emits those lines)`);const s=Cn(e),i=Da;for(const n of s.matchAll(za)){const o=n[0];if(Ps.has(o))throw Be(t,`reserved:${o}`,`"${o}" is a WGSL reserved word (spec 16.2) and cannot be an identifier`);if(i.includes(o)&&!r.needs.includes("subgroups"))throw Be(t,`helper:${o}`,`the body calls ${o} but spec.needs does not list "subgroups"`)}}function Qa(r,e){Ha(r);const t=qa(r.body);if(t===null)throw Be(r.id,"entry","a body must carry exactly one @compute attribute followed by `fn <name>`");const s=Ya(r);Xa(r,s);const i=r.needs.includes("subgroups"),n=i&&e.features.has("subgroups"),o={...r.overrides};o.WG===void 0&&(o.WG=Bs(e)),n&&(o.SUBGROUP_MAX=e.subgroupMaxSize,o.SUBGROUP_MIN=Math.max(Ba,e.subgroupMinSize));const a=[];n&&a.push("enable subgroups;"),a.push(Bn);for(const d of r.overrideDecls)a.push(`override ${d.name}: ${d.type} = ${Va(d)};`);for(const d of r.uniforms)a.push(d.wgsl);i&&a.push(n?La:Ra);for(const d of r.bindings)a.push(ja(d));const c=a.join(`
|
|
219
|
+
`),l=`${c}
|
|
220
|
+
${s}`;return Object.freeze({id:r.id,code:l,bodyLine:c.split(`
|
|
221
|
+
`).length+1,overrides:Object.freeze(o),constants:Object.freeze(Za(l,o)),entryPoint:t,subgroups:n})}function Za(r,e){const t=r.replace(/\/\*[\s\S]*?\*\//g," ").replace(/\/\/[^\n]*/g," ").replace(/^\s*override\s+\w+\s*:[^\n]*$/gm," "),s={};for(const[i,n]of Object.entries(e))new RegExp(String.raw`\b${i}\b`).test(t)&&(s[i]=n);return s}function xr(r){if(Array.isArray(r))return`[${r.map(e=>xr(e)).join(",")}]`;if(typeof r=="object"&&r!==null){const e=r;return`{${Object.keys(e).sort().map(i=>`${JSON.stringify(i)}:${xr(e[i])}`).join(",")}}`}return JSON.stringify(r)}function Ja(r){if(r===void 0||Object.keys(r).length===0)return"0";const e=xr(r);let t=2166136261;for(let s=0;s<e.length;s++)t^=e.charCodeAt(s),t=Math.imul(t,16777619)>>>0;return t.toString(16).padStart(8,"0")}function ec(r,e){const t=r.needs.filter(s=>e.features.has(s)).join(",");return`${r.id}|${xr(r.overrides)}|${t}|${Ja(r.snippets)}`}function tc(r){return r instanceof Error?r.message:String(r)}class rc{constructor(e,t){this.pending=new Map,this.entries=new Map,this.order=[],this.disposed=!1,this.device=e,this.caps=t}key(e){return ec(e,this.caps)}async get(e){return(await this.entry(e)).pipeline}async kernel(e){return(await this.entry(e)).kernel}async warm(e){await Promise.all(e.map(t=>this.entry(t)))}get size(){return this.entries.size}keys(){return this.order.filter(e=>this.entries.has(e))}layoutsOf(e){return this.entries.get(e)?.layouts??null}dispose(){this.disposed=!0,this.pending.clear(),this.entries.clear(),this.order.length=0}entry(e){if(this.disposed)return Promise.reject(new h("E_DISPOSED","PipelineCache: disposed",{label:"PipelineCache"}));const t=this.key(e),s=this.pending.get(t);if(s!==void 0)return s;this.order.push(t);const i=this.compile(e,t).then(n=>(this.disposed||this.entries.set(t,n),n),n=>{this.pending.delete(t);const o=this.order.indexOf(t);throw o>=0&&this.order.splice(o,1),n});return this.pending.set(t,i),i}async compile(e,t){const s=Qa(e,this.caps),{device:i}=this,n=`${e.id}|pipeline`;i.pushErrorScope("validation");const o=i.createShaderModule({code:s.code,label:e.id}),a=Ka(e).map((w,v)=>i.createBindGroupLayout({...w,label:`${e.id}/layout${v}`})),c=i.createPipelineLayout({bindGroupLayouts:a,label:`${e.id}|layout`}),l=i.popErrorScope(),d=await o.getCompilationInfo(),f=await l,p=d.messages.filter(w=>w.type==="error");if(p.length>0){const w=s.bodyLine-1;throw new h("E_SHADER_COMPILE",`${e.id}: WGSL compilation failed (${p.length} error(s))`,{id:e.id,stage:"compile",messages:va(d,w),lines:p.map(v=>v.lineNum-w)})}if(f!==null)throw new h("E_VALIDATION",`${e.id}: ${f.message}`,{label:n,message:f.message});const g={};for(const[w,v]of Object.entries(s.constants))typeof v=="boolean"?g[w]=v?1:0:g[w]=v;let m;try{m=await i.createComputePipelineAsync({label:t,layout:c,compute:{module:o,entryPoint:s.entryPoint,constants:g}})}catch(w){const v=tc(w);throw new h("E_VALIDATION",`${e.id}: pipeline creation failed: ${v}`,{label:n,message:v})}const b=new xa(i,e,s,m,a);return{key:t,composed:s,pipeline:m,layouts:a,kernel:b}}}class sc{constructor(e,t,s,i){this.labels=new WeakMap,this.partialRequests=new WeakSet,this.pending=[],this.cursor=0,this.refused=0,this.destroyed=!1;const n=i??Mo;this.quantised=s,this.slots=n;const o=t&&n>=2&&e.features.has("timestamp-query");this.enabled=o,o?(this.querySet=e.createQuerySet({label:"profiler/timestamps",type:"timestamp",count:n}),this.resolveBuffer=e.createBuffer({label:"profiler/resolve",size:n*8,usage:ie.QUERY_RESOLVE|ie.COPY_SRC})):(this.querySet=null,this.resolveBuffer=null)}beginPass(e){if(this.querySet===null||this.destroyed)return;if(this.cursor+2>this.slots){this.refused++;return}const t=this.cursor;return this.cursor+=2,this.pending.push({label:e,begin:t}),{querySet:this.querySet,beginningOfPassWriteIndex:t,endOfPassWriteIndex:t+1}}resolveInto(e){if(this.querySet===null||this.resolveBuffer===null||this.destroyed||this.pending.length===0)return null;const t=this.cursor;e.resolveQuerySet(this.querySet,0,t,this.resolveBuffer);const s=e.readback(this.resolveBuffer,0,t*8);return this.labels.set(s,this.pending.map(i=>i.label)),this.refused>0&&this.partialRequests.add(s),this.pending=[],this.cursor=0,this.refused=0,s}partial(e){return this.partialRequests.has(e)}timings(e,t){const s=this.labels.get(t);if(s===void 0)return[];const i=new DataView(e),n=[];for(let o=0;o<s.length;o++){const a=t.offset+o*16;if(a+16>e.byteLength)break;const c=i.getBigUint64(a,!0),l=i.getBigUint64(a+8,!0);n.push({label:s[o],ns:Number(l-c)})}return n}destroy(){this.destroyed||(this.destroyed=!0,this.pending=[],this.cursor=0,this.refused=0,this.querySet!==null&&this.querySet.destroy(),this.resolveBuffer!==null&&this.resolveBuffer.destroy())}}class ic{constructor(e){this.buffers=new Set,this.released=!1,this.pool=e}storage(e,t){return this.acquire(e,ie.STORAGE|ie.COPY_SRC|ie.COPY_DST,t)}uniform(e,t){return this.acquire(e,ie.UNIFORM|ie.COPY_DST,t)}acquire(e,t,s){if(this.released)throw new h("E_DISPOSED",`lease already released: cannot acquire "${s}"`,{label:s});const i=this.pool.acquire(e,t,s);return this.buffers.add(i),i}release(){if(this.released)return;this.released=!0;const e=[...this.buffers];this.buffers.clear();for(const t of e)try{this.pool.release(t)}catch(s){if(!Er(s,"E_DISPOSED"))throw s}}get count(){return this.buffers.size}}function Ei(r,e){return`${r}:${e}`}class $s{constructor(e,t,s,i){this.idle=new Map,this.live=new Map,this.liveBytesValue=0,this.idleBytesValue=0,this.disposed=!1,this.allocator=t,this.maxBufferSize=s-s%4,this.maxIdlePerClass=i?.maxIdlePerClass??Go}static sizeClass(e){if(!Number.isInteger(e)||e<=0)throw new h("E_INVALID_ARGUMENT","byteLength: expected a positive integer",{argument:"byteLength",value:e,expected:"a positive integer"});if(e>Fo)return Math.ceil(e/ci)*ci;let t=zo;for(;t<e;)t*=2;return t}acquire(e,t,s){if(this.disposed)throw new h("E_DISPOSED","the buffer pool was disposed",{label:"pool"});const i=$s.sizeClass(e);if(e>this.maxBufferSize)throw new h("E_TOO_LARGE",`pool: ${e} bytes exceed the device maxBufferSize of ${this.maxBufferSize} bytes`,{needed:e,limit:this.maxBufferSize,path:"pool",algorithm:null});const n=Math.min(i,this.maxBufferSize),o=Ei(n,t);let c=this.idle.get(o)?.pop();return c===void 0?c=this.allocator.createBuffer({label:s,size:n,usage:t}):(this.idleBytesValue-=n,c.label=s),this.live.set(c,{sizeClass:n,usage:t}),this.liveBytesValue+=n,c}release(e){if(this.disposed)return;const t=this.live.get(e);if(t===void 0)throw new h("E_INVALID_ARGUMENT","buffer: not a live buffer of this pool",{argument:"buffer",value:e.label,expected:"a buffer acquired from this pool and not yet released"});this.live.delete(e),this.liveBytesValue-=t.sizeClass;const s=Ei(t.sizeClass,t.usage);let i=this.idle.get(s);if(i===void 0&&(i=[],this.idle.set(s,i)),i.length>=this.maxIdlePerClass){this.allocator.destroy(e);return}i.push(e),this.idleBytesValue+=t.sizeClass}trim(){for(const e of this.idle.values())for(const t of e)this.allocator.destroy(t);this.idle.clear(),this.idleBytesValue=0}destroyAll(){this.trim();for(const e of this.live.keys())this.allocator.destroy(e);this.live.clear(),this.liveBytesValue=0,this.disposed=!0}lease(){return new ic(this)}get liveBytes(){return this.liveBytesValue}get idleBytes(){return this.idleBytesValue}}const nc=4*1024*1024,oc=ie.MAP_READ|ie.COPY_DST,Ii=256;function ac(r){return Object.prototype.toString.call(r)==="[object SharedArrayBuffer]"}function Xe(r,e,t){return new h("E_INVALID_ARGUMENT",`${r}: expected ${t}`,{argument:r,value:e,expected:t})}class cc{constructor(e,t,s){this.ring=[],this.disposed=!1;const i=s?.slotBytes??nc;if(!Number.isInteger(i)||i<=0||i%4!==0)throw Xe("slotBytes",i,"a positive multiple of 4");const n=s?.slots??Co;if(!Number.isInteger(n)||n<0)throw Xe("slots",n,"a non-negative integer");this.device=e,this.allocator=t,this.slotBytes=i;for(let o=0;o<n;o++)this.addSlot(i)}async read(e,t,s,i){this.assertLive();const n=i??0;if(!Number.isInteger(t)||t<=0||t%4!==0)throw Xe("byteLength",t,"a positive multiple of 4");if(!Number.isInteger(n)||n<0||n%4!==0)throw Xe("srcOffset",n,"a non-negative multiple of 4");if(n+t>e.size)throw Xe("byteLength",t,`srcOffset + byteLength <= src.size (${e.size})`);if((e.usage&ie.COPY_SRC)===0)throw Xe("src",e.label,"a buffer with COPY_SRC usage");let o,a;if(s===void 0)o=new ArrayBuffer(t),a=new Uint8Array(o);else{if(ac(s.buffer))throw Xe("dest","SharedArrayBuffer","a view over a plain ArrayBuffer");if(s.byteLength<t)throw Xe("dest",s.byteLength,`at least ${t} bytes`);o=s.buffer,a=new Uint8Array(o,s.byteOffset,t)}let c=0;for(;c<t;){const l=Math.min(this.slotBytes,t-c),d=this.borrowSlot(l),f=this.ring[d.index];try{const p=this.device.createCommandEncoder({label:"readback"});p.copyBufferToBuffer(e,n+c,d.buffer,0,l),this.device.queue.submit([p.finish()]),await this.map(f,l),a.set(new Uint8Array(d.buffer.getMappedRange(0,l)),c)}finally{this.returnSlot(d)}c+=l}return o}async readU32(e,t){const s=await this.read(e,4,void 0,t);return new Uint32Array(s)[0]}borrowSlot(e){if(this.assertLive(),!Number.isInteger(e)||e<=0)throw Xe("byteLength",e,"a positive integer");for(const i of this.ring)if(!i.borrowed&&i.slot.capacity>=e)return i.borrowed=!0,i.slot;const t=Math.max(this.slotBytes,Math.ceil(e/Ii)*Ii),s=this.addSlot(t);return s.borrowed=!0,s.slot}mapSlot(e,t){this.assertLive();const s=this.borrowedEntry(e);if(s.mapPending||s.mapped)throw Xe("slot",e.index,"a borrowed slot that is not mapped and has no map pending");return this.track(s,e.buffer.mapAsync(yi.READ,0,t))}returnSlot(e){if(this.disposed)return;const t=this.borrowedEntry(e);if(t.mapPending){t.returnRequested=!0;return}this.finishReturn(t)}borrowedEntry(e){const t=this.ring[e.index];if(t?.slot!==e||!t.borrowed)throw Xe("slot",e.index,"a slot borrowed from this ring and not yet returned");return t}finishReturn(e){const t=e.slot.buffer.mapState;(t===void 0?e.mapped:t!=="unmapped")&&e.slot.buffer.unmap(),e.mapped=!1,e.borrowed=!1,e.returnRequested=!1}track(e,t){e.mapPending=!0;const s=i=>{if(e.mapPending=!1,e.mapped=i,this.disposed){i&&e.slot.buffer.unmap(),this.destroySlot(e),e.mapped=!1;return}e.returnRequested&&this.finishReturn(e)};return t.then(()=>{s(!0)},()=>{s(!1)}),t}get slots(){return this.ring.length}get borrowed(){let e=0;for(const t of this.ring)t.borrowed&&e++;return e}destroyAll(){for(const e of this.ring)e.mapPending||this.destroySlot(e);this.ring.length=0,this.disposed=!0}destroySlot(e){e.tracked?this.allocator.destroy(e.slot.buffer):e.slot.buffer.destroy()}addSlot(e){const t=this.ring.length,s={label:`readback:slot:${t}`,size:e,usage:oc},i=e>=Sn,n=i?this.allocator.createBuffer(s):this.device.createBuffer(s),o={slot:Object.freeze({index:t,buffer:n,capacity:e}),borrowed:!1,mapped:!1,mapPending:!1,returnRequested:!1,tracked:i};return this.ring.push(o),o}async map(e,t){let s=!1,i=null;try{await this.track(e,e.slot.buffer.mapAsync(yi.READ,0,t)),s=!0}catch(n){i=n}if(this.disposed)throw new h("E_DISPOSED","the readback ring was disposed while a read was pending",{label:"readback"});if(!s){const n=i instanceof Error?i.message:String(i);throw new h("E_DEVICE_LOST",`mapAsync rejected: ${n}`,{reason:"mapAsync",message:n})}}assertLive(){if(this.disposed)throw new h("E_DISPOSED","the readback ring was disposed",{label:"readback"})}}const lc=Object.freeze(["rowPtr","colIdx","weights","arcToEdge","edgeToArc"]),uc=new Set(["colIdx","weights","arcToEdge"]);function dc(r){const e=r.arcCount,t=r.flags.arcToEdgeIsIdentity;return{rowPtr:4*(r.nodeCount+1),colIdx:4*e,weights:r.flags.weighted&&e>0?4*e:0,arcToEdge:!t&&e>0?4*e:0,edgeToArc:!t&&r.edgeCount>0?4*r.edgeCount:0}}function fc(r,e){const t=new Set(r);return t.add("rowPtr"),lc.filter(s=>t.has(s)&&e[s]>0)}function Cs(r){return r-r%Mt}function ss(r,e,t){return new h("E_TOO_LARGE",`${t}: ${r} bytes exceed the device limit of ${e} bytes`,{needed:r,limit:e,path:t,algorithm:null})}function is(r,e,t){return new h("E_INVALID_ARGUMENT",`${r}: expected ${t}`,{argument:r,value:e,expected:t})}function hc(r){return Cs(Math.floor(r.maxStorageBufferBindingSize/4))}function pc(r,e,t){if(r.length===0)throw is("rowPtr",r.length,"nodeCount + 1 entries");if(!Number.isInteger(t)||t<Mt||t%Mt!==0)throw is("arcsPerWindow",t,`a positive multiple of ${Mt}`);const s=r.length-1;if(r[s]!==e)throw is("arcCount",e,`rowPtr[${s}] === ${r[s]}`);const i=[];if(e===0)return i;let n=0,o=0,a=0;for(;n<e;){for(;r[o+1]<=n;)o++;const c=Cs(n),l=c+t;if(r[o+1]>l)i.push({start:c,end:l,rowFirst:a,rowLast:o,bufferIndex:0,offset:4*c}),n=l,a=o;else{let d=o;for(;d+1<s&&r[d+2]<=l;)d++;const f=r[d+1];i.push({start:c,end:f,rowFirst:a,rowLast:d,bufferIndex:0,offset:4*c}),n=f,a=d+1}}return i}function gc(r,e,t){if(e<=t)return{buffers:[{byteOffset:0,byteLength:e}],placed:[...r]};const s=[],i=[];let n=0;for(;n<r.length;){let o=n;for(;o+1<r.length&&4*(r[o+1].end-r[n].start)<=t;)o++;const a=r[n].start,c=s.length;s.push({byteOffset:4*a,byteLength:4*(r[o].end-a)});for(let l=n;l<=o;l++){const d=r[l];i.push({start:d.start,end:d.end,rowFirst:d.rowFirst,rowLast:d.rowLast,bufferIndex:c,offset:4*(d.start-a)})}n=o+1}return{buffers:s,placed:i}}function mc(r,e,t){const{limits:s}=e,i=s.maxStorageBufferBindingSize,n=s.maxBufferSize,o=w=>w<=i&&w<=n,a=w=>w>i?i:n,c=dc(r),l=fc(t,c);if(!o(c.rowPtr))throw ss(c.rowPtr,a(c.rowPtr),"rowPtr");const{arena:d}=r;if(d!==null){const v=l.some(S=>(S==="arcToEdge"||S==="edgeToArc")&&d.segments[S]!==null)&&d.byteLength<=n,y=v?d.byteLength:d.hotByteLength,I=l.every(S=>{const P=d.segments[S];return P===null||o(P.byteLength)});if(y<=n&&I){const S=P=>{const E=d.segments[P];if(E===null)return null;const A=E.byteOffset-d.byteOffset;return A+E.byteLength<=y?{offset:A,size:E.byteLength}:null};return{kind:"arena",bytes:y,includesCold:v,segments:{rowPtr:S("rowPtr"),colIdx:S("colIdx"),weights:S("weights"),arcToEdge:S("arcToEdge"),edgeToArc:S("edgeToArc")}}}}if(l.every(w=>o(c[w])))return{kind:"perArray",arrays:l.map(w=>({name:w,byteLength:c[w],buffers:[{byteOffset:0,byteLength:c[w]}]}))};if(l.includes("edgeToArc")&&!o(c.edgeToArc))throw ss(c.edgeToArc,a(c.edgeToArc),"binding");const f=Math.min(hc(s),Cs(Math.floor(n/4)));if(f<Mt)throw ss(4*Mt,i,"binding");const p=pc(r.rowPtr,r.arcCount,f),{buffers:g,placed:m}=gc(p,4*r.arcCount,n);return{kind:"windowed",arrays:l.map(w=>uc.has(w)?{name:w,byteLength:c[w],buffers:g}:{name:w,byteLength:c[w],buffers:[{byteOffset:0,byteLength:c[w]}]}),windows:m,arcsPerWindow:f}}const wc=Object.freeze(["rowPtr","colIdx","weights"]),bc=Object.freeze(["rowPtr","colIdx","weights","arcToEdge","edgeToArc"]),vc=Object.freeze(["colIdx","weights","arcToEdge"]),yc=ie.STORAGE|ie.COPY_DST|ie.COPY_SRC;function _c(r){return vc.includes(r)}function Pc(r){return Object.prototype.toString.call(r)==="[object SharedArrayBuffer]"}function yt(r,e,t){return new h("E_INVALID_ARGUMENT",`${r}: expected ${t}`,{argument:r,value:e,expected:t})}function _r(r){if(r.detached)throw new h("E_SNAPSHOT",`snapshot ${r.serial} is detached (its core was transferred away)`,{reason:"detached",serial:r.serial})}function Sc(r,e){switch(e){case"rowPtr":return!0;case"colIdx":return r.arcCount>0;case"weights":return r.flags.weighted&&r.arcCount>0;case"arcToEdge":return!r.flags.arcToEdgeIsIdentity&&r.arcCount>0;case"edgeToArc":return!r.flags.arcToEdgeIsIdentity&&r.edgeCount>0;default:return!1}}function ki(r,e){switch(e){case"rowPtr":return r.rowPtr;case"colIdx":return r.colIdx;case"weights":{const{weights:t}=r;if(t===null)throw yt("weights",null,"a weighted snapshot");return t}case"arcToEdge":return r.arcToEdge;case"edgeToArc":return r.edgeToArc;default:throw yt("name",e,"a core array name")}}class Ec{constructor(e,t,s,i){this.residents=new WeakMap,this.records=new WeakMap,this.bySerial=new Map,this.orphans=new Set,this.tombstones=new Set,this.liveCount=0,this.liveBytesValue=0,this.warned=!1,this.disposed=!1,this.device=e,this.caps=t,this.allocator=s,this.warnUnreleasedSnapshots=i.warnUnreleasedSnapshots,this.warn=i.warn??(n=>{console.warn(n)})}core(e,t){this.assertLive(),_r(e);const s=t??wc,i=bc.filter(l=>(l==="rowPtr"||s.includes(l))&&Sc(e,l)),n=mc(e,this.caps,i),o=this.ensureRecord(e);if(o.plan===null&&(o.plan=n.kind,n.kind==="arena"&&e.arena!==null)){const{arena:l}=e,d=this.upload(o,e.rowPtr,new Uint8Array(l.buffer,l.byteOffset,n.bytes),`residency:core:${o.serial}:arena`);o.arena={buffer:d.buffer,segments:n.segments}}n.kind==="windowed"&&o.windows===null&&(o.plan="windowed",o.windows=n.windows),this.bindMissing(o,e,n,i);const a=o.bindings.get("rowPtr");if(a===void 0)throw yt("rowPtr",null,"a bound rowPtr (internal invariant)");const c=o.bindings.get("weights")??null;return Object.freeze({serial:o.serial,plan:n.kind,rowPtr:a,colIdx:o.bindings.get("colIdx")??null,weights:c,arcToEdge:o.bindings.get("arcToEdge")??null,edgeToArc:o.bindings.get("edgeToArc")??null,windows:o.windows,arcBuffers:o.windows===null?null:Object.freeze({colIdx:Object.freeze([...o.arcBuffers.get("colIdx")??[]]),weights:Object.freeze([...o.arcBuffers.get("weights")??[]]),arcToEdge:Object.freeze([...o.arcBuffers.get("arcToEdge")??[]])}),hasWeights:c!==null})}view(e,t,s){this.assertLive(),_r(e);const i=s?.packViews===!0;if(t==="reverse"||t==="edgeList"){this.assertNotReleased(e),this.assertNonEmpty(e);const f=i?`${t}:packed`:t,p=this.ensureRecord(e),g=p.views.get(f);if(g!==void 0)return g;const m=t==="reverse"?this.buildReverse(e,p,i):this.buildEdgeList(e,p,i);return p.views.set(f,m),m}if(i)throw new h("E_UNSUPPORTED","packViews applies to the reverse and edgeList views only",{option:"packViews",hint:`the ${t} view uploads one array`});let n,o,a={};switch(t){case"outDegree":this.assertNotReleased(e),this.assertNonEmpty(e),n=e.outDegree(),o="outDegree";break;case"inDegree":this.assertNotReleased(e),this.assertNonEmpty(e),n=e.inDegree(),o="inDegree";break;case"degreeOrder":{this.assertNotReleased(e),this.assertNonEmpty(e);const f=e.degreeOrder();n=f.perm,o="perm",a={segmentOffsets:Array.from(f.segmentOffsets)};break}case"reverseDegreeOrder":{this.assertNotReleased(e),this.assertNonEmpty(e);const f=e.degreeOrder({of:"reverse"});n=f.perm,o="perm",a={segmentOffsets:Array.from(f.segmentOffsets)};break}case"coo":if(this.assertNotReleased(e),this.assertNonEmpty(e),e.arcCount===0)return Object.freeze({view:t,bindings:Object.freeze({}),scalars:Object.freeze({})});n=e.coo().src,o="src";break;case"mate":throw new h("E_UNSUPPORTED","the mate view is not uploaded: no kernel reads it",{feature:"view:mate",hint:"outDegree, inDegree, degreeOrder, reverseDegreeOrder, coo, reverse and edgeList are uploaded"});default:throw yt("name",t,"a view name")}const c=this.ensureRecord(e),l=this.upload(c,n,n,`residency:view:${c.serial}:${t}`),d={buffer:l.buffer,offset:0,size:l.byteLength,window:null};return Object.freeze({view:t,bindings:Object.freeze({[o]:d}),scalars:Object.freeze(a)})}buildReverse(e,t,s){const i=e.reverse();if(!e.directed){const a=this.core(e,["rowPtr","colIdx","weights"]);if(a.plan==="windowed")throw new h("E_TOO_LARGE",`snapshot ${e.serial}: the undirected reverse view is the core, which needs arc windows (${a.windows?.length??0}) that no view executes`,{needed:4*e.arcCount,limit:this.caps.limits.maxStorageBufferBindingSize,path:"windowed",algorithm:null});const c={rowPtr:a.rowPtr};return a.colIdx!==null&&(c.colIdx=a.colIdx),a.weights!==null&&(c.weights=a.weights),Object.freeze({view:"reverse",bindings:Object.freeze(c),scalars:Object.freeze({arcCount:[i.arcCount],directed:[0]})})}const n=[["rowPtr",i.rowPtr],["colIdx",i.colIdx]];i.weights!==null&&n.push(["weights",i.weights]);const o=s?this.packArrays(t,n,this.packKey(t,"reverse:packed"),`residency:view:${t.serial}:reverse:packed`):this.separateArrays(t,n,`residency:view:${t.serial}:reverse`);return Object.freeze({view:"reverse",bindings:o,scalars:Object.freeze({arcCount:[i.arcCount],directed:[1]})})}buildEdgeList(e,t,s){const i=e.edgeList(),n=[["src",i.src],["dst",i.dst]];i.weights!==null&&n.push(["weights",i.weights]);const o=s?this.packArrays(t,n,this.packKey(t,"edgeList:packed"),`residency:view:${t.serial}:edgeList:packed`):this.separateArrays(t,n,`residency:view:${t.serial}:edgeList`);return Object.freeze({view:"edgeList",bindings:o,scalars:Object.freeze({edgeCount:[e.edgeCount]})})}separateArrays(e,t,s){const i={};for(const[n,o]of t){if(o.byteLength===0)continue;const a=this.upload(e,o,o,`${s}:${n}`);i[n]={buffer:a.buffer,offset:0,size:a.byteLength,window:null}}return Object.freeze(i)}packArrays(e,t,s,i){const n=t.filter(([,f])=>f.byteLength>0);if(n.length===0)return Object.freeze({});const o=[];let a=0;for(const[,f]of n)o.push(a),a+=Math.ceil(f.byteLength/Ar)*Ar;const c=new Uint8Array(a);n.forEach(([,f],p)=>{c.set(new Uint8Array(f.buffer,f.byteOffset,f.byteLength),o[p])});const l=this.upload(e,s,c,i),d={};return n.forEach(([f,p],g)=>{d[f]={buffer:l.buffer,offset:o[g],size:p.byteLength,window:null}}),Object.freeze(d)}packKey(e,t){const s=e.packKeys.get(t);if(s!==void 0)return s;const i={};return e.packKeys.set(t,i),i}column(e,t,s){this.assertLive(),_r(s),this.assertNotReleased(s);const i=e.require(t),n=e.gpuView(t);if(n.byteLength===0)throw yt("column",t,"a column with at least one row (zero-length arrays are never uploaded)");const o=this.ensureRecord(s),a=`residency:column:${o.serial}:${i.meta.domain}.${t}`,c=this.residents.get(i);let l;return c!==void 0&&!c.destroyed&&c.serial===o.serial&&c.byteLength===n.byteLength?(l=c,l.version!==i.version&&(this.device.queue.writeBuffer(l.buffer,0,n),l.version=i.version)):(c!==void 0&&this.residents.delete(i),l=this.upload(o,i,n,a),l.version=i.version),Object.freeze({binding:{buffer:l.buffer,offset:0,size:l.byteLength,window:null},column:i,version:l.version,eligibility:i.gpu,components:i.meta.components})}array(e,t,s){if(this.assertLive(),e.byteLength===0||e.byteLength%4!==0)throw yt("key",e.byteLength,"a non-empty array whose byteLength is a multiple of 4");if(Pc(e.buffer))throw yt("key","SharedArrayBuffer","an array over a plain ArrayBuffer");let i=null;s!==void 0&&(_r(s),this.assertNotReleased(s),i=this.ensureRecord(s));const n=this.upload(i,e,e,`residency:array:${t}`);return{binding:{buffer:n.buffer,offset:0,size:n.byteLength,window:null},byteLength:n.byteLength,owner:s??null,destroy:()=>{this.destroyResident(n,i)}}}release(e){const t=this.bySerial.get(e.serial);if(t!==void 0){for(const s of[...t.entries])this.destroyResident(s,t);this.forget(t),this.tombstones.add(e.serial)}}clearOnLoss(){for(const e of[...this.bySerial.values()]){for(const t of e.entries)t.destroyed=!0,this.residents.get(t.key)===t&&this.residents.delete(t.key);e.entries.length=0,this.forget(e)}for(const e of this.orphans)e.destroyed=!0,this.residents.get(e.key)===e&&this.residents.delete(e.key);this.orphans.clear(),this.liveCount=0,this.liveBytesValue=0}destroyAll(){for(const e of[...this.bySerial.values()]){for(const t of[...e.entries])this.destroyResident(t,e);this.forget(e)}for(const e of[...this.orphans])this.destroyResident(e,null);this.disposed=!0}stats(){const e=[...this.bySerial.values()].map(t=>({serial:t.serial,label:t.label,bytes:t.entries.reduce((s,i)=>s+i.byteLength,0),buffers:t.entries.length}));return{buffers:this.liveCount,bytes:this.liveBytesValue,snapshots:this.bySerial.size,perSnapshot:e}}get residentBytes(){return this.liveBytesValue}isReleased(e){return this.tombstones.has(e)}bindMissing(e,t,s,i){for(const n of i)if(!e.bindings.has(n)){const o=s.kind==="windowed"?s.arrays.find(a=>a.name===n):void 0;e.bindings.set(n,o!==void 0&&o.buffers.length>0&&e.windows!==null&&_c(n)?this.bindWindowed(e,t,n,o.buffers,e.windows[0]):this.bindCore(e,t,n))}}bindCore(e,t,s){if(e.arena!==null){const o=e.arena.segments[s];if(o!==null)return{buffer:e.arena.buffer,offset:o.offset,size:o.size,window:null}}const i=ki(t,s),n=this.upload(e,i,i,`residency:core:${e.serial}:${s}`);return{buffer:n.buffer,offset:0,size:n.byteLength,window:null}}bindWindowed(e,t,s,i,n){const o=ki(t,s),a=i.map((c,l)=>this.upload(e,this.windowKey(e,`${s}:${l}`),new Uint8Array(o.buffer,o.byteOffset+c.byteOffset,c.byteLength),`residency:core:${e.serial}:${s}:w${l}`).buffer);return e.arcBuffers.set(s,a),{buffer:a[n.bufferIndex],offset:n.offset,size:4*(n.end-n.start),window:n}}windowKey(e,t){const s=e.windowKeys.get(t);if(s!==void 0)return s;const i={};return e.windowKeys.set(t,i),i}upload(e,t,s,i){const n=e===null?null:e.serial,o=this.residents.get(t);if(o!==void 0&&!o.destroyed&&o.serial===n)return o;const a=this.allocator.createBuffer({label:i,size:s.byteLength,usage:yc});this.device.queue.writeBuffer(a,0,s);const c={key:t,buffer:a,byteLength:s.byteLength,serial:n,version:0,destroyed:!1};return this.residents.set(t,c),e===null?this.orphans.add(c):e.entries.push(c),this.liveCount++,this.liveBytesValue+=s.byteLength,c}destroyResident(e,t){if(!e.destroyed){if(e.destroyed=!0,this.allocator.destroy(e.buffer),this.residents.get(e.key)===e&&this.residents.delete(e.key),t===null)this.orphans.delete(e);else{const s=t.entries.indexOf(e);s>=0&&t.entries.splice(s,1)}this.liveCount--,this.liveBytesValue-=e.byteLength}}forget(e){e.released=!0,e.bindings.clear(),e.views.clear(),e.packKeys.clear(),e.windowKeys.clear(),e.arcBuffers.clear(),e.windows=null,e.arena=null,e.plan=null,this.bySerial.delete(e.serial)}ensureRecord(e){let t=this.records.get(e);return t?.released&&(t=void 0),t===void 0&&(t=this.bySerial.get(e.serial)),t===void 0&&(t={serial:e.serial,label:e.label,entries:[],plan:null,arena:null,bindings:new Map,views:new Map,packKeys:new Map,windowKeys:new Map,windows:null,arcBuffers:new Map,released:!1},this.bySerial.set(e.serial,t),this.tombstones.delete(e.serial),!this.warned&&this.bySerial.size>this.warnUnreleasedSnapshots&&(this.warned=!0,this.warn(`[webgpu-graph-algorithms] ${this.bySerial.size} snapshots are resident on the device (warnUnreleasedSnapshots = ${this.warnUnreleasedSnapshots}); call ctx.release(snapshot) for superseded snapshots -- GPU memory is never freed by garbage collection (spec 4.1)`))),this.records.set(e,t),t}assertNotReleased(e){if(this.tombstones.has(e.serial))throw new h("E_RELEASED",`snapshot ${e.serial} was released; upload it again through core() before binding views, columns or arrays`,{serial:e.serial})}assertNonEmpty(e){if(e.nodeCount===0)throw yt("snapshot",0,"nodeCount > 0 (an empty snapshot has no per-node view to upload)")}assertLive(){if(this.disposed)throw new h("E_DISPOSED","the residency was disposed",{label:"residency"})}}const Ic="webgpu-graph-algorithms",kc=Object.freeze(["subgroups","timestamp-query"]),Mn="high-performance";async function Ac(r){if(r.adapter!==void 0)return r.adapter;if(r.gpu!==void 0)return On(r.gpu,r.powerPreference??Mn);throw new h("E_NO_WEBGPU","GpuContext.create needs one of gpu, adapter or device",{reason:"no gpu, adapter or device given",hint:"pass navigator.gpu (browser) or the gpu of createNodeGpu() (Node), or use the ./browser and ./node entries"})}class zt{constructor(e){this.currentState="ready",this.lostError=null,this.slot=new _a,this.lostListeners=new Set,this.disposers=[],this.batchCounter=0,this.device=e.device,this.caps=e.caps,this.ownsDevice=e.ownsDevice,this.label=e.label,this.debug={inspect:!1},this.workgroupSize=Bs(e.caps),this.allocator=new ya(e.device),this.residency=new Ec(e.device,e.caps,this.allocator,{warnUnreleasedSnapshots:e.warnUnreleasedSnapshots}),this.pool=new $s(e.device,this.allocator,e.caps.limits.maxBufferSize),this.readback=new cc(e.device,this.allocator),this.pipelines=new rc(e.device,e.caps),this.profiler=e.device.features.has("timestamp-query")?new sc(e.device,!0,e.caps.runtime!=="node"):null,this.uninstallSink=Sa(e.device,this.slot,e.onError),this.lost=Ea(e.device,t=>{this.handleLost(t)})}static async probe(e){const{gpu:t}=e;if(t===void 0)return{ok:!1,code:"E_NO_WEBGPU",reason:"no GPU object: navigator.gpu is undefined (no WebGPU in this runtime)",adapter:null,summary:null};let s;try{s=await On(t,e.powerPreference??Mn)}catch(n){return{ok:!1,code:"E_NO_ADAPTER",reason:Nt(n)?n.message:String(n),adapter:null,summary:null}}const i=Kt(s);return e.rejectSoftware===!0&&i.software?{ok:!1,code:"E_SOFTWARE_ONLY",reason:`software adapter rejected: ${i.vendor}/${i.architecture}`,adapter:s,summary:i}:{ok:!0,code:"OK",reason:null,adapter:s,summary:i}}static async create(e){const t=e.runtime??"unknown",s=e.label??Ic,i=e.onError??null,n=e.warnUnreleasedSnapshots??li;if(e.device!==void 0){const p=wi(e.device,{runtime:t});return ts(p),new zt({device:e.device,caps:p,ownsDevice:!1,label:s,onError:i,warnUnreleasedSnapshots:n})}const o=await Ac(e),a=Kt(o);if(e.rejectSoftware===!0&&a.software)throw new h("E_SOFTWARE_ONLY",`software adapter rejected by rejectSoftware: ${a.vendor}/${a.architecture}`,{adapter:a});const c=ga(o,e.limits??"raise"),l=ma(o,e.requiredFeatures??[],e.optionalFeatures??kc),d=await wa(o,{label:s,requiredLimits:c,requiredFeatures:l}),f=ba(d,o.info,t,e.gpu?.wgslLanguageFeatures??[]);try{ts(f)}catch(p){throw d.destroy(),p}return new zt({device:d,caps:f,ownsDevice:!0,label:s,onError:i,warnUnreleasedSnapshots:n})}static from(e,t){const s=wi(e,t);ts(s);const i=e.label??"";return new zt({device:e,caps:s,ownsDevice:!1,label:i===""?"GpuContext.from":i,onError:null,warnUnreleasedSnapshots:li})}get state(){return this.currentState}assertReady(){if(this.currentState==="disposed")throw new h("E_DISPOSED",`GpuContext "${this.label}" is disposed`,{label:this.label});if(this.lostError!==null)throw this.lostError;const e=this.slot.take();if(e!==null)throw e}takePendingError(){return this.slot.take()}nextBatchId(){return this.batchCounter+=1,this.batchCounter}onLost(e){return this.lostListeners.add(e),()=>{this.lostListeners.delete(e)}}attachDisposer(e){if(this.currentState==="disposed"){e();return}this.disposers.push(e)}release(e){this.currentState!=="disposed"&&(this.residency.release(e),this.pool.trim())}dispose(){if(this.currentState==="disposed")return;this.currentState="disposed",this.uninstallSink(),this.residency.destroyAll(),this.pool.destroyAll(),this.readback.destroyAll(),this.profiler!==null&&this.profiler.destroy(),this.ownsDevice&&this.device.destroy();const e=this.disposers.splice(0);for(const t of e)t()}handleLost(e){if(this.currentState!=="disposed"){this.currentState="lost",this.lostError=Nn(e),this.residency.clearOnLoss();for(const t of Array.from(this.lostListeners))try{t(e)}catch{}}}}const Ai=2e3;async function xc(r,e){let t;const s=new Promise(i=>{t=setTimeout(i,e)});await Promise.race([r.then(()=>{},()=>{}),s]),clearTimeout(t)}function Rc(r){return r instanceof Error?r.message:typeof r=="string"?r:`non-error thrown (${typeof r})`}class ne{constructor(e,t,s){this.requests=[],this.openPass=null,this.dispatchCount=0,this.stagingBytes=0,this.slot=null,this.submitted=!1,this.host=e,this.id=e.nextBatchId(),this.generation=s??0,this.label=t,this.encoder=e.device.createCommandEncoder({label:`batch/${t}#${this.id}`})}get dispatches(){return this.dispatchCount}pass(e){this.assertOpen("pass"),this.endPass();const t={label:`batch/${this.label}#${this.id}/${e}`},s=this.host.profiler?.beginPass(e);s!==void 0&&(t.timestampWrites=s);const i=this.encoder.beginComputePass(t);return this.countDispatches(i),this.openPass=i,i}endPass(){this.openPass!==null&&(this.openPass.end(),this.openPass=null)}copy(e,t,s){if(this.assertOpen("copy"),this.checkByteLength(s),s>e.size||s>t.size)throw new h("E_INVALID_ARGUMENT",`copy of ${s} bytes exceeds a binding (src ${e.size}, dst ${t.size})`,{argument:"byteLength",value:s,expected:`<= ${Math.min(e.size,t.size)}`});this.endPass(),this.encoder.copyBufferToBuffer(e.buffer,e.offset,t.buffer,t.offset,s)}readback(e,t,s){if(this.assertOpen("readback"),this.checkByteLength(s),!Number.isInteger(t)||t<0||t%4!==0||t+s>e.size)throw new h("E_INVALID_ARGUMENT",`readback of [${t}, ${t+s}) is outside the ${e.size}-byte source or not 4-aligned`,{argument:"srcOffset",value:t,expected:`a multiple of 4 with srcOffset + byteLength <= ${e.size}`});const i={src:e,srcOffset:t,byteLength:s,offset:this.stagingBytes};return this.requests.push(i),this.stagingBytes+=s,this.ensureSlot(this.stagingBytes),i}resolveQuerySet(e,t,s,i){this.assertOpen("resolveQuerySet"),this.endPass(),this.encoder.resolveQuerySet(e,t,s,i,0)}submit(){this.assertOpen("submit"),this.submitted=!0,this.endPass();const{slot:e}=this,t=this.host.takePendingError();try{this.host.assertReady()}catch(c){throw this.releaseSlot(),c}if(e!==null)for(const c of this.requests)this.encoder.copyBufferToBuffer(c.src,c.srcOffset,e.buffer,c.offset,c.byteLength);const s=this.encoder.finish({label:`batch/${this.label}#${this.id}`});this.host.device.queue.submit([s]);const i=this.host.takePendingError(),n=t??i,o={discarded:!1},a=n===null?this.awaitReadback(e,o):this.rejectReadback(n);return{id:this.id,generation:this.generation,readback:a,discard:()=>{o.discarded=!0}}}rejectReadback(e){return this.releaseSlot(),Promise.reject(this.attribute(e))}async awaitReadback(e,t){if(e===null)return await this.host.allocator.check(),new ArrayBuffer(0);const s=this.stagingBytes;let i=()=>{};const n=new Promise((c,l)=>{i=this.host.onLost(d=>{l(Nn(d))})});n.catch(()=>{});const o=this.host.readback.mapSlot(e,s);o.catch(()=>{});let a;try{await Promise.race([o,n]),this.host.assertReady(),a=t.discarded?new ArrayBuffer(0):e.buffer.getMappedRange(0,s).slice(0)}catch(c){throw await xc(o,Ai),this.releaseSlot(),await this.classify(c,n)}finally{i()}return this.releaseSlot(),await this.host.allocator.check(),a}async classify(e,t){if(Nt(e))return e.code==="E_DEVICE_LOST"||e.code==="E_DISPOSED"?e:this.attribute(e);try{this.host.assertReady()}catch(a){if(Nt(a))return a.code==="E_DEVICE_LOST"||a.code==="E_DISPOSED"?a:this.attribute(a)}let s;const i=new Promise(a=>{s=setTimeout(()=>{a(null)},Ai)}),n=await Promise.race([t.then(()=>null,a=>a),i]);if(clearTimeout(s),Nt(n))return n;const o=Rc(e);return new h("E_VALIDATION",`mapAsync failed for batch ${this.id} "${this.label}": ${o}`,{label:this.label,message:o,batchId:this.id})}attribute(e){return new h(e.code,`${e.message} (delivered to batch ${this.id} "${this.label}")`,{...e.details,batchId:this.id,batchLabel:this.label})}ensureSlot(e){this.slot!==null&&this.slot.capacity>=e||(this.releaseSlot(),this.slot=this.host.readback.borrowSlot(e))}releaseSlot(){if(this.slot===null)return;const{slot:e}=this;this.slot=null;try{this.host.readback.returnSlot(e)}catch(t){if(!Nt(t)||t.code!=="E_DISPOSED")throw t}}countDispatches(e){const t=e.dispatchWorkgroups.bind(e),s=e.dispatchWorkgroupsIndirect.bind(e);e.dispatchWorkgroups=(i,n,o)=>{this.dispatchCount+=1,t(i,n,o)},e.dispatchWorkgroupsIndirect=(i,n)=>{this.dispatchCount+=1,s(i,n)}}assertOpen(e){if(this.submitted)throw new h("E_INVALID_ARGUMENT",`${e}() after submit() on batch ${this.id} "${this.label}"`,{argument:e,value:this.id,expected:"a batch that has not been submitted"})}checkByteLength(e){if(!Number.isInteger(e)||e<=0||e%4!==0)throw new h("E_INVALID_ARGUMENT",`byteLength ${e} is not a positive multiple of 4`,{argument:"byteLength",value:e,expected:"a positive multiple of 4"})}}function Gn(r){return Math.min(Ct,r.limits.maxComputeWorkgroupsPerDimension)}function Ms(r,e){if(!Number.isInteger(e)||e<0)throw new h("E_INVALID_ARGUMENT",`${r} must be a non-negative integer, got ${e}`,{argument:r,value:e,expected:"a non-negative integer"})}function zn(r){let e=r;for(;Number.isInteger(e)&&e>1&&e%2===0;)e/=2;if(e!==1)throw new h("E_INVALID_ARGUMENT",`wg must be a power of two, got ${r}`,{argument:"wg",value:r,expected:"a power of two >= 1"})}function Fn(r,e,t){const s=Gn(t);if(r===0)return{x:0,y:1,z:1,items:e,stride:null};if(r<=s)return{x:r,y:1,z:1,items:e,stride:null};const i=Math.ceil(r/s);if(i>s)throw new h("E_TOO_LARGE",`${r} workgroups exceed the 2D dispatch limit of ${s*s}`,{needed:r,limit:s*s,path:"dispatch",algorithm:null,items:e});return{x:s,y:i,z:1,items:e,stride:null}}function z(r,e,t){return Ms("items",r),zn(e),Fn(Math.ceil(r/e),r,t)}function Ft(r,e){return Ms("groups",r),Fn(r,r,e)}function Oe(r,e,t,s){if(Ms("items",r),zn(e),r===0)return{x:0,y:1,z:1,items:r,stride:null};const i=Math.min(s??(t.software?64:4096),Gn(t)),n=Math.min(Math.ceil(r/e),i);return{x:n,y:1,z:1,items:r,stride:n*e}}function Gs(r){return r.x*r.y}const xi=Object.freeze({u32:{size:4,align:4,width:1,scalar:"u32"},i32:{size:4,align:4,width:1,scalar:"i32"},f32:{size:4,align:4,width:1,scalar:"f32"},vec2f:{size:8,align:8,width:2,scalar:"f32"},vec2u:{size:8,align:8,width:2,scalar:"u32"},vec4f:{size:16,align:16,width:4,scalar:"f32"},vec4u:{size:16,align:16,width:4,scalar:"u32"}}),ns=16,Lc=/^[A-Za-z_]\w*$/,Dc=4294967295,Tc=-2147483648,Oc=2147483647;function Ri(r,e){return Math.ceil(r/e)*e}function Li(r,e){if(!Lc.test(e)||$n.includes(e))throw new h("E_INVALID_ARGUMENT",`UniformBlock: "${e}" is not a usable WGSL identifier`,{argument:r,value:e,expected:"a WGSL identifier that is not a reserved word"})}function Di(r,e,t){if(typeof t!="number")throw new h("E_INVALID_ARGUMENT",`UniformBlock: field "${r}" expects a number`,{argument:r,value:t,expected:e});if(e==="u32"&&!(Number.isInteger(t)&&t>=0&&t<=Dc))throw new h("E_INVALID_ARGUMENT",`UniformBlock: field "${r}" is not a u32`,{argument:r,value:t,expected:"an integer in [0, 4294967295]"});if(e==="i32"&&!(Number.isInteger(t)&&t>=Tc&&t<=Oc))throw new h("E_INVALID_ARGUMENT",`UniformBlock: field "${r}" is not an i32`,{argument:r,value:t,expected:"an integer in [-2147483648, 2147483647]"})}function Nc(r,e,t,s){e==="u32"?r.setUint32(t,s,!0):e==="i32"?r.setInt32(t,s,!0):r.setFloat32(t,s,!0)}function Ti(r,e,t){return e==="u32"?r.getUint32(t,!0):e==="i32"?r.getInt32(t,!0):r.getFloat32(t,!0)}class se{static define(e,t,s){if(Li("name",e),t.length===0)throw new h("E_INVALID_ARGUMENT",`UniformBlock ${e}: a block needs at least one field`,{argument:"fields",value:t.length,expected:">= 1 field"});const i=[],n=new Map;let o=0;for(const[p,g]of t){if(Li("fields",p),n.has(p))throw new h("E_INVALID_ARGUMENT",`UniformBlock ${e}: duplicate field "${p}"`,{argument:"fields",value:p,expected:"unique field names"});const m=xi[g];if(m===void 0)throw new h("E_INVALID_ARGUMENT",`UniformBlock ${e}: unknown field type "${g}"`,{argument:"fields",value:g,expected:Object.keys(xi).join(" | ")});const b=Ri(o,m.align),w={name:p,type:g,offset:b,info:m};i.push(w),n.set(p,w),o=b+m.size}const a=Ri(o,ns),c=s?.padTo;if(c!==void 0&&(!Number.isInteger(c)||c%ns!==0||c<a))throw new h("E_INVALID_ARGUMENT",`UniformBlock ${e}: padTo ${c} is not a multiple of 16 >= ${a}`,{argument:"padTo",value:c,expected:`a multiple of ${ns} not below ${a}`});const l=c??a,d=i.map((p,g)=>{const m=g===i.length-1,b=p.offset+p.info.size;return` ${m&&b<l?`@size(${l-p.offset}) `:""}${p.name}: ${p.type},`}),f=`struct ${e} {
|
|
222
|
+
${d.join(`
|
|
223
|
+
`)}
|
|
224
|
+
}`;return new se(e,s?.layout??"uniform",t,l,f,i,n)}constructor(e,t,s,i,n,o,a){this.name=e,this.layout=t,this.fields=Object.freeze(s.map(([c,l])=>Object.freeze([c,l]))),this.byteLength=i,this.wgsl=n,this.laidOut=o,this.table=a}offsetOf(e){return this.fieldLayout(e).offset}write(e,t,s){const i=this.regionStart(e,s);for(const o of Object.keys(t))if(!this.table.has(o))throw new h("E_INVALID_ARGUMENT",`UniformBlock ${this.name}: unknown field "${o}"`,{argument:"values",value:o,expected:this.laidOut.map(a=>a.name).join(", ")});const n=this.laidOut.map(o=>this.components(o,t[o.name]));for(let o=0;o<this.byteLength;o+=4)e.setUint32(i+o,0,!0);this.laidOut.forEach((o,a)=>{const c=n[a];c!==null&&c.forEach((l,d)=>{Nc(e,o.info.scalar,i+o.offset+4*d,l)})})}components(e,t){if(t===void 0)return null;const{scalar:s,width:i}=e.info;if(i===1)return Di(e.name,s,t),[t];if(!Array.isArray(t)||t.length!==i)throw new h("E_INVALID_ARGUMENT",`UniformBlock ${this.name}: field "${e.name}" expects ${i} components`,{argument:e.name,value:t,expected:`an array of ${i} numbers`});const n=[];for(let o=0;o<i;o++){const a=t[o];Di(e.name,s,a),n.push(a)}return n}read(e,t){const s=this.regionStart(e,t),i={};for(const n of this.laidOut)i[n.name]=this.decode(e,n,s);return i}readField(e,t,s){const i=this.fieldLayout(t);return this.decode(e,i,this.regionStart(e,s))}fieldLayout(e){const t=this.table.get(e);if(t===void 0)throw new h("E_INVALID_ARGUMENT",`UniformBlock ${this.name}: unknown field "${e}"`,{argument:"field",value:e,expected:this.laidOut.map(s=>s.name).join(", ")});return t}regionStart(e,t){const s=t??0;if(!Number.isInteger(s)||s<0||s+this.byteLength>e.byteLength)throw new h("E_INVALID_ARGUMENT",`UniformBlock ${this.name}: region [${s}, ${s+this.byteLength}) is outside the view`,{argument:"byteOffset",value:s,expected:`0 <= byteOffset <= ${e.byteLength-this.byteLength}`});return s}decode(e,t,s){const{scalar:i,width:n}=t.info;if(n===1)return Ti(e,i,s+t.offset);const o=[];for(let a=0;a<n;a++)o.push(Ti(e,i,s+t.offset+4*a));return o}}const Bc=`
|
|
225
|
+
var<workgroup> sh: array<u32, WG>; // the block's degrees, then their inclusive scan
|
|
226
|
+
var<workgroup> rowStart: array<u32, WG>; // the first bound arc of each entry's row
|
|
227
|
+
var<workgroup> base: u32; // the block's reserved span in the edge queue
|
|
228
|
+
var<workgroup> wcount: u32; // the frontier's length on a two-phase level, 0 on any other
|
|
229
|
+
|
|
230
|
+
@compute @workgroup_size(WG)
|
|
231
|
+
fn advance_expand(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
232
|
+
if (lid.x == 0u) { wcount = select(0u, atomicLoad(&counters[0]), atomicLoad(&counters[24]) == 1u); } // frontierCount, on the two-phase path only (the path word)
|
|
233
|
+
let count = workgroupUniformLoad(&wcount); // uniform: the block loop below holds barriers
|
|
234
|
+
for (var b0 = group_id(wid) * WG; b0 < count; b0 = b0 + P.stride) { // grid-stride over blocks of WG entries (a direct dispatch of the plan's groups)
|
|
235
|
+
let i = b0 + lid.x; // this lane's frontier entry
|
|
236
|
+
var deg = 0u;
|
|
237
|
+
var start = 0u;
|
|
238
|
+
if (i < count) { // guarded loads into locals (3.5 rule 1)
|
|
239
|
+
let v = frontierIn[i];
|
|
240
|
+
let lo = max(rowPtr[v], P.arcBase); // the row clipped to the bound window (P8-T12)
|
|
241
|
+
let hi = min(rowPtr[v + 1u], P.arcEnd);
|
|
242
|
+
start = lo;
|
|
243
|
+
deg = select(0u, hi - lo, hi > lo);
|
|
244
|
+
}
|
|
245
|
+
let inclusive = wg_scan_u32(deg, lid.x); // the prelude's inclusive scan in LANE order (P8-T1 Step 5); the twin
|
|
246
|
+
sh[lid.x] = inclusive; // sh is monotone in lid.x, the index the binary search walks
|
|
247
|
+
rowStart[lid.x] = start;
|
|
248
|
+
workgroupBarrier();
|
|
249
|
+
let aggregate = workgroupUniformLoad(&sh[WG - 1u]); // uniform; includes a barrier
|
|
250
|
+
if (lid.x == 0u) {
|
|
251
|
+
base = atomicAdd(&counters[8], aggregate); // edgeCount: ONE reservation per workgroup, not one per arc
|
|
252
|
+
atomicAdd(&counters[9], aggregate); // edgeCountUnclamped: the overflow detector (PD-23)
|
|
253
|
+
atomicAdd(&counters[2], aggregate); // frontierDegreeSum: what this level expanded (the inspect seam)
|
|
254
|
+
}
|
|
255
|
+
workgroupBarrier();
|
|
256
|
+
for (var p = lid.x; p < aggregate; p = p + WG) { // strip [0, aggregate): lane j takes j, j + WG, ...
|
|
257
|
+
var lo = 0u; // upper_bound: the first k with sh[k] > p owns arc p
|
|
258
|
+
var hi = WG;
|
|
259
|
+
loop {
|
|
260
|
+
if (lo >= hi) { break; }
|
|
261
|
+
let mid = (lo + hi) / 2u;
|
|
262
|
+
if (sh[mid] > p) { hi = mid; } else { lo = mid + 1u; }
|
|
263
|
+
}
|
|
264
|
+
let k = lo;
|
|
265
|
+
var exclusive = 0u;
|
|
266
|
+
if (k > 0u) { exclusive = sh[k - 1u]; }
|
|
267
|
+
let arc = rowStart[k] + (p - exclusive);
|
|
268
|
+
let q = base + p;
|
|
269
|
+
if (q < P.edgeCapacity) { edgeQueue[q] = colIdx[arc - P.arcBase]; } // the clamp of PD-23; the queue holds the target vertex only (PD-24)
|
|
270
|
+
}
|
|
271
|
+
workgroupBarrier(); // sh, rowStart and base are reused by the next block
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
`,$c=`
|
|
275
|
+
var<workgroup> tileA: array<f32, APSP_TILE * APSP_TILE>;
|
|
276
|
+
var<workgroup> tileB: array<f32, APSP_TILE * APSP_TILE>;
|
|
277
|
+
|
|
278
|
+
fn tile_row(block: vec2<u32>, c: u32) -> u32 { return block.x * APSP_TILE + c / APSP_TILE; }
|
|
279
|
+
fn tile_col(block: vec2<u32>, c: u32) -> u32 { return block.y * APSP_TILE + c % APSP_TILE; }
|
|
280
|
+
|
|
281
|
+
fn load_cell(block: vec2<u32>, c: u32) -> f32 {
|
|
282
|
+
let i = tile_row(block, c);
|
|
283
|
+
let j = tile_col(block, c);
|
|
284
|
+
if (i < P.n && j < P.n) { return dist[i * P.n + j]; }
|
|
285
|
+
return bitcast<f32>(P.infBits); // outside the matrix: no path through it
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
fn store_cell(block: vec2<u32>, c: u32, v: f32) {
|
|
289
|
+
let i = tile_row(block, c);
|
|
290
|
+
let j = tile_col(block, c);
|
|
291
|
+
if (i >= P.n || j >= P.n) { return; } // an edge tile stores nothing outside n x n
|
|
292
|
+
dist[i * P.n + j] = v;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@compute @workgroup_size(WG)
|
|
296
|
+
fn apsp_fw(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
297
|
+
let g = group_id(wid);
|
|
298
|
+
let r = P.round;
|
|
299
|
+
let others = max(P.blocks, 2u) - 1u; // the blocks of a strip, the pivot excluded
|
|
300
|
+
let cells = APSP_TILE * APSP_TILE;
|
|
301
|
+
var count = 1u;
|
|
302
|
+
var own = vec2<u32>(r, r); // the block this workgroup updates
|
|
303
|
+
var a = vec2<u32>(r, r); // staged in tileA
|
|
304
|
+
var b = vec2<u32>(r, r); // staged in tileB
|
|
305
|
+
if (PHASE == 1u) {
|
|
306
|
+
count = 2u * others;
|
|
307
|
+
let s = g % others;
|
|
308
|
+
let o = select(s, s + 1u, s >= r); // a strip skips the pivot block
|
|
309
|
+
own = select(vec2<u32>(o, r), vec2<u32>(r, o), g < others); // block row r first, then block column r
|
|
310
|
+
b = own;
|
|
311
|
+
}
|
|
312
|
+
if (PHASE == 2u) {
|
|
313
|
+
count = others * others;
|
|
314
|
+
let i = g / others;
|
|
315
|
+
let j = g % others;
|
|
316
|
+
own = vec2<u32>(select(i, i + 1u, i >= r), select(j, j + 1u, j >= r));
|
|
317
|
+
a = vec2<u32>(own.x, r); // the pivot-column block (i, r)
|
|
318
|
+
b = vec2<u32>(r, own.y); // the pivot-row block (r, j)
|
|
319
|
+
}
|
|
320
|
+
if (g >= count) { return; } // uniform: the workgroup id and uniforms only
|
|
321
|
+
|
|
322
|
+
for (var c = lid.x; c < cells; c = c + WG) {
|
|
323
|
+
tileA[c] = load_cell(a, c);
|
|
324
|
+
if (PHASE != 0u) { tileB[c] = load_cell(b, c); }
|
|
325
|
+
}
|
|
326
|
+
workgroupBarrier(); // every staged cell is visible
|
|
327
|
+
|
|
328
|
+
if (PHASE == 2u) {
|
|
329
|
+
for (var c = lid.x; c < cells; c = c + WG) {
|
|
330
|
+
let x = c / APSP_TILE;
|
|
331
|
+
let y = c % APSP_TILE;
|
|
332
|
+
var v = load_cell(own, c);
|
|
333
|
+
for (var kr = 0u; kr < APSP_TILE; kr = kr + 1u) {
|
|
334
|
+
v = min(v, tileA[x * APSP_TILE + kr] + tileB[kr * APSP_TILE + y]);
|
|
335
|
+
}
|
|
336
|
+
store_cell(own, c, v);
|
|
337
|
+
}
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
let pivotRow = PHASE == 1u && own.x == r; // block row r reads d[x][k] from the pivot
|
|
342
|
+
for (var k = 0u; k < APSP_TILE; k = k + 1u) {
|
|
343
|
+
for (var c = lid.x; c < cells; c = c + WG) {
|
|
344
|
+
let x = c / APSP_TILE;
|
|
345
|
+
let y = c % APSP_TILE;
|
|
346
|
+
if (PHASE == 0u) {
|
|
347
|
+
let via = tileA[x * APSP_TILE + k] + tileA[k * APSP_TILE + y];
|
|
348
|
+
if (via < tileA[c]) { tileA[c] = via; }
|
|
349
|
+
} else {
|
|
350
|
+
let left = select(tileB[x * APSP_TILE + k], tileA[x * APSP_TILE + k], pivotRow);
|
|
351
|
+
let right = select(tileA[k * APSP_TILE + y], tileB[k * APSP_TILE + y], pivotRow);
|
|
352
|
+
let via = left + right;
|
|
353
|
+
if (via < tileB[c]) { tileB[c] = via; }
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
workgroupBarrier(); // step k is complete before step k + 1 reads
|
|
357
|
+
}
|
|
358
|
+
for (var c = lid.x; c < cells; c = c + WG) {
|
|
359
|
+
store_cell(own, c, select(tileB[c], tileA[c], PHASE == 0u));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
`,Cc=`
|
|
363
|
+
@compute @workgroup_size(WG)
|
|
364
|
+
fn apsp_init(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
365
|
+
let u = linear_id(wid, lid.x);
|
|
366
|
+
if (u >= P.n) { return; }
|
|
367
|
+
let rowBase = u * P.n;
|
|
368
|
+
let end = rowPtr[u + 1u];
|
|
369
|
+
for (var a = rowPtr[u]; a < end; a = a + 1u) {
|
|
370
|
+
let v = colIdx[a];
|
|
371
|
+
let w = select(1.0, weights[a], HAS_WEIGHTS);
|
|
372
|
+
dist[rowBase + v] = min(dist[rowBase + v], w); // parallel arcs collapse to the cheapest
|
|
373
|
+
}
|
|
374
|
+
dist[rowBase + u] = 0.0; // last: a self-loop never displaces the zero
|
|
375
|
+
}
|
|
376
|
+
`,Mc=`
|
|
377
|
+
fn sigma_of(word: u32) -> f32 { // a stored count: u32, or f32 bits when SCALED
|
|
378
|
+
if (SCALED) { return bitcast<f32>(word); }
|
|
379
|
+
return f32(word);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
@compute @workgroup_size(WG)
|
|
383
|
+
fn bc_backward(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
384
|
+
for (var i = linear_id(wid, lid.x); i < P.count; i = i + P.stride) {
|
|
385
|
+
let t = S[P.start + i]; // s * n + w
|
|
386
|
+
let w = t % P.n;
|
|
387
|
+
let base = t - w; // s * n
|
|
388
|
+
let succ = depthK[t] + 1u;
|
|
389
|
+
let sw = sigma_of(sigmaK[t]);
|
|
390
|
+
let shift = select(0i, sigma_shift(levelMax[depthK[t]]), SCALED); // the scale step to the successors' depth
|
|
391
|
+
var acc = 0.0;
|
|
392
|
+
for (var a = rowPtr[w]; a < rowPtr[w + 1u]; a = a + 1u) {
|
|
393
|
+
let v = base + colIdx[a];
|
|
394
|
+
if (depthK[v] == succ) { // v is a successor of w for source s
|
|
395
|
+
acc = acc + ldexp(sw / sigma_of(sigmaK[v]), -shift) * (1.0 + deltaK[v]);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
deltaK[t] = acc; // written once per (w, s)
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
`,Gc=`
|
|
402
|
+
@compute @workgroup_size(WG)
|
|
403
|
+
fn bc_count(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
404
|
+
let level = atomicLoad(&counters[11]);
|
|
405
|
+
let start = ends[level + 1u]; // the boundary closed the claim-from level here
|
|
406
|
+
let count = atomicLoad(&counters[26]) - start; // stackTop: what the forward dispatch appended
|
|
407
|
+
let shift = sigma_shift(atomicLoad(&levelMax[level]));
|
|
408
|
+
for (var i = linear_id(wid, lid.x); i < count; i = i + P.stride) {
|
|
409
|
+
let t = S[start + i]; // s * n + x, at depth level + 1
|
|
410
|
+
let x = t % P.n;
|
|
411
|
+
let base = t - x; // s * n
|
|
412
|
+
var acc = 0.0;
|
|
413
|
+
for (var a = rowPtr[x]; a < rowPtr[x + 1u]; a = a + 1u) { // the in-arcs (v, x)
|
|
414
|
+
let v = base + colIdx[a];
|
|
415
|
+
if (depthK[v] == level) { acc = acc + bitcast<f32>(sigmaK[v]); } // every predecessor's paths, CSR order
|
|
416
|
+
}
|
|
417
|
+
let sigma = ldexp(acc, -shift);
|
|
418
|
+
let bits = bitcast<u32>(sigma);
|
|
419
|
+
sigmaK[t] = bits;
|
|
420
|
+
let exponent = (bits >> 23u) & 0xffu;
|
|
421
|
+
if (exponent == 0u || exponent == 0xffu) { atomicOr(&counters[27], 1u); } // out of f32's normal range
|
|
422
|
+
atomicMax(&levelMax[level + 1u], bits);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
`,zc=`
|
|
426
|
+
fn sigma_of(word: u32) -> f32 { // a stored count: u32, or f32 bits when SCALED
|
|
427
|
+
if (SCALED) { return bitcast<f32>(word); }
|
|
428
|
+
return f32(word);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
@compute @workgroup_size(WG)
|
|
432
|
+
fn bc_edge_gather(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
433
|
+
for (var a = linear_id(wid, lid.x); a < P.count; a = a + P.stride) {
|
|
434
|
+
var lo = 0u; // the row w with rowPtr[w] <= a < rowPtr[w + 1]
|
|
435
|
+
var hi = P.n;
|
|
436
|
+
loop {
|
|
437
|
+
if (lo >= hi) { break; }
|
|
438
|
+
let mid = (lo + hi) / 2u;
|
|
439
|
+
if (rowPtr[mid + 1u] <= a) { lo = mid + 1u; } else { hi = mid; }
|
|
440
|
+
}
|
|
441
|
+
let w = lo;
|
|
442
|
+
let nbr = colIdx[a];
|
|
443
|
+
var acc = arcScores[a];
|
|
444
|
+
for (var s = 0u; s < P.k; s = s + 1u) {
|
|
445
|
+
let base = s * P.n;
|
|
446
|
+
let dw = depthK[base + w];
|
|
447
|
+
if (dw != INVALID_INDEX && depthK[base + nbr] == dw + 1u) { // (w, nbr) is on a shortest path from s
|
|
448
|
+
let shift = select(0i, sigma_shift(levelMax[dw]), SCALED);
|
|
449
|
+
let ratio = ldexp(sigma_of(sigmaK[base + w]) / sigma_of(sigmaK[base + nbr]), -shift);
|
|
450
|
+
acc = acc + ratio * (1.0 + deltaK[base + nbr]);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
arcScores[a] = acc;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
`,Fc=`
|
|
457
|
+
@compute @workgroup_size(WG)
|
|
458
|
+
fn bc_finalize(@builtin(local_invocation_id) lid: vec3<u32>) {
|
|
459
|
+
if (lid.x != 0u) { return; } // one lane; no barrier follows (3.5 rule 1)
|
|
460
|
+
if (P.role == 1u) { // the seed of a batch
|
|
461
|
+
for (var i = 0u; i < P.k; i = i + 1u) {
|
|
462
|
+
let t = S[i];
|
|
463
|
+
depthK[t] = 0u; // the source is at depth 0
|
|
464
|
+
sigmaK[t] = select(1u, bitcast<u32>(1.0), SCALED); // with one shortest path, itself (f32 bits when SCALED)
|
|
465
|
+
}
|
|
466
|
+
levelMax[0] = bitcast<u32>(1.0); // the largest count at depth 0
|
|
467
|
+
ends[0] = 0u;
|
|
468
|
+
atomicStore(&counters[26], P.k); // stackTop: the seeds are the log's first k entries
|
|
469
|
+
atomicStore(&counters[27], 0u); // sigmaOverflow
|
|
470
|
+
atomicStore(&counters[11], U32_MAX); // level: the first boundary brings it to 0
|
|
471
|
+
atomicStore(&counters[15], 0u); // done
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
if (atomicLoad(&counters[15]) != 0u) { return; } // done: a no-op level the host recorded past the end
|
|
475
|
+
let level = atomicLoad(&counters[11]) + 1u;
|
|
476
|
+
let top = atomicLoad(&counters[26]);
|
|
477
|
+
ends[level + 1u] = top; // the level's entries end where the log ends now
|
|
478
|
+
let count = top - ends[level];
|
|
479
|
+
atomicStore(&counters[0], count); // frontierCount (the inspect seam reads it)
|
|
480
|
+
atomicStore(&counters[11], level);
|
|
481
|
+
atomicStore(&counters[15], select(0u, 1u, count == 0u)); // an empty level ends the batch
|
|
482
|
+
}
|
|
483
|
+
`,Wc=`
|
|
484
|
+
var<workgroup> sh: array<u32, WG>; // the block's degrees, then their inclusive scan
|
|
485
|
+
var<workgroup> rowStart: array<u32, WG>; // the first arc of each entry's row
|
|
486
|
+
var<workgroup> entryOf: array<u32, WG>; // each entry, s * n + u
|
|
487
|
+
var<workgroup> wstart: u32; // the level's first log index
|
|
488
|
+
var<workgroup> wcount: u32; // the level's entry count
|
|
489
|
+
var<workgroup> wwon: atomic<u32>; // the strip's winners
|
|
490
|
+
var<workgroup> wbase: u32; // where the strip's winners go in the log
|
|
491
|
+
|
|
492
|
+
@compute @workgroup_size(WG)
|
|
493
|
+
fn bc_forward(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
494
|
+
let level = atomicLoad(&counters[11]);
|
|
495
|
+
if (lid.x == 0u) {
|
|
496
|
+
let lo = ends[level];
|
|
497
|
+
wstart = lo;
|
|
498
|
+
wcount = ends[level + 1u] - lo;
|
|
499
|
+
}
|
|
500
|
+
let start = workgroupUniformLoad(&wstart);
|
|
501
|
+
let count = workgroupUniformLoad(&wcount); // uniform: the block loop below holds barriers
|
|
502
|
+
let next = level + 1u;
|
|
503
|
+
for (var b0 = group_id(wid) * WG; b0 < count; b0 = b0 + P.stride) { // grid-stride over blocks of WG entries
|
|
504
|
+
let i = b0 + lid.x;
|
|
505
|
+
var deg = 0u;
|
|
506
|
+
var first = 0u;
|
|
507
|
+
var entry = 0u;
|
|
508
|
+
if (i < count) { // guarded loads into locals (3.5 rule 1)
|
|
509
|
+
entry = S[start + i];
|
|
510
|
+
let u = entry % P.n;
|
|
511
|
+
first = rowPtr[u];
|
|
512
|
+
deg = rowPtr[u + 1u] - first;
|
|
513
|
+
}
|
|
514
|
+
sh[lid.x] = deg;
|
|
515
|
+
rowStart[lid.x] = first;
|
|
516
|
+
entryOf[lid.x] = entry;
|
|
517
|
+
workgroupBarrier();
|
|
518
|
+
for (var s = 1u; s < WG; s = s * 2u) { // Hillis-Steele inclusive scan of the degrees
|
|
519
|
+
var t = 0u;
|
|
520
|
+
if (lid.x >= s) { t = sh[lid.x - s]; }
|
|
521
|
+
workgroupBarrier();
|
|
522
|
+
sh[lid.x] = sh[lid.x] + t;
|
|
523
|
+
workgroupBarrier();
|
|
524
|
+
}
|
|
525
|
+
let aggregate = workgroupUniformLoad(&sh[WG - 1u]); // uniform; includes a barrier
|
|
526
|
+
for (var p0 = 0u; p0 < aggregate; p0 = p0 + WG) { // strip [0, aggregate) WG arcs at a time
|
|
527
|
+
let p = p0 + lid.x;
|
|
528
|
+
var won = false;
|
|
529
|
+
var claimed = 0u;
|
|
530
|
+
if (p < aggregate) {
|
|
531
|
+
var lo = 0u; // upper_bound: the first k with sh[k] > p owns arc p
|
|
532
|
+
var hi = WG;
|
|
533
|
+
loop {
|
|
534
|
+
if (lo >= hi) { break; }
|
|
535
|
+
let mid = (lo + hi) / 2u;
|
|
536
|
+
if (sh[mid] > p) { hi = mid; } else { lo = mid + 1u; }
|
|
537
|
+
}
|
|
538
|
+
let k = lo;
|
|
539
|
+
var exclusive = 0u;
|
|
540
|
+
if (k > 0u) { exclusive = sh[k - 1u]; }
|
|
541
|
+
let origin = entryOf[k]; // s * n + u
|
|
542
|
+
let x = (origin - (origin % P.n)) + colIdx[rowStart[k] + (p - exclusive)]; // s * n + x
|
|
543
|
+
if (atomicLoad(&depthK[x]) == INVALID_INDEX) { // the pre-check of design 16.1
|
|
544
|
+
won = atomicMin(&depthK[x], next) == INVALID_INDEX; // the claim: the one winner appends
|
|
545
|
+
}
|
|
546
|
+
if (!SCALED && atomicLoad(&depthK[x]) == next) { // the count: EVERY arc on a shortest path adds
|
|
547
|
+
let add = atomicLoad(&sigmaK[origin]);
|
|
548
|
+
let old = atomicAdd(&sigmaK[x], add);
|
|
549
|
+
if (old + add < old) { atomicOr(&counters[27], 1u); } // the u32 wrap, reported
|
|
550
|
+
}
|
|
551
|
+
claimed = x;
|
|
552
|
+
}
|
|
553
|
+
var slot = 0u;
|
|
554
|
+
if (won) { slot = atomicAdd(&wwon, 1u); }
|
|
555
|
+
workgroupBarrier();
|
|
556
|
+
if (lid.x == 0u) {
|
|
557
|
+
wbase = atomicAdd(&counters[26], atomicLoad(&wwon)); // stackTop: one global atomic per strip
|
|
558
|
+
atomicStore(&wwon, 0u);
|
|
559
|
+
}
|
|
560
|
+
workgroupBarrier();
|
|
561
|
+
if (won) { S[wbase + slot] = claimed; }
|
|
562
|
+
}
|
|
563
|
+
workgroupBarrier(); // sh, rowStart and entryOf are reused by the next block
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
`,Uc=`
|
|
567
|
+
var<workgroup> wlive: u32; // 1 when the level has entries
|
|
568
|
+
var<workgroup> wwon: atomic<u32>; // the strip's winners
|
|
569
|
+
var<workgroup> wbase: u32; // where the strip's winners go in the log
|
|
570
|
+
|
|
571
|
+
fn claim(x: u32, next: u32) -> bool {
|
|
572
|
+
if (atomicLoad(&depthK[x]) != INVALID_INDEX) { return false; } // the pre-check of design 16.1
|
|
573
|
+
return atomicMin(&depthK[x], next) == INVALID_INDEX;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
fn count_paths(origin: u32, x: u32, next: u32) {
|
|
577
|
+
if (!SCALED && atomicLoad(&depthK[x]) == next) { // every arc on a shortest path adds
|
|
578
|
+
let add = atomicLoad(&sigmaK[origin]);
|
|
579
|
+
let old = atomicAdd(&sigmaK[x], add);
|
|
580
|
+
if (old + add < old) { atomicOr(&counters[27], 1u); } // the u32 wrap, reported
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
@compute @workgroup_size(WG)
|
|
585
|
+
fn bc_forward_edge(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
586
|
+
let level = atomicLoad(&counters[11]);
|
|
587
|
+
if (lid.x == 0u) { wlive = select(0u, 1u, ends[level + 1u] > ends[level]); }
|
|
588
|
+
if (workgroupUniformLoad(&wlive) == 0u) { return; } // uniform: nothing below runs on an empty level
|
|
589
|
+
let next = level + 1u;
|
|
590
|
+
for (var s = 0u; s < P.k; s = s + 1u) {
|
|
591
|
+
let base = s * P.n;
|
|
592
|
+
for (var e0 = group_id(wid) * WG; e0 < P.count; e0 = e0 + P.stride) { // grid-stride over the edges
|
|
593
|
+
let e = e0 + lid.x;
|
|
594
|
+
var a = INVALID_INDEX; // the claims this lane won
|
|
595
|
+
var b = INVALID_INDEX;
|
|
596
|
+
if (e < P.count) {
|
|
597
|
+
let u = base + edgeSrc[e];
|
|
598
|
+
let x = base + edgeDst[e];
|
|
599
|
+
if (atomicLoad(&depthK[u]) == level) {
|
|
600
|
+
if (claim(x, next)) { a = x; }
|
|
601
|
+
count_paths(u, x, next);
|
|
602
|
+
}
|
|
603
|
+
if (UNDIRECTED) { // the other direction of an undirected edge
|
|
604
|
+
if (atomicLoad(&depthK[x]) == level) {
|
|
605
|
+
if (claim(u, next)) { b = u; }
|
|
606
|
+
count_paths(x, u, next);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
let mine = select(0u, 1u, a != INVALID_INDEX) + select(0u, 1u, b != INVALID_INDEX);
|
|
611
|
+
var slot = 0u;
|
|
612
|
+
if (mine != 0u) { slot = atomicAdd(&wwon, mine); }
|
|
613
|
+
workgroupBarrier();
|
|
614
|
+
if (lid.x == 0u) {
|
|
615
|
+
wbase = atomicAdd(&counters[26], atomicLoad(&wwon)); // stackTop: one global atomic per strip
|
|
616
|
+
atomicStore(&wwon, 0u);
|
|
617
|
+
}
|
|
618
|
+
workgroupBarrier();
|
|
619
|
+
if (a != INVALID_INDEX) {
|
|
620
|
+
S[wbase + slot] = a;
|
|
621
|
+
slot = slot + 1u;
|
|
622
|
+
}
|
|
623
|
+
if (b != INVALID_INDEX) { S[wbase + slot] = b; }
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
`,Vc=`
|
|
628
|
+
@compute @workgroup_size(WG)
|
|
629
|
+
fn bc_gather(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
630
|
+
for (var w = linear_id(wid, lid.x); w < P.n; w = w + P.stride) {
|
|
631
|
+
var acc = bc[w];
|
|
632
|
+
for (var s = 0u; s < P.k; s = s + 1u) {
|
|
633
|
+
acc = acc + deltaK[s * P.n + w];
|
|
634
|
+
}
|
|
635
|
+
bc[w] = acc;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
`,jc=`
|
|
639
|
+
fn relax(v: u32, nd: f32) {
|
|
640
|
+
var cur = atomicLoad(&dist[v]);
|
|
641
|
+
var tries = 0u;
|
|
642
|
+
loop {
|
|
643
|
+
if (!(nd < bitcast<f32>(cur))) { break; } // no improvement; +Inf is greater than every finite nd
|
|
644
|
+
let r = atomicCompareExchangeWeak(&dist[v], cur, bitcast<u32>(nd));
|
|
645
|
+
if (r.exchanged) { atomicStore(&flags[0], 1u); break; } // changed
|
|
646
|
+
if (r.old_value == cur) { // a spurious failure: the only kind the bound counts
|
|
647
|
+
tries = tries + 1u;
|
|
648
|
+
if (tries >= P.maxRetries) { atomicStore(&flags[1], 1u); break; } // retryExhausted (PD-12)
|
|
649
|
+
}
|
|
650
|
+
cur = r.old_value; // else another lane's improvement won (issue #470)
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
@compute @workgroup_size(WG)
|
|
655
|
+
fn bf_relax(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
656
|
+
let first = linear_id(wid, lid.x);
|
|
657
|
+
let cutoff = bitcast<f32>(P.cutoffBits);
|
|
658
|
+
for (var e = first; e < P.edgeCount; e = e + P.stride) { // each logical edge once (edgeList)
|
|
659
|
+
let u = edgeSrc[e];
|
|
660
|
+
let v = edgeDst[e];
|
|
661
|
+
let w = weights[edgeToArc[e]]; // the edge's weight through its forward arc
|
|
662
|
+
let du = atomicLoad(&dist[u]);
|
|
663
|
+
if (du != F32_INF_BITS) { // unreached is tested as the bit pattern, as sssp-pred does
|
|
664
|
+
let nd = bitcast<f32>(du) + w;
|
|
665
|
+
if (nd <= cutoff) { relax(v, nd); }
|
|
666
|
+
}
|
|
667
|
+
if (UNDIRECTED) { // the other direction of an undirected edge
|
|
668
|
+
let dv = atomicLoad(&dist[v]);
|
|
669
|
+
if (dv != F32_INF_BITS) {
|
|
670
|
+
let nd = bitcast<f32>(dv) + w;
|
|
671
|
+
if (nd <= cutoff) { relax(u, nd); }
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
`,qc=`
|
|
677
|
+
@compute @workgroup_size(WG)
|
|
678
|
+
fn bfs_bitset_build(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
679
|
+
let count = select(0u, atomicLoad(&counters[0]), atomicLoad(&counters[24]) == 3u); // frontierCount, on the bottom-up path only (the path word)
|
|
680
|
+
for (var i = linear_id(wid, lid.x); i < count; i = i + P.stride) { // grid-stride; no barrier anywhere
|
|
681
|
+
let v = frontierIn[i];
|
|
682
|
+
atomicOr(&bits[P.bitsBase + (v >> 5u)], 1u << (v & 31u));
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
`,Kc=`
|
|
686
|
+
var<workgroup> sh: array<u32, WG>;
|
|
687
|
+
var<workgroup> base: u32;
|
|
688
|
+
var<workgroup> wcount: u32; // the unvisited list's length on a bottom-up level, 0 on any other
|
|
689
|
+
|
|
690
|
+
@compute @workgroup_size(WG)
|
|
691
|
+
fn bfs_bottom_up(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
692
|
+
let claim = atomicLoad(&counters[11]) + 1u;
|
|
693
|
+
if (lid.x == 0u) { wcount = select(0u, atomicLoad(&counters[7]), atomicLoad(&counters[24]) == 3u); } // unvisitedListLen, on the bottom-up path only (the path word)
|
|
694
|
+
let len = workgroupUniformLoad(&wcount); // uniform: the block loop below holds barriers
|
|
695
|
+
for (var b0 = group_id(wid) * WG; b0 < len; b0 = b0 + P.stride) { // grid-stride over blocks of WG entries
|
|
696
|
+
let i = b0 + lid.x;
|
|
697
|
+
var won = 0u;
|
|
698
|
+
var v = 0u;
|
|
699
|
+
var reads = 0u;
|
|
700
|
+
if (i < len) { // guarded work into locals
|
|
701
|
+
v = sweepIn[i];
|
|
702
|
+
if (atomicLoad(&depth[v]) == INVALID_INDEX) { // a stale entry, claimed since the rebuild, is skipped
|
|
703
|
+
let end = min(rowPtr[v + 1u], P.arcEnd);
|
|
704
|
+
for (var a = max(rowPtr[v], P.arcBase); a < end; a = a + 1u) { // in-neighbours through the reverse core
|
|
705
|
+
reads = reads + 1u;
|
|
706
|
+
let u = colIdx[a - P.arcBase];
|
|
707
|
+
if (mask_bit(sweepIn[P.bitsBase + (u >> 5u)], u)) { won = 1u; break; } // the early exit: a real break, never a flag
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
sh[lid.x] = won;
|
|
712
|
+
workgroupBarrier();
|
|
713
|
+
for (var s = 1u; s < WG; s = s * 2u) { // Hillis-Steele inclusive scan of won (bfs-contract's)
|
|
714
|
+
var t = 0u;
|
|
715
|
+
if (lid.x >= s) { t = sh[lid.x - s]; }
|
|
716
|
+
workgroupBarrier();
|
|
717
|
+
sh[lid.x] = sh[lid.x] + t;
|
|
718
|
+
workgroupBarrier();
|
|
719
|
+
}
|
|
720
|
+
let inclusive = sh[lid.x];
|
|
721
|
+
let readsTotal = wg_reduce_u32(reads, lid.x, 0u); // arcsScanned, one atomic per workgroup (the sabotage witness, Step 6)
|
|
722
|
+
if (lid.x == WG - 1u) { base = atomicAdd(&counters[1], inclusive); }
|
|
723
|
+
if (lid.x == 0u) { atomicAdd(&counters[16], readsTotal); }
|
|
724
|
+
workgroupBarrier();
|
|
725
|
+
if (won == 1u) {
|
|
726
|
+
atomicStore(&depth[v], claim); // no claim race: the list holds v once and the sweep is vertex-parallel
|
|
727
|
+
frontierOut[base + inclusive - 1u] = v;
|
|
728
|
+
}
|
|
729
|
+
workgroupBarrier(); // sh and base are reused by the next block
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
`,Hc=`
|
|
733
|
+
var<workgroup> sh: array<u32, WG>;
|
|
734
|
+
var<workgroup> base: u32;
|
|
735
|
+
var<workgroup> wcount: u32; // the clamped edge count on a two-phase level, 0 on any other
|
|
736
|
+
|
|
737
|
+
@compute @workgroup_size(WG)
|
|
738
|
+
fn bfs_contract(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
739
|
+
if (lid.x == 0u) { wcount = select(0u, atomicLoad(&counters[8]), atomicLoad(&counters[24]) == 1u); } // edgeCount, clamped by role 1; the two-phase path only (the path word)
|
|
740
|
+
let count = workgroupUniformLoad(&wcount); // uniform: the block loop below holds barriers
|
|
741
|
+
let claim = atomicLoad(&counters[11]) + 1u; // the depth this level assigns
|
|
742
|
+
for (var b0 = group_id(wid) * WG; b0 < count; b0 = b0 + P.stride) { // grid-stride over blocks of WG entries
|
|
743
|
+
let i = b0 + lid.x;
|
|
744
|
+
var won = 0u;
|
|
745
|
+
var v = 0u;
|
|
746
|
+
if (i < count) { // guarded work into locals
|
|
747
|
+
v = edgeQueue[i];
|
|
748
|
+
let old = atomicMin(&depth[v], claim);
|
|
749
|
+
won = select(0u, 1u, old == INVALID_INDEX); // PD-6: the unique winner
|
|
750
|
+
}
|
|
751
|
+
sh[lid.x] = won;
|
|
752
|
+
workgroupBarrier();
|
|
753
|
+
for (var s = 1u; s < WG; s = s * 2u) { // Hillis-Steele inclusive scan of won
|
|
754
|
+
var t = 0u;
|
|
755
|
+
if (lid.x >= s) { t = sh[lid.x - s]; }
|
|
756
|
+
workgroupBarrier();
|
|
757
|
+
sh[lid.x] = sh[lid.x] + t;
|
|
758
|
+
workgroupBarrier();
|
|
759
|
+
}
|
|
760
|
+
let inclusive = sh[lid.x];
|
|
761
|
+
if (lid.x == WG - 1u) { base = atomicAdd(&counters[1], inclusive); } // nextFrontierCount: one reservation per workgroup
|
|
762
|
+
workgroupBarrier();
|
|
763
|
+
if (won == 1u) { frontierOut[base + inclusive - 1u] = v; }
|
|
764
|
+
workgroupBarrier(); // sh and base are reused by the next block
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
`,Yc=`
|
|
768
|
+
var<workgroup> sh: array<u32, WG>;
|
|
769
|
+
var<workgroup> wdeg: u32;
|
|
770
|
+
var<workgroup> wstart: u32;
|
|
771
|
+
var<workgroup> base: u32;
|
|
772
|
+
var<workgroup> wcount: u32; // the frontier's length on a fused or retry level, 0 on any other
|
|
773
|
+
|
|
774
|
+
@compute @workgroup_size(WG)
|
|
775
|
+
fn bfs_fused(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
776
|
+
let claim = atomicLoad(&counters[11]) + 1u;
|
|
777
|
+
if (lid.x == 0u) {
|
|
778
|
+
let path = atomicLoad(&counters[24]); // 2 the fused level, 4 the overflow retry (PD-23)
|
|
779
|
+
wcount = select(0u, atomicLoad(&counters[0]), path == 2u || path == 4u);
|
|
780
|
+
}
|
|
781
|
+
let count = workgroupUniformLoad(&wcount); // uniform: the entry loop below holds barriers
|
|
782
|
+
for (var g = group_id(wid); g < count; g = g + P.stride) { // one workgroup per frontier entry; P.stride is the dispatch's GROUP count
|
|
783
|
+
if (lid.x == 0u) {
|
|
784
|
+
let u = frontierIn[g];
|
|
785
|
+
let a0 = max(rowPtr[u], P.arcBase);
|
|
786
|
+
let a1 = min(rowPtr[u + 1u], P.arcEnd);
|
|
787
|
+
let d = select(0u, a1 - a0, a1 > a0);
|
|
788
|
+
wdeg = d;
|
|
789
|
+
wstart = a0;
|
|
790
|
+
atomicAdd(&counters[2], d); // frontierDegreeSum, so the inspect seam sees fused levels too
|
|
791
|
+
}
|
|
792
|
+
let deg = workgroupUniformLoad(&wdeg); // uniform: the loop below may hold barriers
|
|
793
|
+
let start = workgroupUniformLoad(&wstart);
|
|
794
|
+
for (var p0 = 0u; p0 < deg; p0 = p0 + WG) { // strip the row WG arcs at a time
|
|
795
|
+
let p = p0 + lid.x;
|
|
796
|
+
var won = 0u;
|
|
797
|
+
var v = 0u;
|
|
798
|
+
if (p < deg) { // guarded claim into locals
|
|
799
|
+
v = colIdx[start + p - P.arcBase];
|
|
800
|
+
let old = atomicMin(&depth[v], claim);
|
|
801
|
+
won = select(0u, 1u, old == INVALID_INDEX);
|
|
802
|
+
}
|
|
803
|
+
sh[lid.x] = won;
|
|
804
|
+
workgroupBarrier();
|
|
805
|
+
for (var s = 1u; s < WG; s = s * 2u) { // Hillis-Steele inclusive scan of won (bfs-contract's, verbatim)
|
|
806
|
+
var t = 0u;
|
|
807
|
+
if (lid.x >= s) { t = sh[lid.x - s]; }
|
|
808
|
+
workgroupBarrier();
|
|
809
|
+
sh[lid.x] = sh[lid.x] + t;
|
|
810
|
+
workgroupBarrier();
|
|
811
|
+
}
|
|
812
|
+
let inclusive = sh[lid.x];
|
|
813
|
+
if (lid.x == WG - 1u) { base = atomicAdd(&counters[1], inclusive); }
|
|
814
|
+
workgroupBarrier();
|
|
815
|
+
if (won == 1u) { frontierOut[base + inclusive - 1u] = v; }
|
|
816
|
+
workgroupBarrier(); // sh and base are reused by the next strip
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
`,Xc=`
|
|
821
|
+
@compute @workgroup_size(WG)
|
|
822
|
+
fn bfs_next_degree(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
823
|
+
let count = select(0u, atomicLoad(&counters[1]), atomicLoad(&counters[24]) != 0u); // nextFrontierCount, on a level that claimed (the path word)
|
|
824
|
+
var sum = 0u;
|
|
825
|
+
var inSum = 0u;
|
|
826
|
+
for (var i = linear_id(wid, lid.x); i < count; i = i + P.stride) { // no barrier inside: the trip count is per lane
|
|
827
|
+
let v = frontier[i];
|
|
828
|
+
sum = sum + outDegree[v];
|
|
829
|
+
inSum = inSum + inDegree[v];
|
|
830
|
+
}
|
|
831
|
+
let total = wg_reduce_u32(sum, lid.x, 0u); // the prelude's workgroup sum; uniform: after the loop
|
|
832
|
+
let inTotal = wg_reduce_u32(inSum, lid.x, 0u);
|
|
833
|
+
if (lid.x == 0u) {
|
|
834
|
+
atomicAdd(&counters[25], total); // nextDegreeSum: ONE atomic per workgroup
|
|
835
|
+
atomicSub(&counters[6], inTotal); // unvisitedDegreeSum: the claimed vertices' in-arcs leave it (issue #1358)
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
`,Qc=`
|
|
839
|
+
@compute @workgroup_size(WG)
|
|
840
|
+
fn bfs_unvisited_flags(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
841
|
+
let first = linear_id(wid, lid.x);
|
|
842
|
+
var cnt = 0u;
|
|
843
|
+
var degSum = 0u;
|
|
844
|
+
var len = 0u;
|
|
845
|
+
for (var v = first; v < P.n; v = v + P.stride) { // no barrier inside: the trip count is per lane
|
|
846
|
+
let unv = depth[v] == INVALID_INDEX;
|
|
847
|
+
let listed = unv && (inDegree[v] != 0u);
|
|
848
|
+
flags[v] = select(0u, 1u, listed);
|
|
849
|
+
cnt = cnt + select(0u, 1u, unv);
|
|
850
|
+
degSum = degSum + select(0u, inDegree[v], unv); // the IN-degree: what a bottom-up sweep reads at most (issue #1358)
|
|
851
|
+
len = len + select(0u, 1u, listed);
|
|
852
|
+
}
|
|
853
|
+
let c = wg_reduce_u32(cnt, lid.x, 0u); // the prelude's workgroup sum (combine_u's sum code); uniform: after the loop
|
|
854
|
+
let d = wg_reduce_u32(degSum, lid.x, 0u);
|
|
855
|
+
let l = wg_reduce_u32(len, lid.x, 0u);
|
|
856
|
+
if (lid.x == 0u) { // ONE atomic per word per workgroup
|
|
857
|
+
atomicAdd(&counters[5], c);
|
|
858
|
+
atomicAdd(&counters[6], d);
|
|
859
|
+
atomicAdd(&counters[7], l);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
`,Zc=`
|
|
863
|
+
const max_words: u32 = 8u;
|
|
864
|
+
var<workgroup> tally: array<atomic<u32>, 32u * max_words>; // 32 x max_words: this workgroup's claims per source lane
|
|
865
|
+
var<workgroup> wlive: u32;
|
|
866
|
+
var<workgroup> wpull: u32;
|
|
867
|
+
var<workgroup> wany: atomic<u32>;
|
|
868
|
+
var<workgroup> warcs: atomic<u32>;
|
|
869
|
+
var<workgroup> wover: atomic<u32>;
|
|
870
|
+
|
|
871
|
+
fn dead_lanes(j: u32) -> u32 { // the lanes of word j at or past the batch's source count
|
|
872
|
+
let first = 32u * j;
|
|
873
|
+
if (P.count >= first + 32u) { return 0u; }
|
|
874
|
+
if (P.count <= first) { return U32_MAX; }
|
|
875
|
+
return ~((1u << (P.count - first)) - 1u);
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
fn add_arcs(slot: u32, value: u32) { // the arcs total of a control slot; pull once it passes P.pullAt
|
|
879
|
+
if (value == 0u) { return; }
|
|
880
|
+
let before = atomicAdd(&table[slot + 1u], value);
|
|
881
|
+
if (value > P.pullAt || before + value > P.pullAt || before + value < before) {
|
|
882
|
+
atomicStore(&table[slot + 2u], 1u);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
fn record(j: u32, node: u32, fresh: u32, dist: u32) { // the claims of one word: tallied per lane, a sampled run's sums
|
|
887
|
+
if (P.perNode == 1u) { atomicAdd(&bits[4u * P.base + node], countOneBits(fresh) * dist); }
|
|
888
|
+
var b = fresh;
|
|
889
|
+
loop {
|
|
890
|
+
if (b == 0u) { break; }
|
|
891
|
+
atomicAdd(&tally[32u * j + firstTrailingBit(b)], 1u);
|
|
892
|
+
b = b & (b - 1u);
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
@compute @workgroup_size(WG)
|
|
897
|
+
fn closeness_level(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
898
|
+
let i = linear_id(wid, lid.x);
|
|
899
|
+
let W = P.words;
|
|
900
|
+
if (P.role == 1u) { // seed, part 1: one invocation per word
|
|
901
|
+
if (i == 0u) {
|
|
902
|
+
for (var k = 0u; k < 12u; k = k + 1u) { atomicStore(&table[P.ctrl + k], 0u); }
|
|
903
|
+
}
|
|
904
|
+
if (i < P.total) { atomicStore(&bits[i], select(0u, dead_lanes(i % W), i < P.base)); }
|
|
905
|
+
return; // uniform: P.role is
|
|
906
|
+
}
|
|
907
|
+
if (P.role == 2u) { // seed, part 2: one invocation per source lane
|
|
908
|
+
if (i < P.count) {
|
|
909
|
+
var v = P.source + i;
|
|
910
|
+
if (P.sourcesAt != 0u) { v = atomicLoad(&table[P.sourcesAt + P.source + i]); }
|
|
911
|
+
let w = v * W + i / 32u;
|
|
912
|
+
let bit = 1u << (i % 32u);
|
|
913
|
+
atomicOr(&bits[w], bit); // visited
|
|
914
|
+
let before = atomicOr(&bits[P.base + w], bit); // level 0's frontier (region 1)
|
|
915
|
+
let slot = P.ctrl + 8u; // the slot of "level -1", which level 0 reads
|
|
916
|
+
atomicStore(&table[slot], 1u);
|
|
917
|
+
if (before == 0u) { add_arcs(slot, rowPtr[v + 1u] - rowPtr[v]); }
|
|
918
|
+
}
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// role 0: one level
|
|
923
|
+
let L = P.level;
|
|
924
|
+
let prev = P.ctrl + 4u * ((L + 2u) % 3u);
|
|
925
|
+
let cur = P.ctrl + 4u * (L % 3u);
|
|
926
|
+
if (lid.x == 0u) {
|
|
927
|
+
wlive = atomicLoad(&table[prev]);
|
|
928
|
+
wpull = select(0u, atomicLoad(&table[prev + 2u]), P.pullOk == 1u);
|
|
929
|
+
atomicStore(&wany, 0u);
|
|
930
|
+
atomicStore(&warcs, 0u);
|
|
931
|
+
atomicStore(&wover, 0u);
|
|
932
|
+
}
|
|
933
|
+
for (var k = lid.x; k < 32u * W; k = k + WG) { atomicStore(&tally[k], 0u); }
|
|
934
|
+
let live = workgroupUniformLoad(&wlive); // uniform; includes a barrier
|
|
935
|
+
if (live == 0u) { return; } // the previous level claimed nothing
|
|
936
|
+
let pull = workgroupUniformLoad(&wpull) == 1u;
|
|
937
|
+
if (i == 0u) { // the slot of level L + 1 held level L - 2's
|
|
938
|
+
let nxt = P.ctrl + 4u * ((L + 1u) % 3u);
|
|
939
|
+
atomicStore(&table[nxt], 0u);
|
|
940
|
+
atomicStore(&table[nxt + 1u], 0u);
|
|
941
|
+
atomicStore(&table[nxt + 2u], 0u);
|
|
942
|
+
}
|
|
943
|
+
let frontierBase = P.base * (1u + L % 3u);
|
|
944
|
+
let nextBase = P.base * (1u + (L + 1u) % 3u);
|
|
945
|
+
let staleBase = P.base * (1u + (L + 2u) % 3u);
|
|
946
|
+
let dist = L + 1u;
|
|
947
|
+
var arcs = 0u;
|
|
948
|
+
if (i < P.n) {
|
|
949
|
+
for (var j = 0u; j < W; j = j + 1u) { atomicStore(&bits[staleBase + i * W + j], 0u); }
|
|
950
|
+
}
|
|
951
|
+
if (pull) {
|
|
952
|
+
if (i < P.n) { // one invocation per node: its in-arcs
|
|
953
|
+
var vis: array<u32, max_words>;
|
|
954
|
+
var unseen = 0u;
|
|
955
|
+
for (var j = 0u; j < W; j = j + 1u) {
|
|
956
|
+
vis[j] = atomicLoad(&bits[i * W + j]);
|
|
957
|
+
unseen = unseen | ~vis[j];
|
|
958
|
+
}
|
|
959
|
+
if (unseen != 0u) { // some source has not reached this node yet
|
|
960
|
+
var acc: array<u32, max_words>;
|
|
961
|
+
let end = inRowPtr[i + 1u];
|
|
962
|
+
for (var a = inRowPtr[i]; a < end; a = a + 1u) {
|
|
963
|
+
let u = inColIdx[a];
|
|
964
|
+
var missing = 0u;
|
|
965
|
+
for (var j = 0u; j < W; j = j + 1u) {
|
|
966
|
+
acc[j] = acc[j] | atomicLoad(&bits[frontierBase + u * W + j]);
|
|
967
|
+
missing = missing | ~(acc[j] | vis[j]);
|
|
968
|
+
}
|
|
969
|
+
if (missing == 0u) { break; } // every source has reached it: the early exit
|
|
970
|
+
}
|
|
971
|
+
let degree = rowPtr[i + 1u] - rowPtr[i];
|
|
972
|
+
for (var j = 0u; j < W; j = j + 1u) {
|
|
973
|
+
let fresh = acc[j] & ~vis[j];
|
|
974
|
+
if (fresh != 0u) {
|
|
975
|
+
atomicStore(&bits[i * W + j], vis[j] | fresh);
|
|
976
|
+
atomicStore(&bits[nextBase + i * W + j], fresh);
|
|
977
|
+
arcs = arcs + degree;
|
|
978
|
+
record(j, i, fresh, dist);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
} else if (i < P.arcCount) { // one invocation per arc: no row is walked whole
|
|
984
|
+
var lo = 0u; // the arc's source: the last row starting at or before it
|
|
985
|
+
var hi = P.n - 1u;
|
|
986
|
+
loop {
|
|
987
|
+
if (lo >= hi) { break; }
|
|
988
|
+
let mid = (lo + hi + 1u) / 2u;
|
|
989
|
+
if (rowPtr[mid] <= i) { lo = mid; } else { hi = mid - 1u; }
|
|
990
|
+
}
|
|
991
|
+
let u = lo;
|
|
992
|
+
let x = colIdx[i];
|
|
993
|
+
for (var j = 0u; j < W; j = j + 1u) {
|
|
994
|
+
let mask = atomicLoad(&bits[frontierBase + u * W + j]) & ~atomicLoad(&bits[x * W + j]); // at u, not yet at x
|
|
995
|
+
if (mask == 0u) { continue; }
|
|
996
|
+
let fresh = mask & ~atomicOr(&bits[x * W + j], mask); // the claims this invocation won
|
|
997
|
+
if (fresh == 0u) { continue; }
|
|
998
|
+
if (atomicOr(&bits[nextBase + x * W + j], fresh) == 0u) {
|
|
999
|
+
arcs = arcs + (rowPtr[x + 1u] - rowPtr[x]);
|
|
1000
|
+
}
|
|
1001
|
+
record(j, x, fresh, dist);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
if (arcs > P.pullAt) {
|
|
1005
|
+
atomicStore(&wover, 1u);
|
|
1006
|
+
} else if (arcs != 0u) {
|
|
1007
|
+
let before = atomicAdd(&warcs, arcs);
|
|
1008
|
+
if (before + arcs > P.pullAt || before + arcs < before) { atomicStore(&wover, 1u); }
|
|
1009
|
+
}
|
|
1010
|
+
workgroupBarrier();
|
|
1011
|
+
let row = P.row * 32u * W;
|
|
1012
|
+
for (var k = lid.x; k < 32u * W; k = k + WG) { // ONE global atomic per source lane per workgroup
|
|
1013
|
+
let c = atomicLoad(&tally[k]);
|
|
1014
|
+
if (c != 0u) {
|
|
1015
|
+
atomicAdd(&table[row + k], c);
|
|
1016
|
+
atomicStore(&wany, 1u);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
workgroupBarrier();
|
|
1020
|
+
if (lid.x == 0u) {
|
|
1021
|
+
if (atomicLoad(&wany) != 0u) { atomicStore(&table[cur], 1u); }
|
|
1022
|
+
if (atomicLoad(&wover) != 0u) {
|
|
1023
|
+
atomicStore(&table[cur + 2u], 1u);
|
|
1024
|
+
} else {
|
|
1025
|
+
add_arcs(cur, atomicLoad(&warcs));
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
`,Jc=`
|
|
1030
|
+
var<workgroup> partial: array<u32, WG>;
|
|
1031
|
+
|
|
1032
|
+
@compute @workgroup_size(WG)
|
|
1033
|
+
fn closeness_rowsum(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1034
|
+
let row = group_id(wid); // uniform: one workgroup per row
|
|
1035
|
+
if (row >= P.n) { return; }
|
|
1036
|
+
var whole = 0u;
|
|
1037
|
+
var real = 0.0;
|
|
1038
|
+
for (var c = lid.x; c < P.n; c = c + WG) {
|
|
1039
|
+
let d = dist[row * P.n + c];
|
|
1040
|
+
if (c == row || bitcast<u32>(d) == F32_INF_BITS) { continue; }
|
|
1041
|
+
if (P.role == 0u) {
|
|
1042
|
+
whole = whole + u32(d);
|
|
1043
|
+
} else if (P.role == 1u) {
|
|
1044
|
+
real = real + d;
|
|
1045
|
+
} else if (d > 0.0) {
|
|
1046
|
+
real = real + 1.0 / d; // a zero distance adds nothing, as on the CPU
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
partial[lid.x] = select(whole, bitcast<u32>(real), P.role != 0u);
|
|
1050
|
+
for (var s = WG / 2u; s > 0u; s = s / 2u) {
|
|
1051
|
+
workgroupBarrier();
|
|
1052
|
+
if (lid.x < s) {
|
|
1053
|
+
let a = partial[lid.x];
|
|
1054
|
+
let b = partial[lid.x + s];
|
|
1055
|
+
partial[lid.x] = select(a + b, bitcast<u32>(bitcast<f32>(a) + bitcast<f32>(b)), P.role != 0u);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
if (lid.x == 0u) { out[row] = partial[0]; }
|
|
1059
|
+
}
|
|
1060
|
+
`,el=`
|
|
1061
|
+
@compute @workgroup_size(WG)
|
|
1062
|
+
fn compact_scatter(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1063
|
+
let i = linear_id(wid, lid.x);
|
|
1064
|
+
if (i == 0u) { outCount[P.outIndex] = offsets[P.count - 1u] + flags[P.count - 1u]; } // the exclusive scan's total; the planner never dispatches for count 0
|
|
1065
|
+
if (i >= P.count) { return; } // no barrier follows
|
|
1066
|
+
if (flags[i] != 0u) { out[offsets[i]] = queue[i]; }
|
|
1067
|
+
}
|
|
1068
|
+
`,tl=`
|
|
1069
|
+
@compute @workgroup_size(WG)
|
|
1070
|
+
fn coo_emit(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1071
|
+
let i = linear_id(wid, lid.x);
|
|
1072
|
+
if (i >= P.count) { return; } // no barrier follows
|
|
1073
|
+
var a = i;
|
|
1074
|
+
if (INDEXED) { a = order[i]; } // the arc this position takes
|
|
1075
|
+
let e = a / 2u; // arc 2e is edge e as declared, arc 2e + 1 its reverse
|
|
1076
|
+
let forward = (a % 2u) == 0u;
|
|
1077
|
+
let u = edgeSrc[e];
|
|
1078
|
+
let v = edgeDst[e];
|
|
1079
|
+
var w = 1.0;
|
|
1080
|
+
if (WEIGHTED) { w = edgeWeight[e]; }
|
|
1081
|
+
if (u == v) { // a self-loop is dropped: both arcs sort last and never open a run
|
|
1082
|
+
outSrc[i] = INVALID_INDEX;
|
|
1083
|
+
outDst[i] = INVALID_INDEX;
|
|
1084
|
+
outWeight[i] = 0.0;
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
outSrc[i] = select(v, u, forward);
|
|
1088
|
+
outDst[i] = select(u, v, forward);
|
|
1089
|
+
outWeight[i] = w;
|
|
1090
|
+
}
|
|
1091
|
+
`,rl=`
|
|
1092
|
+
@compute @workgroup_size(WG)
|
|
1093
|
+
fn coo_scatter(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1094
|
+
let i = linear_id(wid, lid.x);
|
|
1095
|
+
if (i >= P.count) { return; } // no barrier follows
|
|
1096
|
+
let s = src[i];
|
|
1097
|
+
var slot = 0u;
|
|
1098
|
+
if (SORTED_INPUT) {
|
|
1099
|
+
if (i > 0u && s < src[i - 1u]) { atomicStore(&cursors[0], 1u); } // unsorted input: the flag the driver refuses
|
|
1100
|
+
let within = i - rowPtr[s]; // the arc's place in its row
|
|
1101
|
+
slot = rowPtr[s] + within;
|
|
1102
|
+
} else {
|
|
1103
|
+
slot = rowPtr[s] + atomicAdd(&cursors[s], 1u); // race order: the row is not sorted in this mode
|
|
1104
|
+
}
|
|
1105
|
+
colIdx[slot] = dst[i];
|
|
1106
|
+
if (WEIGHTED) { outWeight[slot] = weight[i]; }
|
|
1107
|
+
}
|
|
1108
|
+
`,sl=`
|
|
1109
|
+
@compute @workgroup_size(WG)
|
|
1110
|
+
fn counting_scatter(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1111
|
+
let i = linear_id(wid, lid.x);
|
|
1112
|
+
if (i >= P.count) { return; } // no barrier follows
|
|
1113
|
+
let k = keys[i];
|
|
1114
|
+
let slot = atomicAdd(&cursor[k], 1u); // the per-bin cursor (6 row 5)
|
|
1115
|
+
outIndex[start[k] + slot] = i;
|
|
1116
|
+
}
|
|
1117
|
+
`,il=`
|
|
1118
|
+
@compute @workgroup_size(WG)
|
|
1119
|
+
fn dedupe_claim(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1120
|
+
var count = P.count;
|
|
1121
|
+
if (P.countIndex != U32_MAX) { count = min(atomicLoad(&counters[P.countIndex]), P.count); } // a device-side count, clamped to the capacity
|
|
1122
|
+
for (var i = linear_id(wid, lid.x); i < count; i = i + P.stride) { // grid-stride; no barrier anywhere
|
|
1123
|
+
atomicStore(&owner[queue[i]], i);
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
`,nl=`
|
|
1127
|
+
var<workgroup> sh: array<u32, WG>;
|
|
1128
|
+
var<workgroup> base: u32;
|
|
1129
|
+
var<workgroup> wcount: u32;
|
|
1130
|
+
|
|
1131
|
+
@compute @workgroup_size(WG)
|
|
1132
|
+
fn dedupe_filter(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1133
|
+
if (lid.x == 0u) {
|
|
1134
|
+
var count = P.count;
|
|
1135
|
+
if (P.countIndex != U32_MAX) { count = min(atomicLoad(&outCount[P.countIndex]), P.count); } // the same count source as the claim
|
|
1136
|
+
wcount = count;
|
|
1137
|
+
}
|
|
1138
|
+
let count = workgroupUniformLoad(&wcount); // uniform: the block loop below holds barriers
|
|
1139
|
+
for (var b0 = group_id(wid) * WG; b0 < count; b0 = b0 + P.stride) { // grid-stride over blocks of WG entries
|
|
1140
|
+
let i = b0 + lid.x;
|
|
1141
|
+
var keep = 0u;
|
|
1142
|
+
var v = 0u;
|
|
1143
|
+
if (i < count) { v = queue[i]; keep = select(0u, 1u, atomicLoad(&owner[v]) == i); } // guarded work into locals
|
|
1144
|
+
sh[lid.x] = keep;
|
|
1145
|
+
workgroupBarrier(); // every lane, unconditionally (3.5 rule 1)
|
|
1146
|
+
for (var s = 1u; s < WG; s = s * 2u) { // Hillis-Steele inclusive scan of keep
|
|
1147
|
+
var t = 0u;
|
|
1148
|
+
if (lid.x >= s) { t = sh[lid.x - s]; }
|
|
1149
|
+
workgroupBarrier();
|
|
1150
|
+
sh[lid.x] = sh[lid.x] + t;
|
|
1151
|
+
workgroupBarrier();
|
|
1152
|
+
}
|
|
1153
|
+
let inclusive = sh[lid.x];
|
|
1154
|
+
if (lid.x == WG - 1u) { base = atomicAdd(&outCount[P.outIndex], inclusive); } // ONE atomic per workgroup: the block's aggregate
|
|
1155
|
+
workgroupBarrier();
|
|
1156
|
+
if (keep == 1u) { out[base + inclusive - 1u] = v; }
|
|
1157
|
+
workgroupBarrier(); // sh and base are reused by the next block
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
`,ol=`
|
|
1161
|
+
@compute @workgroup_size(WG)
|
|
1162
|
+
fn degree(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1163
|
+
let row = linear_id(wid, lid.x) + P.start;
|
|
1164
|
+
if (row >= P.end) { return; }
|
|
1165
|
+
let i = select(row, perm[row], USE_PERM);
|
|
1166
|
+
let a0 = max(rowPtr[i], P.arcBase);
|
|
1167
|
+
let a1 = min(rowPtr[i + 1u], P.arcEnd);
|
|
1168
|
+
var d = 0u;
|
|
1169
|
+
for (var arc = a0; arc < a1; arc = arc + 1u) {
|
|
1170
|
+
let nbr = colIdx[arc - P.arcBase]; // \`target\` is a WGSL reserved word (spec 16.2); never use it as an identifier
|
|
1171
|
+
d = d + select(0u, 1u, nbr < P.n);
|
|
1172
|
+
}
|
|
1173
|
+
out[i] = select(d, out[i] + d, P.accumulate == 1u);
|
|
1174
|
+
}
|
|
1175
|
+
`,al=`fn store_force(i: u32, f: vec3f) {
|
|
1176
|
+
force[3u * i] = f.x;
|
|
1177
|
+
force[3u * i + 1u] = f.y;
|
|
1178
|
+
force[3u * i + 2u] = f.z;
|
|
1179
|
+
}
|
|
1180
|
+
fn load_force(i: u32) -> vec3f { return vec3f(force[3u * i], force[3u * i + 1u], force[3u * i + 2u]); }
|
|
1181
|
+
fn row_node(row: u32) -> u32 { return select(row, perm[row], USE_PERM); }
|
|
1182
|
+
fn row_force(i: u32, lane: u32, step: u32) -> vec3f { // the arcs of row i this lane walks inside the bound window [P.arcBase, P.arcEnd) (4.2)
|
|
1183
|
+
let pi = pos[i]; // xyz + mass in one load (D23)
|
|
1184
|
+
let a0 = max(rowPtr[i], P.arcBase);
|
|
1185
|
+
let a1 = min(rowPtr[i + 1u], P.arcEnd);
|
|
1186
|
+
var f = vec3f(0.0);
|
|
1187
|
+
for (var a = a0 + lane; a < a1; a = a + step) {
|
|
1188
|
+
let j = colIdx[a - P.arcBase];
|
|
1189
|
+
if (j == i) { continue; } // a self-loop exerts no force
|
|
1190
|
+
var w = 1.0;
|
|
1191
|
+
if (HAS_WEIGHTS) { w = weights[a - P.arcBase]; }
|
|
1192
|
+
let d = pos[j].xyz - pi.xyz; // toward j
|
|
1193
|
+
let len = max(length(d), FA2_DIST_FLOOR);
|
|
1194
|
+
if (LAW == 1u) { w = length(d) / P.frK; } // LAW 1 (FR, 7.20): |F| = d^2 / k along d / d, unfloored; the linear select below applies w as is
|
|
1195
|
+
if (LAW == 2u) { w = P.springCoefficient * (len - P.springLength) / len; } // LAW 2 (spring, ngraph generateCreateSpringForce.js:33-36): Hooke k_s (d - L) toward j
|
|
1196
|
+
let mag = select(w, w * log(1.0 + len) / len, LINLOG); // linear: |F| = w len; linlog: |F| = w log(1 + len)
|
|
1197
|
+
f = f + d * mag;
|
|
1198
|
+
}
|
|
1199
|
+
return f;
|
|
1200
|
+
}
|
|
1201
|
+
fn row_force_dense(i: u32) -> vec3f { // TIER 0's stride-one twin of row_force (see the header)
|
|
1202
|
+
let pi = pos[i];
|
|
1203
|
+
let lo = max(rowPtr[i], P.arcBase);
|
|
1204
|
+
let hi = min(rowPtr[i + 1u], P.arcEnd);
|
|
1205
|
+
var total = vec3f(0.0);
|
|
1206
|
+
for (var left = select(0u, hi - lo, hi > lo); left > 0u; left = left - 1u) { // counts DOWN (see the header)
|
|
1207
|
+
let arc = hi - left; // lo, lo + 1, ..., hi - 1
|
|
1208
|
+
let k = arc - P.arcBase; // the window-local index; this walk is contiguous
|
|
1209
|
+
let nbr = colIdx[k];
|
|
1210
|
+
if (nbr == i) { continue; } // a self-loop exerts no force
|
|
1211
|
+
var weight = 1.0;
|
|
1212
|
+
if (HAS_WEIGHTS) { weight = weights[k]; }
|
|
1213
|
+
let d = pos[nbr].xyz - pi.xyz; // toward the neighbour
|
|
1214
|
+
let len = max(length(d), FA2_DIST_FLOOR);
|
|
1215
|
+
if (LAW == 1u) { weight = length(d) / P.frK; } // LAW 1 (FR, 7.20), as in row_force
|
|
1216
|
+
if (LAW == 2u) { weight = P.springCoefficient * (len - P.springLength) / len; } // LAW 2 (spring), as in row_force
|
|
1217
|
+
let mag = select(weight, weight * log(1.0 + len) / len, LINLOG);
|
|
1218
|
+
total = total + d * mag;
|
|
1219
|
+
}
|
|
1220
|
+
return total;
|
|
1221
|
+
}
|
|
1222
|
+
fn finish(i: u32, f0: vec3f) {
|
|
1223
|
+
var f = f0;
|
|
1224
|
+
if (DISTRIBUTED) { f = f / pos[i].w; }
|
|
1225
|
+
if (P.accumulate == 1u) { f = f + load_force(i); } // the windowed loop of 4.2 (arcBase != 0 dispatches after the first)
|
|
1226
|
+
store_force(i, f); // overwrites: attraction is the first writer of force each iteration
|
|
1227
|
+
}
|
|
1228
|
+
fn tier0(wid: vec3<u32>, lane: u32) { // TIER 0: one row per thread over [tierStart, tierEnd); no barrier, so the early return is legal (3.5 rule 1)
|
|
1229
|
+
let row = linear_id(wid, lane) + P.tierStart;
|
|
1230
|
+
if (row >= P.tierEnd) { return; }
|
|
1231
|
+
let i = row_node(row);
|
|
1232
|
+
finish(i, row_force_dense(i));
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
var<workgroup> sh: array<vec3f, WG>;
|
|
1236
|
+
|
|
1237
|
+
fn tiered(wid: vec3<u32>, lid: u32) { // TIER 1: 32 lanes per row over [hiEnd, midEnd); TIER 2: WG lanes per row over [0, hiEnd) (PD-6, PD-7)
|
|
1238
|
+
let g = group_id(wid);
|
|
1239
|
+
var row = g;
|
|
1240
|
+
var end = P.hiEnd;
|
|
1241
|
+
var lane = lid;
|
|
1242
|
+
var step = WG;
|
|
1243
|
+
if (TIER == 1u) { row = P.hiEnd + g * (WG / 32u) + lid / 32u; end = P.midEnd; lane = lid % 32u; step = 32u; }
|
|
1244
|
+
let valid = row < end;
|
|
1245
|
+
var i = 0u;
|
|
1246
|
+
var f = vec3f(0.0);
|
|
1247
|
+
if (valid) { i = row_node(row); f = row_force(i, lane, step); }
|
|
1248
|
+
if (TIER == 1u) {
|
|
1249
|
+
sh[lid] = f;
|
|
1250
|
+
workgroupBarrier();
|
|
1251
|
+
for (var s = 16u; s >= 1u; s = s / 2u) { // the five-step tree over each 32-lane group; every lane runs every step
|
|
1252
|
+
var t = vec3f(0.0);
|
|
1253
|
+
if (lane < s) { t = sh[lid + s]; }
|
|
1254
|
+
workgroupBarrier();
|
|
1255
|
+
sh[lid] = sh[lid] + t;
|
|
1256
|
+
workgroupBarrier();
|
|
1257
|
+
}
|
|
1258
|
+
if (valid && lane == 0u) { finish(i, sh[lid]); }
|
|
1259
|
+
}
|
|
1260
|
+
if (TIER == 2u) {
|
|
1261
|
+
let t = wg_reduce_vec4(vec4f(f, 0.0), lid, 0u);
|
|
1262
|
+
if (valid && lid == 0u) { finish(i, t.xyz); }
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
@compute @workgroup_size(WG)
|
|
1267
|
+
fn attraction(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1268
|
+
if (TIER == 0u) { tier0(wid, lid.x); } else { tiered(wid, lid.x); }
|
|
1269
|
+
}`,cl=`fn load_force(i: u32) -> vec3f { return vec3f(force[3u * i], force[3u * i + 1u], force[3u * i + 2u]); }
|
|
1270
|
+
fn load_old(i: u32) -> vec3f { return vec3f(oldForce[3u * i], oldForce[3u * i + 1u], oldForce[3u * i + 2u]); }
|
|
1271
|
+
fn store_old(i: u32, f: vec3f) {
|
|
1272
|
+
oldForce[3u * i] = f.x;
|
|
1273
|
+
oldForce[3u * i + 1u] = f.y;
|
|
1274
|
+
oldForce[3u * i + 2u] = f.z;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
@compute @workgroup_size(WG)
|
|
1278
|
+
fn integrate(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1279
|
+
let i = linear_id(wid, lid.x);
|
|
1280
|
+
var dp = vec3f(0.0);
|
|
1281
|
+
var p = vec4f(0.0);
|
|
1282
|
+
var free = false;
|
|
1283
|
+
var valid = false;
|
|
1284
|
+
var ke = 0.0;
|
|
1285
|
+
if (i < P.n) {
|
|
1286
|
+
valid = true;
|
|
1287
|
+
let f = load_force(i);
|
|
1288
|
+
p = pos[i];
|
|
1289
|
+
var swing_i = p.w * length(f); // SWING_MODE 1: NetworkX's local swinging m |F| (layout.py line 1497)
|
|
1290
|
+
if (SWING_MODE == 0u) { swing_i = p.w * length(f - load_old(i)); } // paper: m |F(t) - F(t-1)|, recomputed inline (7.2)
|
|
1291
|
+
let factor = S.speed / (1.0 + sqrt(S.speed * swing_i));
|
|
1292
|
+
let fixed = mask_bit(fixedMask[i >> 5u], i);
|
|
1293
|
+
dp = select(f * factor, vec3f(0.0), fixed); // APPLY 0 (FA2): no clamp on dp (D25)
|
|
1294
|
+
if (APPLY == 1u) { // APPLY 1 (FR, 7.20): move along F by min(|F|, t); a fixed node stays
|
|
1295
|
+
let mag = length(f);
|
|
1296
|
+
let t = select(P.temperature, S.temperature, (P.flags & FA2_FLAG_ADAPTIVE) != 0u); // adaptive cooling: K1's state temperature
|
|
1297
|
+
dp = vec3f(0.0);
|
|
1298
|
+
if (mag > 0.0 && !fixed) { dp = f * (min(mag, t) / mag); }
|
|
1299
|
+
ke = select(0.0, dot(f, f), !fixed); // the force energy of Hu's step control, folded like the spring preset's kinetic energy
|
|
1300
|
+
}
|
|
1301
|
+
if (APPLY == 2u) { // APPLY 2 (spring-electrical): ngraph's Euler step over the velocity in the oldForce slot (PD-2)
|
|
1302
|
+
var v = load_old(i);
|
|
1303
|
+
let fd = f - P.dragCoefficient * v; // drag (generateCreateDragForce.js:18)
|
|
1304
|
+
v = v + (P.timeStep / p.w) * fd; // v += (dt / m) F (generateIntegrator.js:27-29)
|
|
1305
|
+
let sp = length(v);
|
|
1306
|
+
if (sp > 1.0) { v = v / sp; } // the unit speed clamp (generateIntegrator.js:33-37)
|
|
1307
|
+
if (P.dim == 2u) { v.z = 0.0; }
|
|
1308
|
+
dp = select(P.timeStep * v, vec3f(0.0), fixed); // dp = dt v; a pinned body is skipped (generateIntegrator.js:21, 39-41)
|
|
1309
|
+
if (!fixed) { store_old(i, v); }
|
|
1310
|
+
ke = select(0.0, 0.5 * p.w * dot(v, v), !fixed); // partials B under APPLY 2 (PD-4)
|
|
1311
|
+
}
|
|
1312
|
+
if (P.dim == 2u) { dp.z = 0.0; } // 2D never integrates z (7.13)
|
|
1313
|
+
p = vec4f(p.xyz + dp, p.w);
|
|
1314
|
+
pos[i] = p;
|
|
1315
|
+
if (SWING_MODE == 0u) { store_old(i, f); } // fixed nodes too, so a later unpin sees no stale swing (7.11)
|
|
1316
|
+
free = !fixed;
|
|
1317
|
+
}
|
|
1318
|
+
// uniform control flow from here (3.5 rule 1): partials A (sum p, sum |p - c|^2, min, max over valid rows) and C (sum |dp|, free count)
|
|
1319
|
+
let c = S.centroid.xyz;
|
|
1320
|
+
var sumv = vec4f(0.0);
|
|
1321
|
+
var lo = vec4f(F32_MAX);
|
|
1322
|
+
var hi = vec4f(-F32_MAX);
|
|
1323
|
+
var dl = 0.0;
|
|
1324
|
+
var fr = 0u;
|
|
1325
|
+
if (valid) {
|
|
1326
|
+
let q = p.xyz - c;
|
|
1327
|
+
sumv = vec4f(p.xyz, dot(q, q));
|
|
1328
|
+
lo = vec4f(p.xyz, 0.0);
|
|
1329
|
+
hi = vec4f(p.xyz, dot(q, q)); // max.w carries max |p - c|^2 so K1 can write the exact layoutRadius (spec 3.3)
|
|
1330
|
+
}
|
|
1331
|
+
if (free) { dl = length(dp); fr = 1u; }
|
|
1332
|
+
let tSum = wg_reduce_vec4(sumv, lid.x, 0u);
|
|
1333
|
+
let tLo = wg_reduce_vec4(lo, lid.x, 1u);
|
|
1334
|
+
let tHi = wg_reduce_vec4(hi, lid.x, 2u);
|
|
1335
|
+
let tDl = wg_reduce_f32(dl, lid.x, 0u);
|
|
1336
|
+
let tFr = wg_reduce_u32(fr, lid.x, 0u);
|
|
1337
|
+
let tKe = wg_reduce_f32(ke, lid.x, 0u);
|
|
1338
|
+
if (lid.x == 0u) {
|
|
1339
|
+
let g = group_id(wid);
|
|
1340
|
+
partials[g].sum = tSum;
|
|
1341
|
+
partials[g].min = tLo;
|
|
1342
|
+
partials[g].max = tHi;
|
|
1343
|
+
partials[g].dispFree = vec2f(tDl, f32(tFr));
|
|
1344
|
+
if (APPLY != 0u) { partials[g].swingTraction = vec2f(tKe, 0.0); } // overwrites K3's epilogue: K4 never runs under FR or the preset (PD-4)
|
|
1345
|
+
}
|
|
1346
|
+
}`,ll=`
|
|
1347
|
+
var<workgroup> tile: array<vec4f, WG>; // xyz + mass, 4 KiB at WG = 256
|
|
1348
|
+
|
|
1349
|
+
fn load_force(i: u32) -> vec3f { return vec3f(force[3u * i], force[3u * i + 1u], force[3u * i + 2u]); }
|
|
1350
|
+
fn store_force(i: u32, f: vec3f) {
|
|
1351
|
+
force[3u * i] = f.x;
|
|
1352
|
+
force[3u * i + 1u] = f.y;
|
|
1353
|
+
force[3u * i + 2u] = f.z;
|
|
1354
|
+
}
|
|
1355
|
+
fn load_old(i: u32) -> vec3f { return vec3f(oldForce[3u * i], oldForce[3u * i + 1u], oldForce[3u * i + 2u]); }
|
|
1356
|
+
fn kick_magnitude(mi: f32, mj: f32) -> f32 { // the law's magnitude at d = FA2_DIST_FLOOR (PD-10)
|
|
1357
|
+
if (LAW == 1u) { return P.frK * P.frK / FA2_DIST_FLOOR; }
|
|
1358
|
+
if (LAW == 2u) { return -P.coulomb * mi * mj / FA2_DIST_FLOOR_SQ; }
|
|
1359
|
+
return P.scalingRatio * mi * mj / FA2_DIST_FLOOR;
|
|
1360
|
+
}
|
|
1361
|
+
fn gravity_force(pi: vec4f) -> vec3f { // spec 7.9: centroid (GRAVITY_CENTER 0) or origin (1); regular or strong
|
|
1362
|
+
var q = pi.xyz;
|
|
1363
|
+
if (GRAVITY_CENTER == 0u) { q = pi.xyz - S.centroid.xyz; }
|
|
1364
|
+
if (STRONG_GRAVITY) { return -P.gravity * pi.w * q; }
|
|
1365
|
+
let d = length(q);
|
|
1366
|
+
if (d > FA2_DIST_FLOOR) { return -P.gravity * pi.w * q / d; }
|
|
1367
|
+
return vec3f(0.0);
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
@compute @workgroup_size(WG)
|
|
1371
|
+
fn repulsion(
|
|
1372
|
+
@builtin(workgroup_id) wid: vec3<u32>,
|
|
1373
|
+
@builtin(local_invocation_id) lid: vec3<u32>,
|
|
1374
|
+
@builtin(num_workgroups) nwg: vec3<u32>,
|
|
1375
|
+
) {
|
|
1376
|
+
// issue #87: pass p of the tile range is dispatched with p + 1 z slices; only the last slice works, so the pass
|
|
1377
|
+
// index needs no uniform. Uniform: keyed on workgroup_id and num_workgroups only.
|
|
1378
|
+
if (wid.z + 1u < nwg.z) { return; }
|
|
1379
|
+
let i = linear_id(wid, lid.x);
|
|
1380
|
+
let valid = i < P.n;
|
|
1381
|
+
var pi = vec4f(0.0);
|
|
1382
|
+
if (valid) { pi = pos[i]; }
|
|
1383
|
+
var f = vec3f(0.0);
|
|
1384
|
+
let tiles = (P.n + WG - 1u) / WG;
|
|
1385
|
+
let tileBegin = (nwg.z - 1u) * EXACT_TILES_PER_PASS;
|
|
1386
|
+
let tileEnd = min(tiles, tileBegin + EXACT_TILES_PER_PASS);
|
|
1387
|
+
for (var t = tileBegin; t < tileEnd; t = t + 1u) {
|
|
1388
|
+
let j = t * WG + lid.x;
|
|
1389
|
+
if (j < P.n) { tile[lid.x] = pos[j]; } else { tile[lid.x] = vec4f(0.0); } // guarded fill; mass 0 marks the pad
|
|
1390
|
+
workgroupBarrier(); // uniform: every invocation reaches it
|
|
1391
|
+
for (var s = 0u; s < WG; s = s + 1u) {
|
|
1392
|
+
let o = tile[s];
|
|
1393
|
+
let jj = t * WG + s;
|
|
1394
|
+
if (o.w > 0.0 && jj != i) { // mass > 0 for every real node, 0 for the pad
|
|
1395
|
+
let d = pi.xyz - o.xyz;
|
|
1396
|
+
var d2 = dot(d, d);
|
|
1397
|
+
if (d2 < FA2_COINCIDENT_SQ) { // coincident: antisymmetric unit kick of the law's magnitude at d = 0.01 (7.2; PD-10)
|
|
1398
|
+
f = f + kick_dir(i, jj, P.dim) * kick_magnitude(pi.w, o.w);
|
|
1399
|
+
continue;
|
|
1400
|
+
}
|
|
1401
|
+
if (LAW == 0u) { d2 = max(d2, FA2_DIST_FLOOR_SQ); } // FA2 alone floors d >= 0.01 (7.2); FR and coulomb are unfloored (7.20)
|
|
1402
|
+
let k = P.scalingRatio * pi.w * o.w;
|
|
1403
|
+
if (LAW == 0u) { f = f + d * (k / d2); } // LAW 0 (FA2): |F| = k m_i m_j / d along d / d
|
|
1404
|
+
if (LAW == 1u) { f = f + d * (P.frK * P.frK / d2); } // LAW 1 (FR, 7.20): |F| = k^2 / d, mass ignored
|
|
1405
|
+
if (LAW == 2u) { f = f + d * (-P.coulomb * pi.w * o.w / (d2 * sqrt(d2))); } // LAW 2 (coulomb, ngraph generateQuadTree.js:131-132): |F| = -g m_i m_j / d^2
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
workgroupBarrier();
|
|
1409
|
+
}
|
|
1410
|
+
if (tileEnd < tiles) { // an earlier pass: its partial sum only (uniform: P.n, nwg)
|
|
1411
|
+
if (valid) { store_force(i, load_force(i) + f); }
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
// epilogue (7.9, 7.10): gravity and force += under the guard, the swing / traction reduction outside it
|
|
1415
|
+
var sw = 0.0;
|
|
1416
|
+
var tr = 0.0;
|
|
1417
|
+
if (valid) {
|
|
1418
|
+
f = f + gravity_force(pi);
|
|
1419
|
+
let fnew = load_force(i) + f;
|
|
1420
|
+
store_force(i, fnew);
|
|
1421
|
+
if (SWING_MODE == 1u) { // NetworkX: positions and forces mixed, every node (7.2)
|
|
1422
|
+
sw = pi.w * length(pi.xyz - fnew);
|
|
1423
|
+
tr = 0.5 * pi.w * length(pi.xyz + fnew);
|
|
1424
|
+
} else if (!mask_bit(fixedMask[i >> 5u], i)) { // paper: free nodes only (Gephi ForceAtlas2.java 283-293)
|
|
1425
|
+
let fold = load_old(i);
|
|
1426
|
+
sw = pi.w * length(fnew - fold);
|
|
1427
|
+
tr = 0.5 * pi.w * length(fnew + fold);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
let t = wg_reduce_vec4(vec4f(sw, tr, 0.0, 0.0), lid.x, 0u); // uniform control flow: 256 -> 1
|
|
1431
|
+
if (lid.x == 0u) { partials[group_id(wid)].swingTraction = t.xy; }
|
|
1432
|
+
}
|
|
1433
|
+
`,ul=`
|
|
1434
|
+
@compute @workgroup_size(WG)
|
|
1435
|
+
fn speed_finalize(@builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1436
|
+
let groups = (P.n + WG - 1u) / WG;
|
|
1437
|
+
var st = vec2f(0.0);
|
|
1438
|
+
for (var g = lid.x; g < groups; g = g + WG) { st = st + partials[g].swingTraction; } // sequential per lane: deterministic
|
|
1439
|
+
let t = wg_reduce_vec4(vec4f(st, 0.0, 0.0), lid.x, 0u);
|
|
1440
|
+
if (lid.x == 0u) {
|
|
1441
|
+
var swing = t.x;
|
|
1442
|
+
var traction = t.y;
|
|
1443
|
+
if (SWING_MODE == 1u) { swing = S.swing + t.x; traction = S.traction + t.y; } // NetworkX accumulates across iterations from 1
|
|
1444
|
+
let n = f32(P.n);
|
|
1445
|
+
let optJitter = 0.05 * sqrt(n);
|
|
1446
|
+
let minJitter = sqrt(optJitter);
|
|
1447
|
+
let maxJitter = 10.0;
|
|
1448
|
+
let tr = max(traction, 1.0e-30); // guards the division only (7.10)
|
|
1449
|
+
let other = min(maxJitter, optJitter * traction / (n * n));
|
|
1450
|
+
var jitter = P.jitterTolerance * max(minJitter, other);
|
|
1451
|
+
var eff = S.speedEfficiency;
|
|
1452
|
+
if (swing > 2.0 * tr) { // swing / traction > 2 in the exact form (contract 4.5 CONTRACT DECISION K4-1: 2 x is exact, a WGSL f32 division is not)
|
|
1453
|
+
if (eff > 0.05) { eff = eff * 0.5; } // the CPU's conditional multiply (7.2)
|
|
1454
|
+
jitter = max(jitter, P.jitterTolerance);
|
|
1455
|
+
}
|
|
1456
|
+
let targetSpeed = select(jitter * eff * traction / swing, 1.0e30, swing == 0.0); // +Inf in the port; 1e30 gives the same min() below (\`target\` is reserved)
|
|
1457
|
+
if (swing > jitter * traction) {
|
|
1458
|
+
if (eff > 0.05) { eff = eff * 0.7; }
|
|
1459
|
+
} else if (S.speed < 1000.0) {
|
|
1460
|
+
eff = eff * 1.3;
|
|
1461
|
+
}
|
|
1462
|
+
S.speed = S.speed + min(targetSpeed - S.speed, 0.5 * S.speed);
|
|
1463
|
+
S.speedEfficiency = eff;
|
|
1464
|
+
S.swing = swing;
|
|
1465
|
+
S.traction = traction;
|
|
1466
|
+
T[P.iterationIndex].swing = swing;
|
|
1467
|
+
T[P.iterationIndex].traction = traction;
|
|
1468
|
+
T[P.iterationIndex].speed = S.speed;
|
|
1469
|
+
T[P.iterationIndex].speedEfficiency = eff;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
`,dl=`// K1: folds the previous integrate's partials into the state block (spec 7.4); one workgroup
|
|
1473
|
+
@compute @workgroup_size(WG)
|
|
1474
|
+
fn stats_finalize(@builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1475
|
+
let groups = (P.n + WG - 1u) / WG;
|
|
1476
|
+
let fold = (P.flags & FA2_FLAG_FIRST) == 0u; // the first iteration after load() keeps the host-written state
|
|
1477
|
+
var sum = vec4f(0.0);
|
|
1478
|
+
var lo = vec4f(F32_MAX);
|
|
1479
|
+
var hi = vec4f(-F32_MAX);
|
|
1480
|
+
var disp = 0.0;
|
|
1481
|
+
var free = 0u;
|
|
1482
|
+
var ke = 0.0;
|
|
1483
|
+
if (fold) {
|
|
1484
|
+
for (var g = lid.x; g < groups; g = g + WG) { // sequential per lane in index order: deterministic
|
|
1485
|
+
let q = partials[g];
|
|
1486
|
+
sum = sum + q.sum;
|
|
1487
|
+
lo = min(lo, q.min);
|
|
1488
|
+
hi = max(hi, q.max);
|
|
1489
|
+
disp = disp + q.dispFree.x;
|
|
1490
|
+
free = free + u32(q.dispFree.y);
|
|
1491
|
+
ke = ke + q.swingTraction.x;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
let tSum = wg_reduce_vec4(sum, lid.x, 0u);
|
|
1495
|
+
let tLo = wg_reduce_vec4(lo, lid.x, 1u);
|
|
1496
|
+
let tHi = wg_reduce_vec4(hi, lid.x, 2u);
|
|
1497
|
+
let tDisp = wg_reduce_f32(disp, lid.x, 0u);
|
|
1498
|
+
let tFree = wg_reduce_u32(free, lid.x, 0u);
|
|
1499
|
+
let tKe = wg_reduce_f32(ke, lid.x, 0u);
|
|
1500
|
+
if (lid.x == 0u) {
|
|
1501
|
+
if (fold) {
|
|
1502
|
+
let n = f32(P.n);
|
|
1503
|
+
let c = tSum.xyz / n;
|
|
1504
|
+
S.centroid = vec4f(c, 0.0);
|
|
1505
|
+
S.rmsRadius = sqrt(max(tSum.w, 0.0) / n); // RMS radius about the previous centroid (7.17)
|
|
1506
|
+
S.min = vec4f(tLo.xyz, 0.0);
|
|
1507
|
+
S.max = vec4f(tHi.xyz, 0.0);
|
|
1508
|
+
S.radius = sqrt(max(tHi.w, 0.0)); // max |p - centroid| about the same previous centroid as rmsRadius (K5 puts |q|^2 in max.w)
|
|
1509
|
+
let meanDisp = select(tDisp / f32(tFree), 0.0, tFree == 0u); // all-fixed: 0, never NaN (7.4)
|
|
1510
|
+
S.meanDisplacement = meanDisp;
|
|
1511
|
+
S.settledCount = select(0u, S.settledCount + 1u, meanDisp <= min(P.settleThreshold * S.rmsRadius, P.settleFloor)); // relative AND absolute (issue #97)
|
|
1512
|
+
}
|
|
1513
|
+
S.iteration = S.iteration + 1u;
|
|
1514
|
+
T[P.iterationIndex].meanDisplacement = S.meanDisplacement;
|
|
1515
|
+
T[P.iterationIndex].settledCount = S.settledCount;
|
|
1516
|
+
T[P.iterationIndex].iteration = S.iteration;
|
|
1517
|
+
if (P.gridMax > 0u) { // the grid tier (7.7): the robust extent, the cell size, eps, last iteration's counts, the hub counter reset (PD-14)
|
|
1518
|
+
let cells = P.gridMax * P.gridMax * select(1u, P.gridMax, P.dim == 3u);
|
|
1519
|
+
if (fold) {
|
|
1520
|
+
let box = (S.max.xyz - S.min.xyz) * GRID_BBOX_MARGIN;
|
|
1521
|
+
var bboxExtent = max(box.x, box.y);
|
|
1522
|
+
if (P.dim == 3u) { bboxExtent = max(bboxExtent, box.z); }
|
|
1523
|
+
let extent = max(min(bboxExtent, P.extentFactor * S.rmsRadius), GRID_EXTENT_FLOOR); // min(bbox, extentFactor x rms), floored (7.7)
|
|
1524
|
+
let cellSize = extent / f32(P.gridMax);
|
|
1525
|
+
S.gridMin = vec4f(S.centroid.xyz - vec3f(0.5 * extent), cellSize); // gridMin.w carries cellSize
|
|
1526
|
+
S.invCellSize = 1.0 / cellSize;
|
|
1527
|
+
S.eps = 0.25 * cellSize;
|
|
1528
|
+
}
|
|
1529
|
+
var outside = 0u; // the previous iteration's pseudo-cell counts, one per orthant (issue #90; 0 after load)
|
|
1530
|
+
for (var o = 0u; o < select(4u, 8u, P.dim == 3u); o = o + 1u) { outside = outside + cellHist[cells + o]; }
|
|
1531
|
+
S.outsideGrid = outside;
|
|
1532
|
+
S.maxCellOccupancy = atomicLoad(&hubCounters[1]);
|
|
1533
|
+
atomicStore(&hubCounters[0], 0u);
|
|
1534
|
+
atomicStore(&hubCounters[1], 0u);
|
|
1535
|
+
}
|
|
1536
|
+
if (STATS_MODE == 1u) { // FR: this iteration's temperature (7.20) into the state and the trace
|
|
1537
|
+
if ((P.flags & FA2_FLAG_ADAPTIVE) != 0u) { // adaptive cooling (Yifan Hu 2005 3.2): tKe is the previous iteration's sum |F|^2 over free nodes
|
|
1538
|
+
if (fold) {
|
|
1539
|
+
var t = S.temperature;
|
|
1540
|
+
if (tKe < S.frEnergy) {
|
|
1541
|
+
S.frProgress = S.frProgress + 1u;
|
|
1542
|
+
if (S.frProgress >= FR_COOLING_PATIENCE) { S.frProgress = 0u; t = t / FR_COOLING_STEP; }
|
|
1543
|
+
} else {
|
|
1544
|
+
S.frProgress = 0u;
|
|
1545
|
+
t = t * FR_COOLING_STEP;
|
|
1546
|
+
}
|
|
1547
|
+
S.frEnergy = tKe;
|
|
1548
|
+
S.temperature = t;
|
|
1549
|
+
}
|
|
1550
|
+
} else {
|
|
1551
|
+
S.temperature = P.temperature;
|
|
1552
|
+
}
|
|
1553
|
+
T[P.iterationIndex].modelScalar = S.temperature;
|
|
1554
|
+
}
|
|
1555
|
+
if (STATS_MODE == 2u) { // spring-electrical: the kinetic energy K5 folded into partials B (PD-4); 0 on the first iteration after load()
|
|
1556
|
+
S.kineticEnergy = tKe;
|
|
1557
|
+
T[P.iterationIndex].modelScalar = tKe;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
}`,fl=`@compute @workgroup_size(WG)
|
|
1561
|
+
fn to_scene(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1562
|
+
let i = linear_id(wid, lid.x);
|
|
1563
|
+
if (i >= P.n) { return; }
|
|
1564
|
+
let s = pos[i].xyz * P.scale + P.center.xyz;
|
|
1565
|
+
scene[3u * i] = s.x;
|
|
1566
|
+
scene[3u * i + 1u] = s.y;
|
|
1567
|
+
scene[3u * i + 2u] = select(s.z, P.center.z, P.dim == 2u); // 2D writes z = center.z on every readback (7.13)
|
|
1568
|
+
}`,hl=`
|
|
1569
|
+
@compute @workgroup_size(WG)
|
|
1570
|
+
fn fill(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1571
|
+
let i = linear_id(wid, lid.x);
|
|
1572
|
+
if (i >= P.count) { return; }
|
|
1573
|
+
dst[i] = select(P.value, i + P.value, P.mode == 1u);
|
|
1574
|
+
}
|
|
1575
|
+
`,pl=`
|
|
1576
|
+
@compute @workgroup_size(WG)
|
|
1577
|
+
fn frontier_finalize(@builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1578
|
+
if (lid.x != 0u) { return; } // one lane; no barrier follows (3.5 rule 1)
|
|
1579
|
+
if (P.role == 0u) { // the level boundary
|
|
1580
|
+
if (atomicLoad(&counters[15]) != 0u) { // done already: a no-op level the host recorded past the end
|
|
1581
|
+
atomicStore(&counters[24], 0u); // the path word is the only word that moves (P8-T6's levels formula reads the rest)
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
let finished = atomicLoad(&counters[0]);
|
|
1585
|
+
let next = atomicLoad(&counters[1]);
|
|
1586
|
+
let degSum = atomicLoad(&counters[2]);
|
|
1587
|
+
let nextDeg = atomicLoad(&counters[25]); // deg(F_b), summed by bfs-next-degree when F_b was claimed (issue #391)
|
|
1588
|
+
atomicStore(&counters[3], finished); // prevFrontierCount
|
|
1589
|
+
atomicStore(&counters[4], degSum); // prevDegreeSum
|
|
1590
|
+
atomicStore(&counters[0], next); // the rotation
|
|
1591
|
+
atomicStore(&counters[1], 0u);
|
|
1592
|
+
atomicStore(&counters[2], 0u);
|
|
1593
|
+
atomicStore(&counters[25], 0u); // the next level's claims sum from 0
|
|
1594
|
+
atomicStore(&counters[8], 0u); // edgeCount
|
|
1595
|
+
atomicStore(&counters[9], 0u); // edgeCountUnclamped
|
|
1596
|
+
atomicStore(&counters[12], atomicLoad(&counters[12]) + next); // visitedCount
|
|
1597
|
+
if (P.firstOfSubmit >= 1u) { // b = the boundary's index inside its submit, clamped (P8-T8, PD-18): F_b was inside the submit's rebuilt sums iff b >= 1
|
|
1598
|
+
atomicStore(&counters[5], atomicLoad(&counters[5]) - next); // unvisitedCount, exact (bfs-next-degree keeps unvisitedDegreeSum exact)
|
|
1599
|
+
}
|
|
1600
|
+
let level = atomicLoad(&counters[11]) + 1u; // the seed is U32_MAX, so the first boundary lands on 0
|
|
1601
|
+
atomicStore(&counters[11], level);
|
|
1602
|
+
let done = (next == 0u) || (level >= P.maxDepth);
|
|
1603
|
+
atomicStore(&counters[15], select(0u, 1u, done));
|
|
1604
|
+
var direction = atomicLoad(&counters[14]);
|
|
1605
|
+
if (P.mode == 1u) {
|
|
1606
|
+
direction = 0u; // top-down only (the test seam)
|
|
1607
|
+
} else if (direction == 0u) {
|
|
1608
|
+
let bottomUpReads = select(max(atomicLoad(&counters[5]), atomicLoad(&counters[6]) / P.alpha), 0u, P.mode == 2u); // what a sweep is expected to read: at least one in-arc per unvisited vertex (issue #1358); mode 2 (the test seam) forces the switch
|
|
1609
|
+
if (nextDeg > bottomUpReads && next > finished) { direction = 1u; } // m_f > the sweep's reads and growing, m_f the degree of the frontier about to be expanded
|
|
1610
|
+
} else {
|
|
1611
|
+
if (next * P.beta < atomicLoad(&counters[5]) && next < finished) { direction = 0u; } // next * beta < unvisited and shrinking
|
|
1612
|
+
}
|
|
1613
|
+
if (direction != atomicLoad(&counters[14])) { atomicStore(&counters[13], atomicLoad(&counters[13]) + 1u); } // switches
|
|
1614
|
+
var path = 0u; // word 24: what the level's kernels run (0 nothing, 1 two-phase, 2 fused, 3 bottom-up; role 1 writes 4 for the retry)
|
|
1615
|
+
if (done) {
|
|
1616
|
+
path = 0u;
|
|
1617
|
+
} else if (direction == 1u) { // the bottom-up level (P8-T8): the bits fill, the bitset build, the sweep
|
|
1618
|
+
path = 3u;
|
|
1619
|
+
atomicStore(&counters[19], atomicLoad(&counters[19]) + 1u);
|
|
1620
|
+
} else if (next < P.fusedMax) { // P8-T7 makes this branch reachable (fusedMax is 0 until then)
|
|
1621
|
+
path = 2u; // bfs-fused: one WORKGROUP per frontier entry
|
|
1622
|
+
atomicStore(&counters[17], atomicLoad(&counters[17]) + 1u);
|
|
1623
|
+
} else {
|
|
1624
|
+
path = 1u; // advance-expand, then role 1 and bfs-contract
|
|
1625
|
+
}
|
|
1626
|
+
atomicStore(&counters[14], direction);
|
|
1627
|
+
atomicStore(&counters[24], path);
|
|
1628
|
+
} else if (P.role == 1u) { // the edge queue is filled
|
|
1629
|
+
if (atomicLoad(&counters[24]) != 1u) { // role 0 did not choose the two-phase path (done, fused or bottom-up): nothing to clamp, nothing to count
|
|
1630
|
+
return;
|
|
1631
|
+
}
|
|
1632
|
+
let clamped = min(atomicLoad(&counters[8]), P.edgeCapacity);
|
|
1633
|
+
atomicStore(&counters[8], clamped);
|
|
1634
|
+
if (atomicLoad(&counters[9]) > P.edgeCapacity) { // PD-23: the fused retry
|
|
1635
|
+
atomicStore(&counters[24], 4u); // the path word: bfs-fused runs the retry over frontierCount, bfs-contract nothing
|
|
1636
|
+
atomicStore(&counters[10], atomicLoad(&counters[10]) + 1u);
|
|
1637
|
+
atomicStore(&counters[17], atomicLoad(&counters[17]) + 1u);
|
|
1638
|
+
} else {
|
|
1639
|
+
atomicStore(&counters[18], atomicLoad(&counters[18]) + 1u); // twoPhaseLevels counts the CHOICE role 0 made, even for zero edges (P8-T7 Step 4's invariant)
|
|
1640
|
+
}
|
|
1641
|
+
} else if (P.role == 2u) { // the SSSP round boundary (P8-T9, PD-20): which pile this round relaxes
|
|
1642
|
+
atomicStore(&counters[8], 0u); // the dedupe counts (words 8 and 9, the SSSP sense) and the path word: nothing unless a pile is chosen below
|
|
1643
|
+
atomicStore(&counters[9], 0u);
|
|
1644
|
+
atomicStore(&counters[24], 0u);
|
|
1645
|
+
if (atomicLoad(&counters[15]) != 0u) { // done already: a no-op round the host recorded past the end (rule 1)
|
|
1646
|
+
return;
|
|
1647
|
+
}
|
|
1648
|
+
let nearRaw = atomicLoad(&counters[1]); // the raw near half's appends, unclamped
|
|
1649
|
+
let farRaw = atomicLoad(&counters[21]); // the raw far half's appends, unclamped
|
|
1650
|
+
if (nearRaw > P.edgeCapacity || farRaw > P.edgeCapacity) { // a pile overflowed its half: the host raises E_TOO_LARGE
|
|
1651
|
+
atomicStore(&counters[15], 2u);
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
if (nearRaw != 0u) { // a near round: dedupe the near half into nearIn
|
|
1655
|
+
atomicStore(&counters[0], 0u); // the deduped near count, accumulated by dedupe-filter
|
|
1656
|
+
atomicStore(&counters[14], 0u); // mode 0
|
|
1657
|
+
atomicStore(&counters[8], nearRaw); // the near dedupe's count word (dedupe-claim, dedupe-filter over the near half)
|
|
1658
|
+
atomicStore(&counters[24], 5u); // the path word: sssp-relax role 0 runs, role 1 nothing
|
|
1659
|
+
atomicStore(&counters[11], atomicLoad(&counters[11]) + 1u); // rounds dispatched (the done boundary is not counted)
|
|
1660
|
+
} else if (farRaw != 0u) { // the near pile is empty: raise the threshold and re-bucket the far pile
|
|
1661
|
+
let threshold = bitcast<f32>(atomicLoad(&counters[22]));
|
|
1662
|
+
let raised = threshold + bitcast<f32>(atomicLoad(&counters[23])); // ONE f32 add on the bit patterns (PD-9)
|
|
1663
|
+
if (raised == threshold) { // the delta is below the threshold's ulp: the host raises E_UNSUPPORTED
|
|
1664
|
+
atomicStore(&counters[15], 3u);
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
atomicStore(&counters[4], atomicLoad(&counters[22])); // prevThresholdBits: what the pass-through drops below
|
|
1668
|
+
atomicStore(&counters[22], bitcast<u32>(raised));
|
|
1669
|
+
atomicStore(&counters[20], 0u); // the deduped far count, accumulated by dedupe-filter
|
|
1670
|
+
atomicStore(&counters[14], 1u); // mode 1
|
|
1671
|
+
atomicStore(&counters[9], farRaw); // the far dedupe's count word (dedupe-claim, dedupe-filter over the far half)
|
|
1672
|
+
atomicStore(&counters[24], 6u); // the path word: sssp-relax role 1 runs, role 0 nothing
|
|
1673
|
+
atomicStore(&counters[11], atomicLoad(&counters[11]) + 1u);
|
|
1674
|
+
} else { // both piles empty: finished
|
|
1675
|
+
atomicStore(&counters[15], 1u);
|
|
1676
|
+
}
|
|
1677
|
+
} else if (P.role == 3u) { // the piles are deduped: restart the raw half the round consumed
|
|
1678
|
+
if (atomicLoad(&counters[15]) != 0u) { return; } // role 2 chose no pile this round
|
|
1679
|
+
if (atomicLoad(&counters[14]) == 0u) {
|
|
1680
|
+
atomicStore(&counters[1], 0u); // the raw near half restarts (sssp-relax role 0 sizes itself from word 0)
|
|
1681
|
+
} else {
|
|
1682
|
+
atomicStore(&counters[21], 0u); // the raw far half restarts (the pass-through re-appends what stays far; role 1 sizes itself from word 20)
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
`,gl=`
|
|
1687
|
+
fn grid_cells() -> u32 { return P.gridMax * P.gridMax * select(1u, P.gridMax, P.dim == 3u); }
|
|
1688
|
+
|
|
1689
|
+
@compute @workgroup_size(WG)
|
|
1690
|
+
fn grid_cell_key(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1691
|
+
let i = linear_id(wid, lid.x);
|
|
1692
|
+
if (i >= P.n) { return; } // no barrier follows
|
|
1693
|
+
let cells = grid_cells();
|
|
1694
|
+
let gf = f32(P.gridMax);
|
|
1695
|
+
let q = (pos[i].xyz - S.gridMin.xyz) * S.invCellSize; // PD-10: never a division
|
|
1696
|
+
let c = vec3<i32>(floor(clamp(q, vec3f(-1.0), vec3f(gf + 1.0))));
|
|
1697
|
+
let g = i32(P.gridMax);
|
|
1698
|
+
var inside = c.x >= 0 && c.x < g && c.y >= 0 && c.y < g;
|
|
1699
|
+
if (P.dim == 3u) { inside = inside && c.z >= 0 && c.z < g; }
|
|
1700
|
+
var key = cells + select(0u, 1u, c.x >= g / 2) + select(0u, 2u, c.y >= g / 2); // an outside pseudo-cell: its orthant (issue #90)
|
|
1701
|
+
if (P.dim == 3u) { key = key + select(0u, 4u, c.z >= g / 2); }
|
|
1702
|
+
if (inside) {
|
|
1703
|
+
key = u32(c.x) + P.gridMax * u32(c.y);
|
|
1704
|
+
if (P.dim == 3u) { key = key + P.gridMax * P.gridMax * u32(c.z); }
|
|
1705
|
+
}
|
|
1706
|
+
cellKey[i] = key;
|
|
1707
|
+
cellVal[i] = i;
|
|
1708
|
+
}
|
|
1709
|
+
`,ml=`
|
|
1710
|
+
fn grid_cells() -> u32 { return P.gridMax * P.gridMax * select(1u, P.gridMax, P.dim == 3u); }
|
|
1711
|
+
|
|
1712
|
+
@compute @workgroup_size(WG)
|
|
1713
|
+
fn grid_centroid(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1714
|
+
let c = linear_id(wid, lid.x);
|
|
1715
|
+
if (c >= grid_cells() + select(4u, 8u, P.dim == 3u)) { return; } // cells [0, cells + 2^dim): the pseudo-cells follow the real ones; no barrier follows
|
|
1716
|
+
let start = cellStart[c];
|
|
1717
|
+
let count = cellStart[c + 1u] - start;
|
|
1718
|
+
atomicMax(&hubCounters[1], count); // maxCellOccupancy, read by K1 next iteration
|
|
1719
|
+
if (count > GRID_HUB_CELL) { // a hub cell: G4b sums it (PD-13)
|
|
1720
|
+
hubList[atomicAdd(&hubCounters[0], 1u)] = c;
|
|
1721
|
+
return;
|
|
1722
|
+
}
|
|
1723
|
+
var acc = vec4f(0.0);
|
|
1724
|
+
for (var k = start; k < start + count; k = k + 1u) { // sorted order: deterministic
|
|
1725
|
+
let p = pos[sortedIdx[k]];
|
|
1726
|
+
acc = acc + vec4f(p.xyz * p.w, p.w); // (sum m x, sum m y, sum m z, sum m)
|
|
1727
|
+
}
|
|
1728
|
+
pyramid[c] = acc;
|
|
1729
|
+
}
|
|
1730
|
+
`,wl=`
|
|
1731
|
+
@compute @workgroup_size(WG)
|
|
1732
|
+
fn grid_centroid_hub(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1733
|
+
let h = group_id(wid);
|
|
1734
|
+
let valid = h < hubCount[0]; // a workgroup past the count sums nothing
|
|
1735
|
+
var c = 0u;
|
|
1736
|
+
var start = 0u;
|
|
1737
|
+
var count = 0u;
|
|
1738
|
+
if (valid) {
|
|
1739
|
+
c = hubList[h];
|
|
1740
|
+
start = cellStart[c];
|
|
1741
|
+
count = cellStart[c + 1u] - start;
|
|
1742
|
+
}
|
|
1743
|
+
var acc = vec4f(0.0);
|
|
1744
|
+
for (var k = start + lid.x; k < start + count; k = k + WG) { // strided over the cell's sorted range
|
|
1745
|
+
let p = pos[sortedIdx[k]];
|
|
1746
|
+
acc = acc + vec4f(p.xyz * p.w, p.w);
|
|
1747
|
+
}
|
|
1748
|
+
let t = wg_reduce_vec4(acc, lid.x, 0u); // uniform control flow: 256 -> 1
|
|
1749
|
+
if (valid && lid.x == 0u) { pyramid[c] = t; }
|
|
1750
|
+
}
|
|
1751
|
+
`,bl=`
|
|
1752
|
+
@compute @workgroup_size(WG)
|
|
1753
|
+
fn grid_downsample(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1754
|
+
let pc = linear_id(wid, lid.x); // the parent cell inside its level
|
|
1755
|
+
if (pc >= P.parentCells) { return; } // no barrier follows
|
|
1756
|
+
let side = P.parentSide;
|
|
1757
|
+
let cs = 2u * side; // the child level's side
|
|
1758
|
+
let px = pc % side;
|
|
1759
|
+
let py = (pc / side) % side;
|
|
1760
|
+
let pz = pc / (side * side);
|
|
1761
|
+
var acc = vec4f(0.0);
|
|
1762
|
+
for (var dz = 0u; dz < P.depth; dz = dz + 1u) {
|
|
1763
|
+
for (var dy = 0u; dy < 2u; dy = dy + 1u) {
|
|
1764
|
+
for (var dx = 0u; dx < 2u; dx = dx + 1u) {
|
|
1765
|
+
let child = (2u * px + dx) + cs * ((2u * py + dy) + cs * (2u * pz + dz));
|
|
1766
|
+
acc = acc + pyramid[P.childBase + child];
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
pyramid[P.parentBase + pc] = acc;
|
|
1771
|
+
}
|
|
1772
|
+
`,vl=`
|
|
1773
|
+
fn load_force(i: u32) -> vec3f { return vec3f(force[3u * i], force[3u * i + 1u], force[3u * i + 2u]); }
|
|
1774
|
+
fn store_force(i: u32, f: vec3f) {
|
|
1775
|
+
force[3u * i] = f.x;
|
|
1776
|
+
force[3u * i + 1u] = f.y;
|
|
1777
|
+
force[3u * i + 2u] = f.z;
|
|
1778
|
+
}
|
|
1779
|
+
fn grid_cells() -> u32 { return P.gridMax * P.gridMax * select(1u, P.gridMax, P.dim == 3u); }
|
|
1780
|
+
fn grid_side(level: u32) -> u32 { return P.gridMax >> level; }
|
|
1781
|
+
fn outside_cells() -> u32 { return select(4u, 8u, P.dim == 3u); } // one pseudo-cell per orthant (issue #90)
|
|
1782
|
+
fn level_base(level: u32) -> u32 { // the pyramid index of level L's cell 0 (level 0 carries the pseudo-cells at cells ..)
|
|
1783
|
+
var base = 0u;
|
|
1784
|
+
for (var l = 0u; l < level; l = l + 1u) {
|
|
1785
|
+
let s = grid_side(l);
|
|
1786
|
+
base = base + s * s * select(1u, s, P.dim == 3u) + select(0u, outside_cells(), l == 0u);
|
|
1787
|
+
}
|
|
1788
|
+
return base;
|
|
1789
|
+
}
|
|
1790
|
+
fn cell_at(level: u32, cx: i32, cy: i32, cz: i32) -> u32 {
|
|
1791
|
+
let s = grid_side(level);
|
|
1792
|
+
return level_base(level) + u32(cx) + s * (u32(cy) + select(0u, s * u32(cz), P.dim == 3u));
|
|
1793
|
+
}
|
|
1794
|
+
fn cell_force(pi: vec4f, q: vec4f) -> vec3f { // one far-field term, softened by state.eps (7.7)
|
|
1795
|
+
if (q.w <= 0.0) { return vec3f(0.0); } // an empty cell
|
|
1796
|
+
let d = pi.xyz - q.xyz / q.w; // to the mass-weighted centroid
|
|
1797
|
+
var d2 = dot(d, d);
|
|
1798
|
+
if (LAW == 0u) { d2 = max(d2, FA2_DIST_FLOOR_SQ); } // FA2 alone floors d >= 0.01, as K3 and G7 do (issue #89); FR and coulomb are unfloored (7.20)
|
|
1799
|
+
d2 = d2 + S.eps * S.eps;
|
|
1800
|
+
if (LAW == 1u) { return d * (P.frK * P.frK * q.w / d2); } // LAW 1 (FR, 7.20): k^2 / d per node, q.w nodes at the centroid
|
|
1801
|
+
if (LAW == 2u) { return d * (-P.coulomb * pi.w * q.w / (d2 * sqrt(d2))); } // LAW 2 (coulomb): -g m_i M_cell / d^2
|
|
1802
|
+
return d * (P.scalingRatio * pi.w * q.w / d2); // LAW 0 (FA2): |F| = k m_i M_cell / d
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
@compute @workgroup_size(WG)
|
|
1806
|
+
fn grid_far_field(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1807
|
+
let t = linear_id(wid, lid.x);
|
|
1808
|
+
if (t >= P.n) { return; } // no barrier follows
|
|
1809
|
+
let i = sortedIdx[t]; // sorted order (D24)
|
|
1810
|
+
let pi = pos[i];
|
|
1811
|
+
let gf = f32(P.gridMax);
|
|
1812
|
+
let q = (pi.xyz - S.gridMin.xyz) * S.invCellSize; // PD-10
|
|
1813
|
+
var c0 = vec3<i32>(floor(clamp(q, vec3f(-1.0), vec3f(gf + 1.0))));
|
|
1814
|
+
if (P.dim == 2u) { c0.z = 0; } // 2D: one z plane; the loops below visit cz = 0 only, so the 3x3 test must see cz - 0
|
|
1815
|
+
let g = i32(P.gridMax);
|
|
1816
|
+
var inside = c0.x >= 0 && c0.x < g && c0.y >= 0 && c0.y < g;
|
|
1817
|
+
if (P.dim == 3u) { inside = inside && c0.z >= 0 && c0.z < g; }
|
|
1818
|
+
let top = P.levels - 1u;
|
|
1819
|
+
let ts = i32(grid_side(top)); // the coarsest side (4)
|
|
1820
|
+
let zTop = select(0, ts - 1, P.dim == 3u); // z ranges: one plane in 2D
|
|
1821
|
+
var f = vec3f(0.0);
|
|
1822
|
+
if (inside) {
|
|
1823
|
+
let ct = c0 / i32(1u << top); // the node's coarsest cell
|
|
1824
|
+
for (var cz = 0; cz <= zTop; cz = cz + 1) {
|
|
1825
|
+
for (var cy = 0; cy < ts; cy = cy + 1) {
|
|
1826
|
+
for (var cx = 0; cx < ts; cx = cx + 1) {
|
|
1827
|
+
if (abs(cx - ct.x) <= 1 && abs(cy - ct.y) <= 1 && abs(cz - ct.z) <= 1) { continue; } // the 3x3(x3) is finer levels' work
|
|
1828
|
+
f = f + cell_force(pi, pyramid[cell_at(top, cx, cy, cz)]);
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
for (var l = top; l > 0u; l = l - 1u) { // level l - 1: the parent's 3x3 at level l, refined, minus this level's own 3x3
|
|
1833
|
+
let level = l - 1u;
|
|
1834
|
+
let cl = c0 / i32(1u << level);
|
|
1835
|
+
let cp = cl / 2;
|
|
1836
|
+
let side = i32(grid_side(level));
|
|
1837
|
+
let zLo = select(0, max(0, 2 * (cp.z - 1)), P.dim == 3u);
|
|
1838
|
+
let zHi = select(0, min(side - 1, 2 * (cp.z + 1) + 1), P.dim == 3u);
|
|
1839
|
+
for (var cz = zLo; cz <= zHi; cz = cz + 1) {
|
|
1840
|
+
for (var cy = max(0, 2 * (cp.y - 1)); cy <= min(side - 1, 2 * (cp.y + 1) + 1); cy = cy + 1) {
|
|
1841
|
+
for (var cx = max(0, 2 * (cp.x - 1)); cx <= min(side - 1, 2 * (cp.x + 1) + 1); cx = cx + 1) {
|
|
1842
|
+
if (abs(cx - cl.x) <= 1 && abs(cy - cl.y) <= 1 && abs(cz - cl.z) <= 1) { continue; }
|
|
1843
|
+
f = f + cell_force(pi, pyramid[cell_at(level, cx, cy, cz)]);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
for (var o = 0u; o < outside_cells(); o = o + 1u) { // every outside pseudo-cell: one far-field term per orthant
|
|
1849
|
+
f = f + cell_force(pi, pyramid[grid_cells() + o]);
|
|
1850
|
+
}
|
|
1851
|
+
} else {
|
|
1852
|
+
for (var cz = 0; cz <= zTop; cz = cz + 1) { // an outside node: the coarsest level in full, no pseudo-cell (G7 sums them pair by pair)
|
|
1853
|
+
for (var cy = 0; cy < ts; cy = cy + 1) {
|
|
1854
|
+
for (var cx = 0; cx < ts; cx = cx + 1) {
|
|
1855
|
+
f = f + cell_force(pi, pyramid[cell_at(top, cx, cy, cz)]);
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
store_force(i, load_force(i) + f);
|
|
1861
|
+
}
|
|
1862
|
+
`,yl=`
|
|
1863
|
+
fn load_force(i: u32) -> vec3f { return vec3f(force[3u * i], force[3u * i + 1u], force[3u * i + 2u]); }
|
|
1864
|
+
fn store_force(i: u32, f: vec3f) {
|
|
1865
|
+
force[3u * i] = f.x;
|
|
1866
|
+
force[3u * i + 1u] = f.y;
|
|
1867
|
+
force[3u * i + 2u] = f.z;
|
|
1868
|
+
}
|
|
1869
|
+
fn load_old(i: u32) -> vec3f { return vec3f(oldForce[3u * i], oldForce[3u * i + 1u], oldForce[3u * i + 2u]); }
|
|
1870
|
+
fn gravity_force(pi: vec4f) -> vec3f { // spec 7.9: centroid (GRAVITY_CENTER 0) or origin (1); regular or strong
|
|
1871
|
+
var q = pi.xyz;
|
|
1872
|
+
if (GRAVITY_CENTER == 0u) { q = pi.xyz - S.centroid.xyz; }
|
|
1873
|
+
if (STRONG_GRAVITY) { return -P.gravity * pi.w * q; }
|
|
1874
|
+
let d = length(q);
|
|
1875
|
+
if (d > FA2_DIST_FLOOR) { return -P.gravity * pi.w * q / d; }
|
|
1876
|
+
return vec3f(0.0);
|
|
1877
|
+
}
|
|
1878
|
+
fn grid_cells() -> u32 { return P.gridMax * P.gridMax * select(1u, P.gridMax, P.dim == 3u); }
|
|
1879
|
+
fn kick_magnitude(mi: f32, mj: f32) -> f32 { // the law's magnitude at d = FA2_DIST_FLOOR (the P5 plan's PD-10)
|
|
1880
|
+
if (LAW == 1u) { return P.frK * P.frK / FA2_DIST_FLOOR; }
|
|
1881
|
+
if (LAW == 2u) { return -P.coulomb * mi * mj / FA2_DIST_FLOOR_SQ; }
|
|
1882
|
+
return P.scalingRatio * mi * mj / FA2_DIST_FLOOR;
|
|
1883
|
+
}
|
|
1884
|
+
fn pair_force(i: u32, pi: vec4f, jj: u32, o: vec4f) -> vec3f { // the exact pair law of K3 (7.6, 7.20): the floor (FA2 only), the coincident kick
|
|
1885
|
+
let d = pi.xyz - o.xyz;
|
|
1886
|
+
var d2 = dot(d, d);
|
|
1887
|
+
if (d2 < FA2_COINCIDENT_SQ) { return kick_dir(i, jj, P.dim) * kick_magnitude(pi.w, o.w); }
|
|
1888
|
+
if (LAW == 0u) { d2 = max(d2, FA2_DIST_FLOOR_SQ); }
|
|
1889
|
+
let k = P.scalingRatio * pi.w * o.w;
|
|
1890
|
+
if (LAW == 1u) { return d * (P.frK * P.frK / d2); }
|
|
1891
|
+
if (LAW == 2u) { return d * (-P.coulomb * pi.w * o.w / (d2 * sqrt(d2))); }
|
|
1892
|
+
return d * (k / d2);
|
|
1893
|
+
}
|
|
1894
|
+
fn cell_sum(i: u32, pi: vec4f, c: u32, own: bool) -> vec3f { // one finest cell: exact below nearMax entries, Horvitz-Thompson above (PD-15)
|
|
1895
|
+
let start = cellStart[c];
|
|
1896
|
+
let count = cellStart[c + 1u] - start;
|
|
1897
|
+
var f = vec3f(0.0);
|
|
1898
|
+
if (count <= P.nearMax) {
|
|
1899
|
+
for (var k = start; k < start + count; k = k + 1u) {
|
|
1900
|
+
let jj = sortedIdx[k];
|
|
1901
|
+
if (jj != i) { f = f + pair_force(i, pi, jj, pos[jj]); }
|
|
1902
|
+
}
|
|
1903
|
+
return f;
|
|
1904
|
+
}
|
|
1905
|
+
let base = (c ^ (P.iterationIndex * 0x9E3779B9u)) ^ P.seed; // the per-iteration draw seed (7.16)
|
|
1906
|
+
var sampled = 0u;
|
|
1907
|
+
for (var k = 0u; k < P.nearMax; k = k + 1u) {
|
|
1908
|
+
let jj = sortedIdx[start + (lowbias32(base ^ (k * 0x85EBCA6Bu)) % count)]; // draw k: independent inclusion, with replacement (PD-15)
|
|
1909
|
+
if (jj == i) { continue; }
|
|
1910
|
+
f = f + pair_force(i, pi, jj, pos[jj]);
|
|
1911
|
+
sampled = sampled + 1u;
|
|
1912
|
+
}
|
|
1913
|
+
if (sampled == 0u) { return vec3f(0.0); }
|
|
1914
|
+
let others = select(count, count - 1u, own);
|
|
1915
|
+
return f * (f32(others) / f32(sampled)); // others / sampled over the realised sample (DEP-P4-K)
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
@compute @workgroup_size(WG)
|
|
1919
|
+
fn grid_near_field(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
1920
|
+
let t = linear_id(wid, lid.x);
|
|
1921
|
+
let valid = t < P.n;
|
|
1922
|
+
var i = 0u;
|
|
1923
|
+
var pi = vec4f(0.0);
|
|
1924
|
+
var f = vec3f(0.0);
|
|
1925
|
+
if (valid) {
|
|
1926
|
+
i = sortedIdx[t]; // sorted order (D24)
|
|
1927
|
+
pi = pos[i];
|
|
1928
|
+
let gf = f32(P.gridMax);
|
|
1929
|
+
let q = (pi.xyz - S.gridMin.xyz) * S.invCellSize;
|
|
1930
|
+
var c0 = vec3<i32>(floor(clamp(q, vec3f(-1.0), vec3f(gf + 1.0))));
|
|
1931
|
+
if (P.dim == 2u) { c0.z = 0; } // 2D: the one z plane
|
|
1932
|
+
let g = i32(P.gridMax);
|
|
1933
|
+
var inside = c0.x >= 0 && c0.x < g && c0.y >= 0 && c0.y < g;
|
|
1934
|
+
if (P.dim == 3u) { inside = inside && c0.z >= 0 && c0.z < g; }
|
|
1935
|
+
if (inside) {
|
|
1936
|
+
let zr = select(0, 1, P.dim == 3u);
|
|
1937
|
+
for (var dz = -zr; dz <= zr; dz = dz + 1) {
|
|
1938
|
+
for (var dy = -1; dy <= 1; dy = dy + 1) {
|
|
1939
|
+
for (var dx = -1; dx <= 1; dx = dx + 1) {
|
|
1940
|
+
let cx = c0.x + dx;
|
|
1941
|
+
let cy = c0.y + dy;
|
|
1942
|
+
let cz = c0.z + dz;
|
|
1943
|
+
if (cx < 0 || cx >= g || cy < 0 || cy >= g || cz < 0 || cz >= g) { continue; }
|
|
1944
|
+
let c = u32(cx) + P.gridMax * (u32(cy) + select(0u, P.gridMax * u32(cz), P.dim == 3u));
|
|
1945
|
+
f = f + cell_sum(i, pi, c, dx == 0 && dy == 0 && dz == 0);
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
} else {
|
|
1950
|
+
var own = grid_cells() + select(0u, 1u, c0.x >= g / 2) + select(0u, 2u, c0.y >= g / 2); // G1's orthant key
|
|
1951
|
+
if (P.dim == 3u) { own = own + select(0u, 4u, c0.z >= g / 2); }
|
|
1952
|
+
for (var o = grid_cells(); o < grid_cells() + select(4u, 8u, P.dim == 3u); o = o + 1u) { // an outside node: every outside pseudo-cell
|
|
1953
|
+
f = f + cell_sum(i, pi, o, o == own);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
// epilogue (7.9, 7.10): gravity and force += under the guard, the swing / traction reduction outside it (K3's text)
|
|
1958
|
+
var sw = 0.0;
|
|
1959
|
+
var tr = 0.0;
|
|
1960
|
+
if (valid) {
|
|
1961
|
+
f = f + gravity_force(pi);
|
|
1962
|
+
let fnew = load_force(i) + f;
|
|
1963
|
+
store_force(i, fnew);
|
|
1964
|
+
if (SWING_MODE == 1u) { // NetworkX: positions and forces mixed, every node (7.2)
|
|
1965
|
+
sw = pi.w * length(pi.xyz - fnew);
|
|
1966
|
+
tr = 0.5 * pi.w * length(pi.xyz + fnew);
|
|
1967
|
+
} else if (!mask_bit(fixedMask[i >> 5u], i)) { // paper: free nodes only
|
|
1968
|
+
let fold = load_old(i);
|
|
1969
|
+
sw = pi.w * length(fnew - fold);
|
|
1970
|
+
tr = 0.5 * pi.w * length(fnew + fold);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
let tt = wg_reduce_vec4(vec4f(sw, tr, 0.0, 0.0), lid.x, 0u); // uniform control flow: 256 -> 1
|
|
1974
|
+
if (lid.x == 0u) { partials[group_id(wid)].swingTraction = tt.xy; }
|
|
1975
|
+
}
|
|
1976
|
+
`,_l=`
|
|
1977
|
+
var<workgroup> shMax: array<f32, WG>;
|
|
1978
|
+
var<workgroup> shKey: array<u32, WG>;
|
|
1979
|
+
var<workgroup> shSum: array<u32, WG>;
|
|
1980
|
+
|
|
1981
|
+
fn weight_of(a: u32) -> f32 {
|
|
1982
|
+
if (WEIGHTED) { return weights[a]; }
|
|
1983
|
+
return 1.0;
|
|
1984
|
+
}
|
|
1985
|
+
fn scale_of(maxW: f32, d: u32) -> f32 { // 2^s, maxW x d x 2^s in [2^28, 2^30), from exponents alone
|
|
1986
|
+
let em = i32((bitcast<u32>(maxW) >> 23u) & 255u) - 127;
|
|
1987
|
+
let ed = i32(firstLeadingBit(max(d, 1u)));
|
|
1988
|
+
let s = clamp(28 - em - ed, -126, 126);
|
|
1989
|
+
return bitcast<f32>(u32(s + 127) << 23u);
|
|
1990
|
+
}
|
|
1991
|
+
fn inverse_of(scale: f32) -> f32 { // 2^-s, exact
|
|
1992
|
+
let s = i32((bitcast<u32>(scale) >> 23u) & 255u) - 127;
|
|
1993
|
+
return bitcast<f32>(u32(127 - s) << 23u);
|
|
1994
|
+
}
|
|
1995
|
+
fn quantize(w: f32, scale: f32) -> u32 { // nearest integer, halves up; every step exact
|
|
1996
|
+
let x = max(w * scale, 0.0);
|
|
1997
|
+
let i = u32(x);
|
|
1998
|
+
return select(i, i + 1u, x - f32(i) >= 0.5);
|
|
1999
|
+
}
|
|
2000
|
+
fn better(sum: u32, key: u32, bestSum: u32, bestKey0: u32, own: u32) -> bool { // the larger sum; on a tie own key, then lowest
|
|
2001
|
+
return sum > bestSum || (sum == bestSum && bestKey0 != own && (key == own || key < bestKey0));
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
fn row_thread(v: u32) {
|
|
2005
|
+
let lo = rowPtr[v];
|
|
2006
|
+
let hi = rowPtr[v + 1u];
|
|
2007
|
+
var maxW = 0.0;
|
|
2008
|
+
for (var a = lo; a < hi; a = a + 1u) { maxW = max(maxW, weight_of(a)); }
|
|
2009
|
+
let scale = scale_of(maxW, hi - lo);
|
|
2010
|
+
let own = keyIn[v];
|
|
2011
|
+
var bk = INVALID_INDEX;
|
|
2012
|
+
var bs = 0u;
|
|
2013
|
+
for (var a = lo; a < hi; a = a + 1u) {
|
|
2014
|
+
let k = keyIn[colIdx[a]];
|
|
2015
|
+
var seen = false;
|
|
2016
|
+
for (var b = lo; b < a; b = b + 1u) { if (keyIn[colIdx[b]] == k) { seen = true; break; } }
|
|
2017
|
+
if (seen) { continue; } // this key was summed at its first arc
|
|
2018
|
+
var sum = 0u;
|
|
2019
|
+
for (var b = a; b < hi; b = b + 1u) { if (keyIn[colIdx[b]] == k) { sum = sum + quantize(weight_of(b), scale); } }
|
|
2020
|
+
if (better(sum, k, bs, bk, own)) { bk = k; bs = sum; }
|
|
2021
|
+
}
|
|
2022
|
+
bestKey[v] = bk;
|
|
2023
|
+
bestScore[v] = f32(bs) * inverse_of(scale);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
fn row_hash(g: u32, lid: u32) {
|
|
2027
|
+
let v = rows[P.rowsBase + g];
|
|
2028
|
+
let base = rows[P.basesBase + g];
|
|
2029
|
+
let lo = rowPtr[v];
|
|
2030
|
+
let hi = rowPtr[v + 1u];
|
|
2031
|
+
let cap = GROUP_HASH_LOAD_FACTOR * (hi - lo);
|
|
2032
|
+
var m = 0.0;
|
|
2033
|
+
for (var a = lo + lid; a < hi; a = a + WG) { m = max(m, weight_of(a)); }
|
|
2034
|
+
shMax[lid] = m;
|
|
2035
|
+
workgroupBarrier();
|
|
2036
|
+
for (var s = WG / 2u; s > 0u; s = s / 2u) {
|
|
2037
|
+
if (lid < s) { shMax[lid] = max(shMax[lid], shMax[lid + s]); }
|
|
2038
|
+
workgroupBarrier();
|
|
2039
|
+
}
|
|
2040
|
+
let scale = scale_of(shMax[0], hi - lo);
|
|
2041
|
+
for (var j = lid; j < cap; j = j + WG) { // the region is this workgroup's alone: clear it
|
|
2042
|
+
atomicStore(&hashRegion[base + 2u * j], INVALID_INDEX);
|
|
2043
|
+
atomicStore(&hashRegion[base + 2u * j + 1u], 0u);
|
|
2044
|
+
}
|
|
2045
|
+
storageBarrier();
|
|
2046
|
+
var exhausted = false;
|
|
2047
|
+
for (var a = lo + lid; a < hi; a = a + WG) {
|
|
2048
|
+
let k = keyIn[colIdx[a]];
|
|
2049
|
+
let q = quantize(weight_of(a), scale);
|
|
2050
|
+
var slot = lowbias32(k) % cap;
|
|
2051
|
+
var steps = 0u;
|
|
2052
|
+
loop {
|
|
2053
|
+
if (steps >= cap + 64u) { exhausted = true; break; } // bounded: a spurious compare-exchange failure is legal (WGSL 17.8.5)
|
|
2054
|
+
steps = steps + 1u;
|
|
2055
|
+
let r = atomicCompareExchangeWeak(&hashRegion[base + 2u * slot], INVALID_INDEX, k);
|
|
2056
|
+
if (r.exchanged || r.old_value == k) { atomicAdd(&hashRegion[base + 2u * slot + 1u], q); break; }
|
|
2057
|
+
if (r.old_value == INVALID_INDEX) { continue; } // a spurious failure: the same slot again
|
|
2058
|
+
slot = (slot + 1u) % cap; // linear probing
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
if (exhausted) { atomicStore(&hashRegion[0], 1u); }
|
|
2062
|
+
storageBarrier();
|
|
2063
|
+
let own = keyIn[v];
|
|
2064
|
+
var bk = INVALID_INDEX;
|
|
2065
|
+
var bs = 0u;
|
|
2066
|
+
for (var j = lid; j < cap; j = j + WG) {
|
|
2067
|
+
let k = atomicLoad(&hashRegion[base + 2u * j]);
|
|
2068
|
+
if (k != INVALID_INDEX) {
|
|
2069
|
+
let sum = atomicLoad(&hashRegion[base + 2u * j + 1u]);
|
|
2070
|
+
if (better(sum, k, bs, bk, own)) { bk = k; bs = sum; }
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
shKey[lid] = bk;
|
|
2074
|
+
shSum[lid] = bs;
|
|
2075
|
+
workgroupBarrier();
|
|
2076
|
+
for (var s = WG / 2u; s > 0u; s = s / 2u) {
|
|
2077
|
+
if (lid < s && better(shSum[lid + s], shKey[lid + s], shSum[lid], shKey[lid], own)) {
|
|
2078
|
+
shKey[lid] = shKey[lid + s];
|
|
2079
|
+
shSum[lid] = shSum[lid + s];
|
|
2080
|
+
}
|
|
2081
|
+
workgroupBarrier();
|
|
2082
|
+
}
|
|
2083
|
+
if (lid == 0u) {
|
|
2084
|
+
bestKey[v] = shKey[0];
|
|
2085
|
+
bestScore[v] = f32(shSum[0]) * inverse_of(scale);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
@compute @workgroup_size(WG)
|
|
2090
|
+
fn group_by_key_row(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2091
|
+
if (TIER == 0u) {
|
|
2092
|
+
let i = linear_id(wid, lid.x);
|
|
2093
|
+
if (i < P.count) { row_thread(rows[P.rowsBase + i]); }
|
|
2094
|
+
return; // TIER is an override: uniform
|
|
2095
|
+
}
|
|
2096
|
+
let g = group_id(wid);
|
|
2097
|
+
if (g >= P.count) { return; } // uniform: the workgroup id and a uniform
|
|
2098
|
+
row_hash(g, lid.x);
|
|
2099
|
+
}
|
|
2100
|
+
`,Pl=`
|
|
2101
|
+
@compute @workgroup_size(WG)
|
|
2102
|
+
fn histogram(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2103
|
+
let i = linear_id(wid, lid.x);
|
|
2104
|
+
if (i >= P.count) { return; } // no barrier follows
|
|
2105
|
+
let k = keys[i];
|
|
2106
|
+
if (k < P.bins) { atomicAdd(&hist[k], 1u); } // order-independent: the count is the same whatever the schedule
|
|
2107
|
+
}
|
|
2108
|
+
`,Sl=`
|
|
2109
|
+
@compute @workgroup_size(WG)
|
|
2110
|
+
fn indirect_finalize(@builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2111
|
+
if (lid.x != 0u) { return; } // one lane; no barrier follows (3.5 rule 1)
|
|
2112
|
+
let count = counters[P.countIndex];
|
|
2113
|
+
let groups = count / P.wg + select(0u, 1u, count % P.wg != 0u); // ceil(count / wg) without the u32 wrap of (count + wg - 1) above 2^32 - wg: plan1d's rule (5.2) for ANY u32 count
|
|
2114
|
+
var x = groups;
|
|
2115
|
+
var y = 1u;
|
|
2116
|
+
if (groups > MAX_WORKGROUPS_PER_DIM) { // the 2D split; y <= 1,025 for any u32 count
|
|
2117
|
+
x = MAX_WORKGROUPS_PER_DIM;
|
|
2118
|
+
y = (groups + MAX_WORKGROUPS_PER_DIM - 1u) / MAX_WORKGROUPS_PER_DIM;
|
|
2119
|
+
}
|
|
2120
|
+
let base = 4u * P.slot; // 16-byte slots: (x, y, 1, count) (PD-2)
|
|
2121
|
+
args[base] = x;
|
|
2122
|
+
args[base + 1u] = y;
|
|
2123
|
+
args[base + 2u] = 1u;
|
|
2124
|
+
args[base + 3u] = count;
|
|
2125
|
+
}
|
|
2126
|
+
`,El=`
|
|
2127
|
+
var<workgroup> moved: atomic<u32>;
|
|
2128
|
+
|
|
2129
|
+
@compute @workgroup_size(WG)
|
|
2130
|
+
fn lpa_step(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2131
|
+
if (lid.x == 0u) { atomicStore(&moved, 0u); }
|
|
2132
|
+
workgroupBarrier();
|
|
2133
|
+
let v = linear_id(wid, lid.x);
|
|
2134
|
+
if (v < P.n) {
|
|
2135
|
+
let cur = labelsIn[v];
|
|
2136
|
+
let best = bestKey[v];
|
|
2137
|
+
var next = cur;
|
|
2138
|
+
if (best != INVALID_INDEX && best != cur) {
|
|
2139
|
+
let down = best < cur;
|
|
2140
|
+
if (down == (P.direction == 0u)) { next = best; } // the alternating direction rule
|
|
2141
|
+
}
|
|
2142
|
+
labelsOut[v] = next;
|
|
2143
|
+
if (next != cur) { atomicAdd(&moved, 1u); }
|
|
2144
|
+
}
|
|
2145
|
+
workgroupBarrier(); // every lane, unconditionally
|
|
2146
|
+
if (lid.x == 0u) {
|
|
2147
|
+
let m = atomicLoad(&moved);
|
|
2148
|
+
if (m > 0u) { atomicAdd(&counters[P.counterIndex], m); } // one atomic per workgroup
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
`,Il=`
|
|
2152
|
+
@compute @workgroup_size(WG)
|
|
2153
|
+
fn mst_best(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2154
|
+
let e = linear_id(wid, lid.x);
|
|
2155
|
+
if (e >= P.count) { return; }
|
|
2156
|
+
let cu = comp[edgeSrc[e]];
|
|
2157
|
+
let cv = comp[edgeDst[e]];
|
|
2158
|
+
if (cu == cv) { return; } // inside one component, or a self-loop
|
|
2159
|
+
var w = 1.0;
|
|
2160
|
+
if (WEIGHTED) { w = edgeWeight[e]; }
|
|
2161
|
+
let k = order_key(w);
|
|
2162
|
+
if (PASS == 0u) {
|
|
2163
|
+
atomicMin(&bestKey[cu], k);
|
|
2164
|
+
atomicMin(&bestKey[cv], k);
|
|
2165
|
+
} else {
|
|
2166
|
+
if (k == atomicLoad(&bestKey[cu])) { atomicMin(&bestEdge[cu], e); }
|
|
2167
|
+
if (k == atomicLoad(&bestKey[cv])) { atomicMin(&bestEdge[cv], e); }
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
`,kl=`
|
|
2171
|
+
var<workgroup> added: atomic<u32>;
|
|
2172
|
+
|
|
2173
|
+
@compute @workgroup_size(WG)
|
|
2174
|
+
fn mst_link(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2175
|
+
if (lid.x == 0u) { atomicStore(&added, 0u); }
|
|
2176
|
+
workgroupBarrier();
|
|
2177
|
+
let c = linear_id(wid, lid.x);
|
|
2178
|
+
if (c < P.count) {
|
|
2179
|
+
let e = bestEdge[c];
|
|
2180
|
+
if (e != INVALID_INDEX) {
|
|
2181
|
+
let a = atomicLoad(&comp[edgeSrc[e]]);
|
|
2182
|
+
let b = atomicLoad(&comp[edgeDst[e]]);
|
|
2183
|
+
let other = select(a, b, a == c);
|
|
2184
|
+
let keep = bestEdge[other] == e && c <= other; // the lower root of a two-cycle stays
|
|
2185
|
+
if (!keep) { atomicStore(&comp[c], other); }
|
|
2186
|
+
if (!keep) { treeEdge[c] = e; atomicAdd(&added, 1u); }
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
workgroupBarrier(); // every lane, unconditionally
|
|
2190
|
+
if (lid.x == 0u) {
|
|
2191
|
+
let k = atomicLoad(&added);
|
|
2192
|
+
if (k > 0u) { atomicAdd(&counters[P.counterIndex], k); } // one atomic per workgroup
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
`,Al=`
|
|
2196
|
+
@compute @workgroup_size(WG)
|
|
2197
|
+
fn orient_flags(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2198
|
+
let a = linear_id(wid, lid.x);
|
|
2199
|
+
if (a >= P.count) { return; } // no barrier follows
|
|
2200
|
+
let u = src[a];
|
|
2201
|
+
let v = colIdx[a];
|
|
2202
|
+
let du = rowPtr[u + 1u] - rowPtr[u];
|
|
2203
|
+
let dv = rowPtr[v + 1u] - rowPtr[v];
|
|
2204
|
+
let keep = dv > du || (dv == du && v > u); // (degree, id) of the target above the source's
|
|
2205
|
+
flags[a] = select(0u, 1u, keep);
|
|
2206
|
+
}
|
|
2207
|
+
`,xl=`
|
|
2208
|
+
@compute @workgroup_size(WG)
|
|
2209
|
+
fn pr_finalize(@builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2210
|
+
var d = 0.0;
|
|
2211
|
+
var e = 0.0;
|
|
2212
|
+
var m = 0.0;
|
|
2213
|
+
for (var g = lid.x; g < P.groups; g = g + WG) {
|
|
2214
|
+
d = d + partials[1u + g].danglingMass;
|
|
2215
|
+
e = e + partials[1u + g].delta;
|
|
2216
|
+
m = m + partials[1u + g].norm;
|
|
2217
|
+
}
|
|
2218
|
+
let folded = wg_reduce_vec4(vec4f(d, e, m, 0.0), lid.x, 0u);
|
|
2219
|
+
if (lid.x == 0u) {
|
|
2220
|
+
partials[0].danglingMass = folded.x;
|
|
2221
|
+
partials[0].delta = folded.y;
|
|
2222
|
+
var norm = folded.z;
|
|
2223
|
+
if (NORM_MODE == 2u) { norm = sqrt(max(0.0, folded.z)); }
|
|
2224
|
+
partials[0].norm = norm;
|
|
2225
|
+
partials[0].iteration = P.iteration;
|
|
2226
|
+
let unset = partials[0].firstConverged == U32_MAX;
|
|
2227
|
+
if (P.trackConvergence == 1u && P.iteration >= 2u && folded.y < P.convergeThreshold && unset) {
|
|
2228
|
+
partials[0].firstConverged = P.iteration - 1u;
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
`,Rl=`
|
|
2233
|
+
@compute @workgroup_size(WG)
|
|
2234
|
+
fn pr_scale(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2235
|
+
let u = linear_id(wid, lid.x);
|
|
2236
|
+
let inRange = u < P.n;
|
|
2237
|
+
var x = 0.0;
|
|
2238
|
+
var prev = 0.0;
|
|
2239
|
+
if (inRange) { x = rankIn[u]; prev = rankPrev[u]; }
|
|
2240
|
+
var dangling = 0.0;
|
|
2241
|
+
var delta = 0.0;
|
|
2242
|
+
var normTerm = 0.0;
|
|
2243
|
+
if (inRange) {
|
|
2244
|
+
delta = abs(x - prev);
|
|
2245
|
+
if (NORM_MODE == 0u) {
|
|
2246
|
+
let divisor = outWeightSum[u];
|
|
2247
|
+
if (divisor <= 0.0) { dangling = x; xNorm[u] = 0.0; } else { xNorm[u] = x / divisor; }
|
|
2248
|
+
}
|
|
2249
|
+
if (NORM_MODE == 1u) { normTerm = abs(x); }
|
|
2250
|
+
if (NORM_MODE == 2u) { normTerm = x * x; }
|
|
2251
|
+
if (NORM_MODE == 3u) {
|
|
2252
|
+
var scale = partials[0].norm;
|
|
2253
|
+
if (scale <= 0.0) { scale = 1.0; }
|
|
2254
|
+
xNorm[u] = x / scale;
|
|
2255
|
+
}
|
|
2256
|
+
if (NORM_MODE == 4u) { xNorm[u] = x; }
|
|
2257
|
+
}
|
|
2258
|
+
let folded = wg_reduce_vec4(vec4f(dangling, delta, normTerm, 0.0), lid.x, 0u);
|
|
2259
|
+
if (lid.x == 0u) {
|
|
2260
|
+
let slot = 1u + group_id(wid);
|
|
2261
|
+
partials[slot].danglingMass = folded.x;
|
|
2262
|
+
partials[slot].delta = folded.y;
|
|
2263
|
+
partials[slot].norm = folded.z;
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
`,Ll=`
|
|
2267
|
+
var<workgroup> local: array<atomic<u32>, RADIX_BINS>;
|
|
2268
|
+
|
|
2269
|
+
@compute @workgroup_size(WG)
|
|
2270
|
+
fn radix_hist(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2271
|
+
for (var b = lid.x; b < RADIX_BINS; b = b + WG) { atomicStore(&local[b], 0u); }
|
|
2272
|
+
workgroupBarrier();
|
|
2273
|
+
let g = group_id(wid);
|
|
2274
|
+
let i = g * WG + lid.x;
|
|
2275
|
+
if (i < P.count) {
|
|
2276
|
+
let d = (keys[i] >> P.shift) & RADIX_DIGIT_MASK; // the pass's digit
|
|
2277
|
+
atomicAdd(&local[d], 1u);
|
|
2278
|
+
}
|
|
2279
|
+
workgroupBarrier();
|
|
2280
|
+
// Above MAX_1D_ITEMS plan1d pads the 2D grid to x * y >= P.groups workgroups; a padding workgroup (g >= P.groups)
|
|
2281
|
+
// holds an all-zero table and its digit-major slot b * P.groups + g is digit b + 1's slot of a REAL group, so it
|
|
2282
|
+
// must not store. Uniform per workgroup, no barrier inside. Every test size fits 1D (the ladder tops at 2^22); no
|
|
2283
|
+
// case reaches this branch, so the guard is proved by reading, not by a run.
|
|
2284
|
+
if (g < P.groups) {
|
|
2285
|
+
for (var b = lid.x; b < RADIX_BINS; b = b + WG) { hist[b * P.groups + g] = atomicLoad(&local[b]); } // digit-major
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
`,Dl=`
|
|
2289
|
+
var<workgroup> rank: array<u32, WG>;
|
|
2290
|
+
var<workgroup> cnt: array<u32, RADIX_BINS>;
|
|
2291
|
+
|
|
2292
|
+
@compute @workgroup_size(WG)
|
|
2293
|
+
fn radix_scatter(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2294
|
+
for (var b = lid.x; b < RADIX_BINS; b = b + WG) { cnt[b] = 0u; }
|
|
2295
|
+
let g = group_id(wid);
|
|
2296
|
+
let i = g * WG + lid.x;
|
|
2297
|
+
var key = 0u;
|
|
2298
|
+
var d = 0u;
|
|
2299
|
+
if (i < P.count) {
|
|
2300
|
+
key = keys[i];
|
|
2301
|
+
d = (key >> P.shift) & RADIX_DIGIT_MASK;
|
|
2302
|
+
}
|
|
2303
|
+
workgroupBarrier();
|
|
2304
|
+
if (lid.x == 0u) { // serial stable ranking (PD-5)
|
|
2305
|
+
let last = min(WG, P.count - g * WG);
|
|
2306
|
+
for (var s = 0u; s < last; s = s + 1u) {
|
|
2307
|
+
let ds = (keys[g * WG + s] >> P.shift) & RADIX_DIGIT_MASK;
|
|
2308
|
+
rank[s] = cnt[ds];
|
|
2309
|
+
cnt[ds] = cnt[ds] + 1u;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
workgroupBarrier();
|
|
2313
|
+
if (i < P.count) {
|
|
2314
|
+
let dst = offsets[d * P.groups + g] + rank[lid.x];
|
|
2315
|
+
keysOut[dst] = key;
|
|
2316
|
+
valsOut[dst] = vals[i];
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
`,Tl=`
|
|
2320
|
+
// DTYPE 0 = f32, 1 = u32, 2 = vec4f (4 words per element); OP 0 = sum, 1 = min, 2 = max; FINAL = the one-workgroup level
|
|
2321
|
+
fn identity_f() -> f32 { if (OP == 1u) { return F32_MAX; } if (OP == 2u) { return -F32_MAX; } return 0.0; }
|
|
2322
|
+
fn identity_u() -> u32 { if (OP == 1u) { return U32_MAX; } return 0u; } // U32_MAX from the prelude: the literal is forbidden in bodies (4.1)
|
|
2323
|
+
fn comb_f(a: f32, b: f32) -> f32 { if (OP == 1u) { return min(a, b); } if (OP == 2u) { return max(a, b); } return a + b; }
|
|
2324
|
+
fn comb_u(a: u32, b: u32) -> u32 { if (OP == 1u) { return min(a, b); } if (OP == 2u) { return max(a, b); } return a + b; }
|
|
2325
|
+
fn comb_v(a: vec4f, b: vec4f) -> vec4f { if (OP == 1u) { return min(a, b); } if (OP == 2u) { return max(a, b); } return a + b; }
|
|
2326
|
+
fn load_f(i: u32) -> f32 { return bitcast<f32>(src[i]); }
|
|
2327
|
+
fn load_v(i: u32) -> vec4f {
|
|
2328
|
+
return vec4f(bitcast<f32>(src[4u * i]), bitcast<f32>(src[4u * i + 1u]), bitcast<f32>(src[4u * i + 2u]), bitcast<f32>(src[4u * i + 3u]));
|
|
2329
|
+
}
|
|
2330
|
+
fn store_f(i: u32, v: f32) { out[i] = bitcast<u32>(v); }
|
|
2331
|
+
fn store_v(i: u32, v: vec4f) {
|
|
2332
|
+
out[4u * i] = bitcast<u32>(v.x);
|
|
2333
|
+
out[4u * i + 1u] = bitcast<u32>(v.y);
|
|
2334
|
+
out[4u * i + 2u] = bitcast<u32>(v.z);
|
|
2335
|
+
out[4u * i + 3u] = bitcast<u32>(v.w);
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
@compute @workgroup_size(WG)
|
|
2339
|
+
fn reduce(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2340
|
+
var accF = identity_f();
|
|
2341
|
+
var accU = identity_u();
|
|
2342
|
+
var accV = vec4f(identity_f());
|
|
2343
|
+
if (FINAL) {
|
|
2344
|
+
for (var i = lid.x; i < P.count; i = i + WG) { // sequential per lane in index order: deterministic
|
|
2345
|
+
if (DTYPE == 0u) { accF = comb_f(accF, load_f(i)); }
|
|
2346
|
+
else if (DTYPE == 1u) { accU = comb_u(accU, src[i]); }
|
|
2347
|
+
else { accV = comb_v(accV, load_v(i)); }
|
|
2348
|
+
}
|
|
2349
|
+
} else {
|
|
2350
|
+
let i = linear_id(wid, lid.x);
|
|
2351
|
+
if (i < P.count) {
|
|
2352
|
+
if (DTYPE == 0u) { accF = load_f(i); }
|
|
2353
|
+
else if (DTYPE == 1u) { accU = src[i]; }
|
|
2354
|
+
else { accV = load_v(i); }
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
// uniform control flow: the workgroup reduction of the selected dtype (DTYPE is a pipeline constant, so the branch is uniform)
|
|
2358
|
+
var tF = 0.0;
|
|
2359
|
+
var tU = 0u;
|
|
2360
|
+
var tV = vec4f(0.0);
|
|
2361
|
+
if (DTYPE == 0u) { tF = wg_reduce_f32(accF, lid.x, OP); }
|
|
2362
|
+
else if (DTYPE == 1u) { tU = wg_reduce_u32(accU, lid.x, OP); }
|
|
2363
|
+
else { tV = wg_reduce_vec4(accV, lid.x, OP); }
|
|
2364
|
+
if (lid.x == 0u) {
|
|
2365
|
+
let g = select(group_id(wid), 0u, FINAL);
|
|
2366
|
+
let o = P.outOffset + g;
|
|
2367
|
+
if (DTYPE == 0u) { store_f(o, tF); }
|
|
2368
|
+
else if (DTYPE == 1u) { out[o] = tU; }
|
|
2369
|
+
else { store_v(o, tV); }
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
`,Ol=`
|
|
2373
|
+
@compute @workgroup_size(WG)
|
|
2374
|
+
fn run_flags(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2375
|
+
let i = linear_id(wid, lid.x);
|
|
2376
|
+
if (i >= P.count) { return; } // no barrier follows
|
|
2377
|
+
let a = keysA[i];
|
|
2378
|
+
var first = a != INVALID_INDEX; // a dropped arc never opens a run
|
|
2379
|
+
if (i > 0u) { first = first && (a != keysA[i - 1u] || keysB[i] != keysB[i - 1u]); }
|
|
2380
|
+
flags[i] = select(0u, 1u, first);
|
|
2381
|
+
}
|
|
2382
|
+
`,Nl=`
|
|
2383
|
+
@compute @workgroup_size(WG)
|
|
2384
|
+
fn scan_add(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2385
|
+
let g = group_id(wid);
|
|
2386
|
+
let i = g * WG + lid.x;
|
|
2387
|
+
if (i >= P.count) { return; } // no barrier follows
|
|
2388
|
+
out[i] = out[i] + blockOffsets[g];
|
|
2389
|
+
}
|
|
2390
|
+
`,Bl=`
|
|
2391
|
+
var<workgroup> sh: array<u32, WG>;
|
|
2392
|
+
|
|
2393
|
+
@compute @workgroup_size(WG)
|
|
2394
|
+
fn scan_block(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2395
|
+
let g = group_id(wid);
|
|
2396
|
+
let i = g * WG + lid.x;
|
|
2397
|
+
var v = 0u;
|
|
2398
|
+
if (i < P.count) { v = src[i]; }
|
|
2399
|
+
sh[lid.x] = v;
|
|
2400
|
+
workgroupBarrier();
|
|
2401
|
+
for (var s = 1u; s < WG; s = s * 2u) { // Hillis-Steele inclusive scan; uniform: every lane runs every round
|
|
2402
|
+
var t = 0u;
|
|
2403
|
+
if (lid.x >= s) { t = sh[lid.x - s]; }
|
|
2404
|
+
workgroupBarrier();
|
|
2405
|
+
sh[lid.x] = sh[lid.x] + t;
|
|
2406
|
+
workgroupBarrier();
|
|
2407
|
+
}
|
|
2408
|
+
let inclusive = sh[lid.x];
|
|
2409
|
+
if (i < P.count) { out[i] = inclusive - v; } // exclusive = inclusive - own value
|
|
2410
|
+
if (lid.x == WG - 1u) { blockSums[g] = inclusive; } // the block total (the last lane's inclusive sum)
|
|
2411
|
+
}
|
|
2412
|
+
`,$l=`
|
|
2413
|
+
fn identity() -> f32 { if (OP == 1u) { return F32_MAX; } if (OP == 2u) { return -F32_MAX; } return 0.0; }
|
|
2414
|
+
fn comb(a: f32, b: f32) -> f32 { if (OP == 1u) { return min(a, b); } if (OP == 2u) { return max(a, b); } return a + b; }
|
|
2415
|
+
fn row_node(row: u32) -> u32 { return select(row, perm[row], USE_PERM); }
|
|
2416
|
+
fn row_fold(i: u32, lane: u32, step: u32) -> f32 { // the arcs of row i this lane walks inside the bound window [P.arcBase, P.arcEnd)
|
|
2417
|
+
let row = i; // the CSR row the VALUE snippet may name (the node index, under USE_PERM too)
|
|
2418
|
+
let a0 = max(rowPtr[i], P.arcBase);
|
|
2419
|
+
let a1 = min(rowPtr[i + 1u], P.arcEnd);
|
|
2420
|
+
var acc = identity();
|
|
2421
|
+
for (var arc = a0 + lane; arc < a1; arc = arc + step) {
|
|
2422
|
+
let nbr = colIdx[arc - P.arcBase]; // the neighbour index (\`target\` is a WGSL reserved word)
|
|
2423
|
+
var weight = 1.0;
|
|
2424
|
+
if (HAS_WEIGHTS) { weight = weights[arc - P.arcBase]; }
|
|
2425
|
+
var v = 0.0;
|
|
2426
|
+
//@@VALUE@@
|
|
2427
|
+
acc = comb(acc, v);
|
|
2428
|
+
}
|
|
2429
|
+
return acc;
|
|
2430
|
+
}
|
|
2431
|
+
fn row_fold_dense(i: u32) -> f32 { // TIER 0's stride-one twin of row_fold (see the header)
|
|
2432
|
+
let row = i; // the CSR row the VALUE snippet may name (as in row_fold)
|
|
2433
|
+
let lo = max(rowPtr[i], P.arcBase);
|
|
2434
|
+
let hi = min(rowPtr[i + 1u], P.arcEnd);
|
|
2435
|
+
var acc = identity();
|
|
2436
|
+
for (var left = select(0u, hi - lo, hi > lo); left > 0u; left = left - 1u) { // counts DOWN (see the header)
|
|
2437
|
+
let arc = hi - left; // lo, lo + 1, ..., hi - 1
|
|
2438
|
+
let k = arc - P.arcBase; // the window-local index; this walk is contiguous
|
|
2439
|
+
let nbr = colIdx[k];
|
|
2440
|
+
var weight = 1.0;
|
|
2441
|
+
if (HAS_WEIGHTS) { weight = weights[k]; }
|
|
2442
|
+
var v = 0.0;
|
|
2443
|
+
//@@VALUE@@
|
|
2444
|
+
acc = comb(acc, v);
|
|
2445
|
+
}
|
|
2446
|
+
return acc;
|
|
2447
|
+
}
|
|
2448
|
+
fn finish(i: u32, acc: f32) { out[i] = select(acc, comb(out[i], acc), P.accumulate == 1u); }
|
|
2449
|
+
fn tier0(wid: vec3<u32>, lane: u32) { // TIER 0: one row per thread over [P.start, P.end); no barrier, so the early return is legal (3.5 rule 1)
|
|
2450
|
+
let row = linear_id(wid, lane) + P.start;
|
|
2451
|
+
if (row >= P.end) { return; }
|
|
2452
|
+
let i = row_node(row);
|
|
2453
|
+
finish(i, row_fold_dense(i));
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
var<workgroup> sh: array<f32, WG>;
|
|
2457
|
+
|
|
2458
|
+
fn tiered(wid: vec3<u32>, lid: u32) { // TIER 1: 32 lanes per row, WG / 32 rows per workgroup; TIER 2: WG lanes per row (PD-6)
|
|
2459
|
+
let g = group_id(wid);
|
|
2460
|
+
var row = P.start + g;
|
|
2461
|
+
var lane = lid;
|
|
2462
|
+
var step = WG;
|
|
2463
|
+
if (TIER == 1u) { row = P.start + g * (WG / 32u) + lid / 32u; lane = lid % 32u; step = 32u; }
|
|
2464
|
+
let valid = row < P.end;
|
|
2465
|
+
var i = 0u;
|
|
2466
|
+
var acc = identity();
|
|
2467
|
+
if (valid) { i = row_node(row); acc = row_fold(i, lane, step); }
|
|
2468
|
+
if (TIER == 1u) {
|
|
2469
|
+
sh[lid] = acc;
|
|
2470
|
+
workgroupBarrier();
|
|
2471
|
+
for (var s = 16u; s >= 1u; s = s / 2u) { // the five-step tree over each 32-lane group; every lane runs every step
|
|
2472
|
+
var t = identity();
|
|
2473
|
+
if (lane < s) { t = sh[lid + s]; }
|
|
2474
|
+
workgroupBarrier();
|
|
2475
|
+
sh[lid] = comb(sh[lid], t);
|
|
2476
|
+
workgroupBarrier();
|
|
2477
|
+
}
|
|
2478
|
+
if (valid && lane == 0u) { finish(i, sh[lid]); }
|
|
2479
|
+
}
|
|
2480
|
+
if (TIER == 2u) {
|
|
2481
|
+
let t = wg_reduce_f32(acc, lid, OP); // the workgroup tree of the prelude (subgroup variant when available)
|
|
2482
|
+
if (valid && lid == 0u) { finish(i, t); }
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
@compute @workgroup_size(WG)
|
|
2487
|
+
fn segmented_reduce(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2488
|
+
if (TIER == 0u) { tier0(wid, lid.x); } else { tiered(wid, lid.x); }
|
|
2489
|
+
}
|
|
2490
|
+
`,Cl=`
|
|
2491
|
+
fn row_node(row: u32) -> u32 { return select(row, perm[row], USE_PERM); }
|
|
2492
|
+
fn row_sum(v: u32, lane: u32, step: u32) -> f32 { // this lane's arcs of row v inside the bound window, the two-level 64-term fold of the header
|
|
2493
|
+
let a0 = max(rowPtr[v], P.arcBase);
|
|
2494
|
+
let a1 = min(rowPtr[v + 1u], P.arcEnd);
|
|
2495
|
+
var acc = 0.0;
|
|
2496
|
+
var chunk = 0.0;
|
|
2497
|
+
var inChunk = 0u;
|
|
2498
|
+
for (var arc = a0 + lane; arc < a1; arc = arc + step) {
|
|
2499
|
+
let nbr = colIdx[arc - P.arcBase]; // \`target\` is a WGSL reserved word (spec 16.2)
|
|
2500
|
+
var weight = 1.0;
|
|
2501
|
+
if (HAS_WEIGHTS) { weight = weights[arc - P.arcBase]; }
|
|
2502
|
+
// two-level sum: 64 terms into chunk, chunk into acc (see the header; no compensation, no select)
|
|
2503
|
+
chunk = chunk + (weight * xNorm[nbr]);
|
|
2504
|
+
inChunk = inChunk + 1u;
|
|
2505
|
+
if (inChunk == 64u) {
|
|
2506
|
+
acc = acc + chunk;
|
|
2507
|
+
chunk = 0.0;
|
|
2508
|
+
inChunk = 0u;
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
acc = acc + chunk;
|
|
2512
|
+
return acc;
|
|
2513
|
+
}
|
|
2514
|
+
fn row_sum_dense(v: u32) -> f32 { // TIER 0's stride-one twin of row_sum (see the header)
|
|
2515
|
+
let lo = max(rowPtr[v], P.arcBase);
|
|
2516
|
+
let hi = min(rowPtr[v + 1u], P.arcEnd);
|
|
2517
|
+
var acc = 0.0;
|
|
2518
|
+
var chunk = 0.0;
|
|
2519
|
+
var inChunk = 0u;
|
|
2520
|
+
for (var left = select(0u, hi - lo, hi > lo); left > 0u; left = left - 1u) { // counts DOWN (see the header)
|
|
2521
|
+
let arc = hi - left; // lo, lo + 1, ..., hi - 1
|
|
2522
|
+
let k = arc - P.arcBase; // the window-local index; this walk is contiguous
|
|
2523
|
+
let nbr = colIdx[k]; // \`target\` is a WGSL reserved word (spec 16.2)
|
|
2524
|
+
var weight = 1.0;
|
|
2525
|
+
if (HAS_WEIGHTS) { weight = weights[k]; }
|
|
2526
|
+
// two-level sum: 64 terms into chunk, chunk into acc (see the header; no compensation, no select)
|
|
2527
|
+
chunk = chunk + (weight * xNorm[nbr]);
|
|
2528
|
+
inChunk = inChunk + 1u;
|
|
2529
|
+
if (inChunk == 64u) {
|
|
2530
|
+
acc = acc + chunk;
|
|
2531
|
+
chunk = 0.0;
|
|
2532
|
+
inChunk = 0u;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
acc = acc + chunk;
|
|
2536
|
+
return acc;
|
|
2537
|
+
}
|
|
2538
|
+
fn finish(v: u32, acc: f32) {
|
|
2539
|
+
var dangling = 0.0;
|
|
2540
|
+
if (USE_DANGLING) { dangling = partials[0].danglingMass; }
|
|
2541
|
+
var pv = P.uniformP;
|
|
2542
|
+
if (HAS_PERSONALIZATION) { pv = personalization[v]; }
|
|
2543
|
+
rankOut[v] = (P.beta * pv) + (P.alpha * (acc + (dangling * pv)));
|
|
2544
|
+
}
|
|
2545
|
+
fn tier0(wid: vec3<u32>, lane: u32) { // TIER 0: grid-stride over the rows [P.start, P.n); no barrier
|
|
2546
|
+
for (var row = linear_id(wid, lane) + P.start; row < P.n; row = row + P.stride) {
|
|
2547
|
+
let v = row_node(row);
|
|
2548
|
+
finish(v, row_sum_dense(v));
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
var<workgroup> sh: array<f32, WG>;
|
|
2553
|
+
|
|
2554
|
+
fn tiered(wid: vec3<u32>, lid: u32) { // TIER 1: 32 lanes per row; TIER 2: WG lanes per row; rows [P.start, P.n) (PD-6)
|
|
2555
|
+
let g = group_id(wid);
|
|
2556
|
+
var row = P.start + g;
|
|
2557
|
+
var lane = lid;
|
|
2558
|
+
var step = WG;
|
|
2559
|
+
if (TIER == 1u) { row = P.start + g * (WG / 32u) + lid / 32u; lane = lid % 32u; step = 32u; }
|
|
2560
|
+
let valid = row < P.n;
|
|
2561
|
+
var v = 0u;
|
|
2562
|
+
var acc = 0.0;
|
|
2563
|
+
if (valid) { v = row_node(row); acc = row_sum(v, lane, step); }
|
|
2564
|
+
if (TIER == 1u) {
|
|
2565
|
+
sh[lid] = acc;
|
|
2566
|
+
workgroupBarrier();
|
|
2567
|
+
for (var s = 16u; s >= 1u; s = s / 2u) { // the five-step tree over each 32-lane group; every lane runs every step
|
|
2568
|
+
var t = 0.0;
|
|
2569
|
+
if (lane < s) { t = sh[lid + s]; }
|
|
2570
|
+
workgroupBarrier();
|
|
2571
|
+
sh[lid] = sh[lid] + t;
|
|
2572
|
+
workgroupBarrier();
|
|
2573
|
+
}
|
|
2574
|
+
if (valid && lane == 0u) { finish(v, sh[lid]); }
|
|
2575
|
+
}
|
|
2576
|
+
if (TIER == 2u) {
|
|
2577
|
+
let t = wg_reduce_f32(acc, lid, 0u);
|
|
2578
|
+
if (valid && lid == 0u) { finish(v, t); }
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2582
|
+
@compute @workgroup_size(WG)
|
|
2583
|
+
fn spmv_pull(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2584
|
+
if (TIER == 0u) { tier0(wid, lid.x); } else { tiered(wid, lid.x); }
|
|
2585
|
+
}
|
|
2586
|
+
`,Ml=`
|
|
2587
|
+
@compute @workgroup_size(WG)
|
|
2588
|
+
fn sssp_pred(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2589
|
+
let first = linear_id(wid, lid.x);
|
|
2590
|
+
let hb = ((P.n + 63u) / 64u) * 64u; // MODE 0 only: hops[v] is pred[hb + v]; pred[2 hb] is the changed word, pred[2 hb + 1] the orphan word (PD-27)
|
|
2591
|
+
if (P.role == 1u && P.iteration != 0u && atomicLoad(&pred[2u * hb]) < P.iteration) { return; } // the previous hop pass changed nothing: converged (no barrier anywhere, so the early return is legal)
|
|
2592
|
+
for (var u = first; u < P.n; u = u + P.stride) { // grid-stride over the rows (planGridStride(n)); no barrier anywhere
|
|
2593
|
+
let du = dist[u];
|
|
2594
|
+
var unreached = false;
|
|
2595
|
+
if (MODE == 1u) { unreached = du == INVALID_INDEX; } else { unreached = du == F32_INF_BITS; }
|
|
2596
|
+
if (unreached) { continue; }
|
|
2597
|
+
var hu = 0u; // u's hop count (MODE 0, roles 1 and 2)
|
|
2598
|
+
if (MODE == 0u && P.role != 0u) {
|
|
2599
|
+
hu = atomicLoad(&pred[hb + u]);
|
|
2600
|
+
if (hu == INVALID_INDEX) { // the key has not reached u yet
|
|
2601
|
+
if (P.role == 2u && u != P.source) { atomicAdd(&pred[2u * hb + 1u], 1u); } // an orphan: only bellmanFord can make one (P8-T10 Step 3)
|
|
2602
|
+
continue;
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
let end = min(rowPtr[u + 1u], P.arcEnd);
|
|
2606
|
+
for (var a = max(rowPtr[u], P.arcBase); a < end; a = a + 1u) {
|
|
2607
|
+
let v = colIdx[a - P.arcBase];
|
|
2608
|
+
if (v == P.source) { continue; } // the source keeps INVALID_INDEX whatever attains it (a zero-weight arc could)
|
|
2609
|
+
let dv = dist[v];
|
|
2610
|
+
var tight = false; // the arc explains dist[v]
|
|
2611
|
+
var below = false; // and its source sits at a strictly smaller distance
|
|
2612
|
+
if (MODE == 1u) {
|
|
2613
|
+
tight = (du + 1u) == dv; // depth mode: BFS parent, one depth down
|
|
2614
|
+
} else {
|
|
2615
|
+
let w = select(1.0, weights[a - P.arcBase], HAS_WEIGHTS);
|
|
2616
|
+
tight = (dv != F32_INF_BITS) && (bitcast<u32>(bitcast<f32>(du) + w) == dv); // one f32 add, compared as the bit pattern PD-9 stores; never into an unreached v (an overflowed sum is +Inf too)
|
|
2617
|
+
below = bitcast<f32>(du) < bitcast<f32>(dv);
|
|
2618
|
+
}
|
|
2619
|
+
if (!tight) { continue; }
|
|
2620
|
+
var admit = false; // this arc is one key step below v
|
|
2621
|
+
if (MODE == 1u) {
|
|
2622
|
+
admit = true;
|
|
2623
|
+
} else if (P.role == 0u) { // the roots pass (the plateau rule only; bellmanFord seeds the source alone)
|
|
2624
|
+
if (P.mode == 0u && below) { atomicMin(&pred[hb + v], 0u); }
|
|
2625
|
+
} else if (P.role == 1u) { // a hop pass: one hop along a plateau arc (mode 0) or along any tight arc (mode 1)
|
|
2626
|
+
if (P.mode == 1u || du == dv) {
|
|
2627
|
+
let old = atomicMin(&pred[hb + v], hu + 1u);
|
|
2628
|
+
if (hu + 1u < old) { atomicMax(&pred[2u * hb], P.iteration + 1u); } // this pass changed something
|
|
2629
|
+
}
|
|
2630
|
+
} else { // the predecessor pass: the smallest tight arc one key step below v
|
|
2631
|
+
let hv = atomicLoad(&pred[hb + v]);
|
|
2632
|
+
if (P.mode == 1u) { admit = hu + 1u == hv; } else if (hv == 0u) { admit = below; } else { admit = (du == dv) && (hu + 1u == hv); }
|
|
2633
|
+
}
|
|
2634
|
+
if (admit) { atomicMin(&pred[v], select(a, u, P.predKind == 1u)); }
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
`,Gl=`
|
|
2639
|
+
@compute @workgroup_size(WG)
|
|
2640
|
+
fn sssp_relax(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2641
|
+
let first = linear_id(wid, lid.x);
|
|
2642
|
+
let mine = select(5u, 6u, P.role == 1u); // the path word role 2 wrote: 5 a near round, 6 a far pass-through
|
|
2643
|
+
let chosen = atomicLoad(&counters[24]) == mine; // the other role's dispatch of the round is a no-op
|
|
2644
|
+
let count = select(0u, min(atomicLoad(&counters[select(0u, 20u, P.role == 1u)]), P.n), chosen); // the deduped near or far pile
|
|
2645
|
+
let threshold = bitcast<f32>(atomicLoad(&counters[22]));
|
|
2646
|
+
let cutoff = bitcast<f32>(P.cutoffBits);
|
|
2647
|
+
for (var i = first; i < count; i = i + P.stride) { // no barrier anywhere: the loops may be per lane
|
|
2648
|
+
let u = queueIn[i];
|
|
2649
|
+
let du = bitcast<f32>(atomicLoad(&dist[u]));
|
|
2650
|
+
if (P.role == 1u) { // the pass-through (PD-20): re-bucket a far entry
|
|
2651
|
+
if (du < bitcast<f32>(atomicLoad(&counters[4]))) { continue; } // below the previous threshold: relaxed in an earlier bucket
|
|
2652
|
+
if (du < threshold) {
|
|
2653
|
+
let q = atomicAdd(&counters[1], 1u);
|
|
2654
|
+
if (q < P.edgeCapacity) { queueOut[q] = u; }
|
|
2655
|
+
} else {
|
|
2656
|
+
let q = atomicAdd(&counters[21], 1u);
|
|
2657
|
+
if (q < P.edgeCapacity) { queueOut[P.edgeCapacity + q] = u; }
|
|
2658
|
+
}
|
|
2659
|
+
continue;
|
|
2660
|
+
}
|
|
2661
|
+
let end = rowPtr[u + 1u];
|
|
2662
|
+
for (var a = rowPtr[u]; a < end; a = a + 1u) { // the whole row: never windowed (DEP-P8-E)
|
|
2663
|
+
let v = colIdx[a];
|
|
2664
|
+
let nd = du + select(1.0, weights[a], HAS_WEIGHTS); // ONE f32 add (PD-9)
|
|
2665
|
+
if (nd > cutoff) { continue; } // SsspOptions.cutoff: the CPU port's dv <= cutoff
|
|
2666
|
+
let bits = bitcast<u32>(nd);
|
|
2667
|
+
let old = atomicMin(&dist[v], bits); // exact on non-negative floats
|
|
2668
|
+
if (bits < old) { // this lane improved v, so it owns the append
|
|
2669
|
+
if (nd < threshold) {
|
|
2670
|
+
let q = atomicAdd(&counters[1], 1u);
|
|
2671
|
+
if (q < P.edgeCapacity) { queueOut[q] = v; }
|
|
2672
|
+
} else {
|
|
2673
|
+
let q = atomicAdd(&counters[21], 1u);
|
|
2674
|
+
if (q < P.edgeCapacity) { queueOut[P.edgeCapacity + q] = v; }
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
`,zl=`
|
|
2681
|
+
fn contains(lo0: u32, hi0: u32, x: u32) -> bool { // binary search of x in colIdx[lo0, hi0), sorted ascending
|
|
2682
|
+
var lo = lo0;
|
|
2683
|
+
var hi = hi0;
|
|
2684
|
+
loop {
|
|
2685
|
+
if (lo >= hi) { break; }
|
|
2686
|
+
let mid = lo + (hi - lo) / 2u;
|
|
2687
|
+
let y = colIdx[mid];
|
|
2688
|
+
if (y == x) { return true; }
|
|
2689
|
+
if (y < x) { lo = mid + 1u; } else { hi = mid; }
|
|
2690
|
+
}
|
|
2691
|
+
return false;
|
|
2692
|
+
}
|
|
2693
|
+
|
|
2694
|
+
@compute @workgroup_size(WG)
|
|
2695
|
+
fn tri_intersect(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2696
|
+
let a = linear_id(wid, lid.x);
|
|
2697
|
+
if (a >= P.count) { return; } // no barrier follows
|
|
2698
|
+
let u = src[a];
|
|
2699
|
+
let v = colIdx[a];
|
|
2700
|
+
let uLo = rowPtr[u];
|
|
2701
|
+
let uHi = rowPtr[u + 1u];
|
|
2702
|
+
let vLo = rowPtr[v];
|
|
2703
|
+
let vHi = rowPtr[v + 1u];
|
|
2704
|
+
let du = uHi - uLo;
|
|
2705
|
+
let dv = vHi - vLo;
|
|
2706
|
+
var search = SEARCH == 2u;
|
|
2707
|
+
if (SEARCH == 0u) { search = du / TRIANGLE_BINARY_SEARCH_RATIO > dv || dv / TRIANGLE_BINARY_SEARCH_RATIO > du; }
|
|
2708
|
+
var found = 0u;
|
|
2709
|
+
if (search) {
|
|
2710
|
+
var sLo = uLo;
|
|
2711
|
+
var sHi = uHi;
|
|
2712
|
+
var lLo = vLo;
|
|
2713
|
+
var lHi = vHi;
|
|
2714
|
+
if (du > dv) { sLo = vLo; sHi = vHi; lLo = uLo; lHi = uHi; }
|
|
2715
|
+
for (var k = sLo; k < sHi; k = k + 1u) {
|
|
2716
|
+
let w = colIdx[k];
|
|
2717
|
+
if (contains(lLo, lHi, w)) { atomicAdd(&counts[w], 1u); found = found + 1u; }
|
|
2718
|
+
}
|
|
2719
|
+
} else {
|
|
2720
|
+
var i = uLo;
|
|
2721
|
+
var j = vLo;
|
|
2722
|
+
loop {
|
|
2723
|
+
if (i >= uHi || j >= vHi) { break; }
|
|
2724
|
+
let x = colIdx[i];
|
|
2725
|
+
let y = colIdx[j];
|
|
2726
|
+
if (x == y) { atomicAdd(&counts[x], 1u); found = found + 1u; i = i + 1u; j = j + 1u; }
|
|
2727
|
+
else if (x < y) { i = i + 1u; }
|
|
2728
|
+
else { j = j + 1u; }
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
if (found > 0u) { atomicAdd(&counts[u], found); atomicAdd(&counts[v], found); }
|
|
2732
|
+
}
|
|
2733
|
+
`,Fl=`
|
|
2734
|
+
@compute @workgroup_size(WG)
|
|
2735
|
+
fn wcc_compress(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2736
|
+
let first = linear_id(wid, lid.x);
|
|
2737
|
+
for (var v = first; v < P.items; v = v + P.stride) {
|
|
2738
|
+
var root = atomicLoad(&comp[v]);
|
|
2739
|
+
var steps = 0u;
|
|
2740
|
+
loop {
|
|
2741
|
+
let parent = atomicLoad(&comp[root]);
|
|
2742
|
+
if (parent == root) { break; }
|
|
2743
|
+
if (steps >= P.maxSteps) { break; }
|
|
2744
|
+
steps = steps + 1u;
|
|
2745
|
+
root = parent;
|
|
2746
|
+
}
|
|
2747
|
+
atomicStore(&comp[v], root);
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
`,Wl=`
|
|
2751
|
+
fn link_pair(a: u32, b: u32) {
|
|
2752
|
+
var p1 = atomicLoad(&comp[a]);
|
|
2753
|
+
var p2 = atomicLoad(&comp[b]);
|
|
2754
|
+
var steps = 0u;
|
|
2755
|
+
loop {
|
|
2756
|
+
if (p1 == p2) { break; }
|
|
2757
|
+
if (steps >= P.maxSteps) { atomicStore(&comp[P.flagIndex], 1u); break; }
|
|
2758
|
+
steps = steps + 1u;
|
|
2759
|
+
let hi = max(p1, p2);
|
|
2760
|
+
let lo = min(p1, p2);
|
|
2761
|
+
let pHigh = atomicLoad(&comp[hi]);
|
|
2762
|
+
if (pHigh == lo) { break; }
|
|
2763
|
+
if (pHigh == hi) {
|
|
2764
|
+
let swapped = atomicCompareExchangeWeak(&comp[hi], hi, lo);
|
|
2765
|
+
if (swapped.exchanged) { atomicStore(&comp[P.flagIndex], 1u); break; }
|
|
2766
|
+
}
|
|
2767
|
+
p1 = atomicLoad(&comp[atomicLoad(&comp[hi])]);
|
|
2768
|
+
p2 = atomicLoad(&comp[lo]);
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2772
|
+
@compute @workgroup_size(WG)
|
|
2773
|
+
fn wcc_link_edges(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2774
|
+
let first = linear_id(wid, lid.x);
|
|
2775
|
+
for (var e = first; e < P.items; e = e + P.stride) {
|
|
2776
|
+
let u = edgeSrc[e];
|
|
2777
|
+
let v = edgeDst[e];
|
|
2778
|
+
if (u == v) { continue; }
|
|
2779
|
+
if (atomicLoad(&comp[u]) == P.giant && atomicLoad(&comp[v]) == P.giant) { continue; }
|
|
2780
|
+
link_pair(u, v);
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
`,Ul=`
|
|
2784
|
+
fn link_pair(a: u32, b: u32) {
|
|
2785
|
+
var p1 = atomicLoad(&comp[a]);
|
|
2786
|
+
var p2 = atomicLoad(&comp[b]);
|
|
2787
|
+
var steps = 0u;
|
|
2788
|
+
loop {
|
|
2789
|
+
if (p1 == p2) { break; }
|
|
2790
|
+
if (steps >= P.maxSteps) { atomicStore(&comp[P.flagIndex], 1u); break; }
|
|
2791
|
+
steps = steps + 1u;
|
|
2792
|
+
let hi = max(p1, p2);
|
|
2793
|
+
let lo = min(p1, p2);
|
|
2794
|
+
let pHigh = atomicLoad(&comp[hi]);
|
|
2795
|
+
if (pHigh == lo) { break; }
|
|
2796
|
+
if (pHigh == hi) {
|
|
2797
|
+
let swapped = atomicCompareExchangeWeak(&comp[hi], hi, lo);
|
|
2798
|
+
if (swapped.exchanged) { atomicStore(&comp[P.flagIndex], 1u); break; }
|
|
2799
|
+
}
|
|
2800
|
+
p1 = atomicLoad(&comp[atomicLoad(&comp[hi])]);
|
|
2801
|
+
p2 = atomicLoad(&comp[lo]);
|
|
2802
|
+
}
|
|
2803
|
+
}
|
|
2804
|
+
|
|
2805
|
+
@compute @workgroup_size(WG)
|
|
2806
|
+
fn wcc_link_sample(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2807
|
+
let first = linear_id(wid, lid.x);
|
|
2808
|
+
for (var row = first; row < P.items; row = row + P.stride) {
|
|
2809
|
+
let v = select(row, perm[row], USE_PERM);
|
|
2810
|
+
let a0 = rowPtr[v];
|
|
2811
|
+
let a1 = rowPtr[v + 1u];
|
|
2812
|
+
if (a0 + P.r < a1) {
|
|
2813
|
+
link_pair(v, colIdx[a0 + P.r]);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
`,Vl=`
|
|
2818
|
+
@compute @workgroup_size(WG)
|
|
2819
|
+
fn wcc_sample(@builtin(workgroup_id) wid: vec3<u32>, @builtin(local_invocation_id) lid: vec3<u32>) {
|
|
2820
|
+
let i = linear_id(wid, lid.x);
|
|
2821
|
+
if (i >= P.items) { return; }
|
|
2822
|
+
let v = lowbias32(i + P.r) % P.n;
|
|
2823
|
+
hist[i] = atomicLoad(&comp[v]);
|
|
2824
|
+
}
|
|
2825
|
+
`,zs=se.define("RangeParams",[["start","u32"],["end","u32"],["arcBase","u32"],["arcEnd","u32"],["accumulate","u32"],["n","u32"],["pad0","u32"],["pad1","u32"]]),jl=se.define("ReduceParams",[["count","u32"],["outOffset","u32"],["level","u32"],["pad0","u32"]]),Ae=se.define("FillParams",[["count","u32"],["value","u32"],["mode","u32"],["pad0","u32"]]),Ue=se.define("Fa2Params",[["n","u32"],["dim","u32"],["flags","u32"],["tierStart","u32"],["tierEnd","u32"],["iterationIndex","u32"],["seed","u32"],["nearMax","u32"],["scalingRatio","f32"],["gravity","f32"],["jitterTolerance","f32"],["scale","f32"],["center","vec4f"],["settleThreshold","f32"],["extentFactor","f32"],["gridMax","u32"],["levels","u32"],["arcBase","u32"],["arcEnd","u32"],["accumulate","u32"],["hiEnd","u32"],["frK","f32"],["temperature","f32"],["springLength","f32"],["springCoefficient","f32"],["coulomb","f32"],["dragCoefficient","f32"],["timeStep","f32"],["midEnd","u32"],["settleFloor","f32"]]),et=se.define("Fa2State",[["speed","f32"],["speedEfficiency","f32"],["swing","f32"],["traction","f32"],["centroid","vec4f"],["rmsRadius","f32"],["radius","f32"],["meanDisplacement","f32"],["iteration","u32"],["min","vec4f"],["max","vec4f"],["gridMin","vec4f"],["eps","f32"],["settledCount","u32"],["outsideGrid","u32"],["maxCellOccupancy","u32"],["temperature","f32"],["kineticEnergy","f32"],["frEnergy","f32"],["frProgress","u32"],["invCellSize","f32"],["reserved0","f32"],["reserved1","vec2f"],["reserved2","vec4f"],["reserved3","vec4f"],["reserved4","vec4f"],["reserved5","vec4f"],["reserved6","vec4f"],["reserved7","vec4f"],["reserved8","vec4f"]],{layout:"storage",padTo:He}),Yt=se.define("Fa2Trace",[["swing","f32"],["traction","f32"],["speed","f32"],["speedEfficiency","f32"],["meanDisplacement","f32"],["settledCount","u32"],["iteration","u32"],["modelScalar","f32"]],{layout:"storage"}),dr=se.define("Fa2Partial",[["sum","vec4f"],["min","vec4f"],["max","vec4f"],["swingTraction","vec2f"],["dispFree","vec2f"]],{layout:"storage"}),Wn=se.define("SpmvParams",[["n","u32"],["arcBase","u32"],["arcEnd","u32"],["stride","u32"],["alpha","f32"],["beta","f32"],["uniformP","f32"],["start","u32"]]),zr=se.define("PrParams",[["n","u32"],["groups","u32"],["iteration","u32"],["trackConvergence","u32"],["convergeThreshold","f32"],["pad0","u32"],["pad1","u32"],["pad2","u32"]]),ke=se.define("PrPartial",[["danglingMass","f32"],["delta","f32"],["firstConverged","u32"],["iteration","u32"],["norm","f32"],["pad0","f32"],["pad1","f32"],["pad2","f32"]],{layout:"storage"}),Xt=se.define("WccParams",[["n","u32"],["items","u32"],["stride","u32"],["r","u32"],["flagIndex","u32"],["giant","u32"],["maxSteps","u32"],["pad0","u32"]]),ql=se.define("IndirectParams",[["countIndex","u32"],["wg","u32"],["slot","u32"],["pad0","u32"]]),Rr=se.define("ScanParams",[["count","u32"],["pad0","u32"],["pad1","u32"],["pad2","u32"]]),Un=se.define("GridLevelParams",[["childBase","u32"],["parentBase","u32"],["parentSide","u32"],["parentCells","u32"],["depth","u32"],["pad0","u32"],["pad1","u32"],["pad2","u32"]]),Fr=se.define("HistParams",[["count","u32"],["bins","u32"],["pad0","u32"],["pad1","u32"]]),Fs=se.define("RadixParams",[["count","u32"],["shift","u32"],["groups","u32"],["pad0","u32"]]),lr=se.define("CompactParams",[["count","u32"],["outIndex","u32"],["countIndex","u32"],["stride","u32"]]),Ce=se.define("FrontierCounters",[["frontierCount","u32"],["nextFrontierCount","u32"],["frontierDegreeSum","u32"],["prevFrontierCount","u32"],["prevDegreeSum","u32"],["unvisitedCount","u32"],["unvisitedDegreeSum","u32"],["unvisitedListLen","u32"],["edgeCount","u32"],["edgeCountUnclamped","u32"],["overflowLevels","u32"],["level","u32"],["visitedCount","u32"],["switches","u32"],["direction","u32"],["done","u32"],["arcsScanned","u32"],["fusedLevels","u32"],["twoPhaseLevels","u32"],["bottomUpLevels","u32"],["farCount","u32"],["nextFarCount","u32"],["thresholdBits","u32"],["deltaBits","u32"],["path","u32"],["nextDegreeSum","u32"],["stackTop","u32"],["sigmaOverflow","u32"]],{layout:"storage"}),ve=se.define("FrontierParams",[["role","u32"],["wg","u32"],["alpha","u32"],["beta","u32"],["fusedMax","u32"],["edgeCapacity","u32"],["maxDepth","u32"],["n","u32"],["mode","u32"],["cutoffBits","u32"],["arcBase","u32"],["arcEnd","u32"],["predKind","u32"],["bitsBase","u32"],["source","u32"],["stride","u32"],["firstOfSubmit","u32"],["iteration","u32"],["pad3","u32"],["pad2","u32"]]),_t=se.define("BcParams",[["n","u32"],["k","u32"],["start","u32"],["count","u32"],["stride","u32"],["role","u32"],["pad0","u32"],["pad1","u32"]]),Wt=se.define("ClosenessParams",[["role","u32"],["n","u32"],["words","u32"],["base","u32"],["total","u32"],["level","u32"],["row","u32"],["ctrl","u32"],["count","u32"],["source","u32"],["sourcesAt","u32"],["pullAt","u32"],["perNode","u32"],["arcCount","u32"],["pullOk","u32"],["pad0","u32"]]),Vn=se.define("BfParams",[["edgeCount","u32"],["stride","u32"],["maxRetries","u32"],["cutoffBits","u32"]]),er=se.define("BfFlags",[["changed","u32"],["retryExhausted","u32"],["pad0","u32"],["pad1","u32"]],{layout:"storage"}),Lr=se.define("ApspParams",[["n","u32"],["round","u32"],["blocks","u32"],["infBits","u32"]]),nt=se.define("CooParams",[["count","u32"],["pad0","u32"],["pad1","u32"],["pad2","u32"]]),jn=se.define("GroupParams",[["rowsBase","u32"],["basesBase","u32"],["count","u32"],["pad0","u32"]]),qn=se.define("LpaParams",[["n","u32"],["direction","u32"],["counterIndex","u32"],["pad0","u32"]]),Dr=se.define("MstParams",[["count","u32"],["counterIndex","u32"],["pad0","u32"],["pad1","u32"]]);function u(r,e,t,s,i){return{group:r,binding:e,name:t,kind:s,wgslType:i}}const tt=[u(0,0,"rowPtr","storage-ro","array<u32>"),u(0,1,"colIdx","storage-ro","array<u32>"),u(0,2,"weights","storage-ro","array<f32>"),u(0,3,"perm","storage-ro","array<u32>")],Kl={id:"degree",body:ol,entryPoint:"degree",bindings:tt.concat(u(1,0,"out","storage","array<u32>"),u(2,0,"P","uniform","RangeParams")),overrideDecls:[],uniforms:[zs],needs:[],snippetSlots:[],phase:"P1"},Hl={id:"reduce",body:Tl,entryPoint:"reduce",bindings:[u(1,0,"src","storage-ro","array<u32>"),u(1,1,"out","storage","array<u32>"),u(2,0,"P","uniform","ReduceParams")],overrideDecls:[{name:"OP",type:"u32",default:0},{name:"DTYPE",type:"u32",default:0},{name:"FINAL",type:"bool",default:!1}],uniforms:[jl],needs:["subgroups"],snippetSlots:[],phase:"P1"},Yl={id:"fill",body:hl,entryPoint:"fill",bindings:[u(1,0,"dst","storage","array<u32>"),u(2,0,"P","uniform","FillParams")],overrideDecls:[],uniforms:[Ae],needs:[],snippetSlots:[],phase:"P1"},Xl={id:"segmented-reduce",body:$l,entryPoint:"segmented_reduce",bindings:tt.concat(u(1,0,"out","storage","array<f32>"),u(2,0,"P","uniform","RangeParams")),overrideDecls:[{name:"OP",type:"u32",default:0},{name:"TIER",type:"u32",default:0}],uniforms:[zs],needs:["subgroups"],snippetSlots:["VALUE"],phase:"P2"},Ql={id:"fa2-stats-finalize",body:dl,entryPoint:"stats_finalize",bindings:[u(1,0,"partials","storage-ro","array<Fa2Partial>"),u(1,1,"S","storage","Fa2State"),u(1,2,"T","storage","array<Fa2Trace>"),u(1,3,"cellHist","storage-ro","array<u32>"),u(1,4,"hubCounters","storage","array<atomic<u32>>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[{name:"STATS_MODE",type:"u32",default:0}],uniforms:[Ue,et,Yt,dr],needs:["subgroups"],snippetSlots:[],phase:"P3"},Zl={id:"fa2-attraction",body:al,entryPoint:"attraction",bindings:tt.concat(u(1,0,"pos","storage-ro","array<vec4f>"),u(1,1,"force","storage","array<f32>"),u(2,0,"P","uniform","Fa2Params")),overrideDecls:[{name:"LINLOG",type:"bool",default:!1},{name:"DISTRIBUTED",type:"bool",default:!1},{name:"TIER",type:"u32",default:0},{name:"LAW",type:"u32",default:0}],uniforms:[Ue],needs:["subgroups"],snippetSlots:[],phase:"P3"},Jl={id:"fa2-repulsion-exact",body:ll,entryPoint:"repulsion",bindings:[u(1,0,"pos","storage-ro","array<vec4f>"),u(1,1,"S","storage","Fa2State"),u(1,2,"force","storage","array<f32>"),u(1,3,"oldForce","storage-ro","array<f32>"),u(1,4,"fixedMask","storage-ro","array<u32>"),u(1,5,"partials","storage","array<Fa2Partial>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[{name:"SWING_MODE",type:"u32",default:0},{name:"STRONG_GRAVITY",type:"bool",default:!1},{name:"GRAVITY_CENTER",type:"u32",default:0},{name:"LAW",type:"u32",default:0}],uniforms:[Ue,et,dr],needs:["subgroups"],snippetSlots:[],phase:"P1"},eu={id:"fa2-speed-finalize",body:ul,entryPoint:"speed_finalize",bindings:[u(1,0,"partials","storage-ro","array<Fa2Partial>"),u(1,1,"S","storage","Fa2State"),u(1,2,"T","storage","array<Fa2Trace>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[{name:"SWING_MODE",type:"u32",default:0}],uniforms:[Ue,et,Yt,dr],needs:["subgroups"],snippetSlots:[],phase:"P1"},tu={id:"fa2-integrate",body:cl,entryPoint:"integrate",bindings:[u(1,0,"force","storage-ro","array<f32>"),u(1,1,"oldForce","storage","array<f32>"),u(1,2,"fixedMask","storage-ro","array<u32>"),u(1,3,"S","storage","Fa2State"),u(1,4,"pos","storage","array<vec4f>"),u(1,5,"partials","storage","array<Fa2Partial>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[{name:"SWING_MODE",type:"u32",default:0},{name:"APPLY",type:"u32",default:0}],uniforms:[Ue,et,dr],needs:["subgroups"],snippetSlots:[],phase:"P3"},ru={id:"fa2-to-scene",body:fl,entryPoint:"to_scene",bindings:[u(1,0,"pos","storage-ro","array<vec4f>"),u(1,1,"scene","storage","array<f32>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[],uniforms:[Ue],needs:[],snippetSlots:[],phase:"P3"},su={id:"spmv-pull",body:Cl,entryPoint:"spmv_pull",bindings:tt.concat(u(1,0,"xNorm","storage-ro","array<f32>"),u(1,1,"rankOut","storage","array<f32>"),u(1,2,"personalization","storage-ro","array<f32>"),u(1,3,"partials","storage-ro","array<PrPartial>"),u(2,0,"P","uniform","SpmvParams")),overrideDecls:[{name:"HAS_PERSONALIZATION",type:"bool",default:!1},{name:"USE_DANGLING",type:"bool",default:!1},{name:"TIER",type:"u32",default:0}],uniforms:[Wn,ke],needs:["subgroups"],snippetSlots:[],phase:"P7"},iu={id:"pr-scale",body:Rl,entryPoint:"pr_scale",bindings:[u(1,0,"rankIn","storage-ro","array<f32>"),u(1,1,"rankPrev","storage-ro","array<f32>"),u(1,2,"outWeightSum","storage-ro","array<f32>"),u(1,3,"xNorm","storage","array<f32>"),u(1,4,"partials","storage","array<PrPartial>"),u(2,0,"P","uniform","PrParams")],overrideDecls:[{name:"NORM_MODE",type:"u32",default:0}],uniforms:[zr,ke],needs:["subgroups"],snippetSlots:[],phase:"P7"},nu={id:"pr-finalize",body:xl,entryPoint:"pr_finalize",bindings:[u(1,0,"partials","storage","array<PrPartial>"),u(2,0,"P","uniform","PrParams")],overrideDecls:[{name:"NORM_MODE",type:"u32",default:0}],uniforms:[zr,ke],needs:["subgroups"],snippetSlots:[],phase:"P7"},ou={id:"wcc-link-sample",body:Ul,entryPoint:"wcc_link_sample",bindings:tt.concat(u(1,0,"comp","storage","array<atomic<u32>>"),u(2,0,"P","uniform","WccParams")),overrideDecls:[],uniforms:[Xt],needs:[],snippetSlots:[],phase:"P7"},au={id:"wcc-link-edges",body:Wl,entryPoint:"wcc_link_edges",bindings:[u(1,0,"edgeSrc","storage-ro","array<u32>"),u(1,1,"edgeDst","storage-ro","array<u32>"),u(1,2,"comp","storage","array<atomic<u32>>"),u(2,0,"P","uniform","WccParams")],overrideDecls:[],uniforms:[Xt],needs:[],snippetSlots:[],phase:"P7"},cu={id:"wcc-compress",body:Fl,entryPoint:"wcc_compress",bindings:[u(1,0,"comp","storage","array<atomic<u32>>"),u(2,0,"P","uniform","WccParams")],overrideDecls:[],uniforms:[Xt],needs:[],snippetSlots:[],phase:"P7"},lu={id:"wcc-sample",body:Vl,entryPoint:"wcc_sample",bindings:[u(1,0,"comp","storage","array<atomic<u32>>"),u(1,1,"hist","storage","array<u32>"),u(2,0,"P","uniform","WccParams")],overrideDecls:[],uniforms:[Xt],needs:[],snippetSlots:[],phase:"P7"},uu={id:"indirect-finalize",body:Sl,entryPoint:"indirect_finalize",bindings:[u(1,0,"counters","storage-ro","array<u32>"),u(1,1,"args","storage","array<u32>"),u(2,0,"P","uniform","IndirectParams")],overrideDecls:[],uniforms:[ql],needs:[],snippetSlots:[],phase:"P4"},du={id:"scan-block",body:Bl,entryPoint:"scan_block",bindings:[u(1,0,"src","storage-ro","array<u32>"),u(1,1,"out","storage","array<u32>"),u(1,2,"blockSums","storage","array<u32>"),u(2,0,"P","uniform","ScanParams")],overrideDecls:[],uniforms:[Rr],needs:[],snippetSlots:[],phase:"P4"},fu={id:"scan-add",body:Nl,entryPoint:"scan_add",bindings:[u(1,0,"out","storage","array<u32>"),u(1,1,"blockOffsets","storage-ro","array<u32>"),u(2,0,"P","uniform","ScanParams")],overrideDecls:[],uniforms:[Rr],needs:[],snippetSlots:[],phase:"P4"},hu={id:"histogram",body:Pl,entryPoint:"histogram",bindings:[u(1,0,"keys","storage-ro","array<u32>"),u(1,1,"hist","storage","array<atomic<u32>>"),u(2,0,"P","uniform","HistParams")],overrideDecls:[],uniforms:[Fr],needs:[],snippetSlots:[],phase:"P4"},pu={id:"counting-scatter",body:sl,entryPoint:"counting_scatter",bindings:[u(1,0,"keys","storage-ro","array<u32>"),u(1,1,"start","storage-ro","array<u32>"),u(1,2,"cursor","storage","array<atomic<u32>>"),u(1,3,"outIndex","storage","array<u32>"),u(2,0,"P","uniform","HistParams")],overrideDecls:[],uniforms:[Fr],needs:[],snippetSlots:[],phase:"P4"},gu={id:"radix-hist",body:Ll,entryPoint:"radix_hist",bindings:[u(1,0,"keys","storage-ro","array<u32>"),u(1,1,"hist","storage","array<u32>"),u(2,0,"P","uniform","RadixParams")],overrideDecls:[],uniforms:[Fs],needs:[],snippetSlots:[],phase:"P4"},mu={id:"radix-scatter",body:Dl,entryPoint:"radix_scatter",bindings:[u(1,0,"keys","storage-ro","array<u32>"),u(1,1,"vals","storage-ro","array<u32>"),u(1,2,"offsets","storage-ro","array<u32>"),u(1,3,"keysOut","storage","array<u32>"),u(1,4,"valsOut","storage","array<u32>"),u(2,0,"P","uniform","RadixParams")],overrideDecls:[],uniforms:[Fs],needs:[],snippetSlots:[],phase:"P4"},wu={id:"grid-cell-key",body:gl,entryPoint:"grid_cell_key",bindings:[u(1,0,"pos","storage-ro","array<vec4f>"),u(1,1,"S","storage-ro","Fa2State"),u(1,2,"cellKey","storage","array<u32>"),u(1,3,"cellVal","storage","array<u32>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[],uniforms:[Ue,et],needs:[],snippetSlots:[],phase:"P4"},bu={id:"grid-centroid",body:ml,entryPoint:"grid_centroid",bindings:[u(1,0,"sortedIdx","storage-ro","array<u32>"),u(1,1,"cellStart","storage-ro","array<u32>"),u(1,2,"pos","storage-ro","array<vec4f>"),u(1,3,"pyramid","storage","array<vec4f>"),u(1,4,"hubList","storage","array<u32>"),u(1,5,"hubCounters","storage","array<atomic<u32>>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[],uniforms:[Ue],needs:[],snippetSlots:[],phase:"P4"},vu={id:"grid-centroid-hub",body:wl,entryPoint:"grid_centroid_hub",bindings:[u(1,0,"sortedIdx","storage-ro","array<u32>"),u(1,1,"cellStart","storage-ro","array<u32>"),u(1,2,"pos","storage-ro","array<vec4f>"),u(1,3,"pyramid","storage","array<vec4f>"),u(1,4,"hubList","storage-ro","array<u32>"),u(1,5,"hubCount","storage-ro","array<u32>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[],uniforms:[Ue],needs:["subgroups"],snippetSlots:[],phase:"P4"},yu={id:"grid-downsample",body:bl,entryPoint:"grid_downsample",bindings:[u(1,0,"pyramid","storage","array<vec4f>"),u(2,0,"P","uniform","GridLevelParams")],overrideDecls:[],uniforms:[Un],needs:[],snippetSlots:[],phase:"P4"},_u={id:"grid-far-field",body:vl,entryPoint:"grid_far_field",bindings:[u(1,0,"pos","storage-ro","array<vec4f>"),u(1,1,"sortedIdx","storage-ro","array<u32>"),u(1,2,"pyramid","storage-ro","array<vec4f>"),u(1,3,"S","storage-ro","Fa2State"),u(1,4,"force","storage","array<f32>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[{name:"LAW",type:"u32",default:0}],uniforms:[Ue,et],needs:[],snippetSlots:[],phase:"P4"},Pu={id:"grid-near-field",body:yl,entryPoint:"grid_near_field",bindings:[u(1,0,"pos","storage-ro","array<vec4f>"),u(1,1,"sortedIdx","storage-ro","array<u32>"),u(1,2,"cellStart","storage-ro","array<u32>"),u(1,3,"S","storage","Fa2State"),u(1,4,"force","storage","array<f32>"),u(1,5,"oldForce","storage-ro","array<f32>"),u(1,6,"fixedMask","storage-ro","array<u32>"),u(1,7,"partials","storage","array<Fa2Partial>"),u(2,0,"P","uniform","Fa2Params")],overrideDecls:[{name:"SWING_MODE",type:"u32",default:0},{name:"STRONG_GRAVITY",type:"bool",default:!1},{name:"GRAVITY_CENTER",type:"u32",default:0},{name:"LAW",type:"u32",default:0}],uniforms:[Ue,et,dr],needs:["subgroups"],snippetSlots:[],phase:"P4"},Su={id:"compact-scatter",body:el,entryPoint:"compact_scatter",bindings:[u(1,0,"queue","storage-ro","array<u32>"),u(1,1,"flags","storage-ro","array<u32>"),u(1,2,"offsets","storage-ro","array<u32>"),u(1,3,"out","storage","array<u32>"),u(1,4,"outCount","storage","array<u32>"),u(2,0,"P","uniform","CompactParams")],overrideDecls:[],uniforms:[lr],needs:[],snippetSlots:[],phase:"P8"},Eu={id:"dedupe-claim",body:il,entryPoint:"dedupe_claim",bindings:[u(1,0,"queue","storage-ro","array<u32>"),u(1,1,"owner","storage","array<atomic<u32>>"),u(1,2,"counters","storage","array<atomic<u32>>"),u(2,0,"P","uniform","CompactParams")],overrideDecls:[],uniforms:[lr],needs:[],snippetSlots:[],phase:"P8"},Iu={id:"dedupe-filter",body:nl,entryPoint:"dedupe_filter",bindings:[u(1,0,"queue","storage-ro","array<u32>"),u(1,1,"owner","storage","array<atomic<u32>>"),u(1,2,"out","storage","array<u32>"),u(1,3,"outCount","storage","array<atomic<u32>>"),u(2,0,"P","uniform","CompactParams")],overrideDecls:[],uniforms:[lr],needs:[],snippetSlots:[],phase:"P8"},ku={id:"frontier-finalize",body:pl,entryPoint:"frontier_finalize",bindings:[u(1,0,"counters","storage","array<atomic<u32>>"),u(2,0,"P","uniform","FrontierParams")],overrideDecls:[],uniforms:[ve],needs:[],snippetSlots:[],phase:"P8"},Au={id:"advance-expand",body:Bc,entryPoint:"advance_expand",bindings:tt.concat(u(1,0,"frontierIn","storage-ro","array<u32>"),u(1,1,"counters","storage","array<atomic<u32>>"),u(1,2,"edgeQueue","storage","array<u32>"),u(2,0,"P","uniform","FrontierParams")),overrideDecls:[],uniforms:[ve],needs:["subgroups"],snippetSlots:[],phase:"P8"},xu={id:"bfs-contract",body:Hc,entryPoint:"bfs_contract",bindings:[u(1,0,"edgeQueue","storage-ro","array<u32>"),u(1,1,"counters","storage","array<atomic<u32>>"),u(1,2,"depth","storage","array<atomic<u32>>"),u(1,3,"frontierOut","storage","array<u32>"),u(2,0,"P","uniform","FrontierParams")],overrideDecls:[],uniforms:[ve],needs:[],snippetSlots:[],phase:"P8"},Ru={id:"sssp-pred",body:Ml,entryPoint:"sssp_pred",bindings:tt.concat(u(1,0,"dist","storage-ro","array<u32>"),u(1,1,"pred","storage","array<atomic<u32>>"),u(2,0,"P","uniform","FrontierParams")),overrideDecls:[{name:"MODE",type:"u32",default:0}],uniforms:[ve],needs:[],snippetSlots:[],phase:"P8"},Lu={id:"bfs-fused",body:Yc,entryPoint:"bfs_fused",bindings:tt.concat(u(1,0,"frontierIn","storage-ro","array<u32>"),u(1,1,"counters","storage","array<atomic<u32>>"),u(1,2,"depth","storage","array<atomic<u32>>"),u(1,3,"frontierOut","storage","array<u32>"),u(2,0,"P","uniform","FrontierParams")),overrideDecls:[],uniforms:[ve],needs:[],snippetSlots:[],phase:"P8"},Du={id:"bfs-bottom-up",body:Kc,entryPoint:"bfs_bottom_up",bindings:tt.concat(u(1,0,"sweepIn","storage-ro","array<u32>"),u(1,1,"counters","storage","array<atomic<u32>>"),u(1,2,"depth","storage","array<atomic<u32>>"),u(1,3,"frontierOut","storage","array<u32>"),u(2,0,"P","uniform","FrontierParams")),overrideDecls:[],uniforms:[ve],needs:["subgroups"],snippetSlots:[],phase:"P8"},Tu={id:"bfs-bitset-build",body:qc,entryPoint:"bfs_bitset_build",bindings:[u(1,0,"frontierIn","storage-ro","array<u32>"),u(1,1,"counters","storage","array<atomic<u32>>"),u(1,2,"bits","storage","array<atomic<u32>>"),u(2,0,"P","uniform","FrontierParams")],overrideDecls:[],uniforms:[ve],needs:[],snippetSlots:[],phase:"P8"},Ou={id:"bfs-unvisited-flags",body:Qc,entryPoint:"bfs_unvisited_flags",bindings:[u(1,0,"inDegree","storage-ro","array<u32>"),u(1,1,"depth","storage-ro","array<u32>"),u(1,2,"flags","storage","array<u32>"),u(1,3,"counters","storage","array<atomic<u32>>"),u(2,0,"P","uniform","FrontierParams")],overrideDecls:[],uniforms:[ve],needs:["subgroups"],snippetSlots:[],phase:"P8"},Nu={id:"bfs-next-degree",body:Xc,entryPoint:"bfs_next_degree",bindings:[u(1,0,"frontier","storage-ro","array<u32>"),u(1,1,"outDegree","storage-ro","array<u32>"),u(1,2,"counters","storage","array<atomic<u32>>"),u(1,3,"inDegree","storage-ro","array<u32>"),u(2,0,"P","uniform","FrontierParams")],overrideDecls:[],uniforms:[ve],needs:["subgroups"],snippetSlots:[],phase:"P8"},Bu={id:"sssp-relax",body:Gl,entryPoint:"sssp_relax",bindings:tt.concat(u(1,0,"dist","storage","array<atomic<u32>>"),u(1,1,"counters","storage","array<atomic<u32>>"),u(1,2,"queueIn","storage-ro","array<u32>"),u(1,3,"queueOut","storage","array<u32>"),u(2,0,"P","uniform","FrontierParams")),overrideDecls:[],uniforms:[ve],needs:[],snippetSlots:[],phase:"P8"},$u={id:"bf-relax",body:jc,entryPoint:"bf_relax",bindings:[u(1,0,"edgeSrc","storage-ro","array<u32>"),u(1,1,"edgeDst","storage-ro","array<u32>"),u(1,2,"edgeToArc","storage-ro","array<u32>"),u(1,3,"weights","storage-ro","array<f32>"),u(1,4,"dist","storage","array<atomic<u32>>"),u(1,5,"flags","storage","array<atomic<u32>>"),u(2,0,"P","uniform","BfParams")],overrideDecls:[{name:"UNDIRECTED",type:"bool",default:!1}],uniforms:[Vn],needs:[],snippetSlots:[],phase:"P8"},Cu={id:"closeness-level",body:Zc,entryPoint:"closeness_level",bindings:[u(1,0,"rowPtr","storage-ro","array<u32>"),u(1,1,"colIdx","storage-ro","array<u32>"),u(1,2,"inRowPtr","storage-ro","array<u32>"),u(1,3,"inColIdx","storage-ro","array<u32>"),u(1,4,"bits","storage","array<atomic<u32>>"),u(1,5,"table","storage","array<atomic<u32>>"),u(2,0,"P","uniform","ClosenessParams")],overrideDecls:[],uniforms:[Wt],needs:[],snippetSlots:[],phase:"P8"},Mu={id:"closeness-rowsum",body:Jc,entryPoint:"closeness_rowsum",bindings:[u(1,0,"dist","storage-ro","array<f32>"),u(1,1,"out","storage","array<u32>"),u(2,0,"P","uniform","ClosenessParams")],overrideDecls:[],uniforms:[Wt],needs:[],snippetSlots:[],phase:"P9"},Gu={id:"bc-finalize",body:Fc,entryPoint:"bc_finalize",bindings:[u(1,0,"counters","storage","array<atomic<u32>>"),u(1,1,"ends","storage","array<u32>"),u(1,2,"S","storage-ro","array<u32>"),u(1,3,"depthK","storage","array<u32>"),u(1,4,"sigmaK","storage","array<u32>"),u(1,5,"levelMax","storage","array<u32>"),u(2,0,"P","uniform","BcParams")],overrideDecls:[{name:"SCALED",type:"bool",default:!1}],uniforms:[_t],needs:[],snippetSlots:[],phase:"P9"},zu={id:"bc-forward",body:Wc,entryPoint:"bc_forward",bindings:[u(1,0,"rowPtr","storage-ro","array<u32>"),u(1,1,"colIdx","storage-ro","array<u32>"),u(1,2,"S","storage","array<u32>"),u(1,3,"ends","storage-ro","array<u32>"),u(1,4,"counters","storage","array<atomic<u32>>"),u(1,5,"depthK","storage","array<atomic<u32>>"),u(1,6,"sigmaK","storage","array<atomic<u32>>"),u(2,0,"P","uniform","BcParams")],overrideDecls:[{name:"SCALED",type:"bool",default:!1}],uniforms:[_t],needs:[],snippetSlots:[],phase:"P9"},Fu={id:"bc-backward",body:Mc,entryPoint:"bc_backward",bindings:[u(1,0,"rowPtr","storage-ro","array<u32>"),u(1,1,"colIdx","storage-ro","array<u32>"),u(1,2,"S","storage-ro","array<u32>"),u(1,3,"depthK","storage-ro","array<u32>"),u(1,4,"sigmaK","storage-ro","array<u32>"),u(1,5,"deltaK","storage","array<f32>"),u(1,6,"levelMax","storage-ro","array<u32>"),u(2,0,"P","uniform","BcParams")],overrideDecls:[{name:"SCALED",type:"bool",default:!1}],uniforms:[_t],needs:[],snippetSlots:[],phase:"P9"},Wu={id:"bc-gather",body:Vc,entryPoint:"bc_gather",bindings:[u(1,0,"deltaK","storage-ro","array<f32>"),u(1,1,"bc","storage","array<f32>"),u(2,0,"P","uniform","BcParams")],overrideDecls:[],uniforms:[_t],needs:[],snippetSlots:[],phase:"P9"},Uu={id:"bc-edge-gather",body:zc,entryPoint:"bc_edge_gather",bindings:[u(1,0,"rowPtr","storage-ro","array<u32>"),u(1,1,"colIdx","storage-ro","array<u32>"),u(1,2,"depthK","storage-ro","array<u32>"),u(1,3,"sigmaK","storage-ro","array<u32>"),u(1,4,"deltaK","storage-ro","array<f32>"),u(1,5,"arcScores","storage","array<f32>"),u(1,6,"levelMax","storage-ro","array<u32>"),u(2,0,"P","uniform","BcParams")],overrideDecls:[{name:"SCALED",type:"bool",default:!1}],uniforms:[_t],needs:[],snippetSlots:[],phase:"P9"},Vu={id:"bc-forward-edge",body:Uc,entryPoint:"bc_forward_edge",bindings:[u(1,0,"edgeSrc","storage-ro","array<u32>"),u(1,1,"edgeDst","storage-ro","array<u32>"),u(1,2,"S","storage","array<u32>"),u(1,3,"ends","storage-ro","array<u32>"),u(1,4,"counters","storage","array<atomic<u32>>"),u(1,5,"depthK","storage","array<atomic<u32>>"),u(1,6,"sigmaK","storage","array<atomic<u32>>"),u(2,0,"P","uniform","BcParams")],overrideDecls:[{name:"UNDIRECTED",type:"bool",default:!1},{name:"SCALED",type:"bool",default:!1}],uniforms:[_t],needs:[],snippetSlots:[],phase:"P9"},ju={id:"bc-count",body:Gc,entryPoint:"bc_count",bindings:[u(1,0,"rowPtr","storage-ro","array<u32>"),u(1,1,"colIdx","storage-ro","array<u32>"),u(1,2,"S","storage-ro","array<u32>"),u(1,3,"ends","storage-ro","array<u32>"),u(1,4,"counters","storage","array<atomic<u32>>"),u(1,5,"depthK","storage-ro","array<u32>"),u(1,6,"sigmaK","storage","array<u32>"),u(1,7,"levelMax","storage","array<atomic<u32>>"),u(2,0,"P","uniform","BcParams")],overrideDecls:[],uniforms:[_t],needs:[],snippetSlots:[],phase:"P9"},qu={id:"apsp-init",body:Cc,entryPoint:"apsp_init",bindings:tt.concat(u(1,0,"dist","storage","array<f32>"),u(2,0,"P","uniform","ApspParams")),overrideDecls:[],uniforms:[Lr],needs:[],snippetSlots:[],phase:"P9"},Ku={id:"apsp-fw",body:$c,entryPoint:"apsp_fw",bindings:[u(1,0,"dist","storage","array<f32>"),u(2,0,"P","uniform","ApspParams")],overrideDecls:[{name:"PHASE",type:"u32",default:0}],uniforms:[Lr],needs:[],snippetSlots:[],phase:"P9"},Hu={id:"coo-emit",body:tl,entryPoint:"coo_emit",bindings:[u(1,0,"edgeSrc","storage-ro","array<u32>"),u(1,1,"edgeDst","storage-ro","array<u32>"),u(1,2,"edgeWeight","storage-ro","array<f32>"),u(1,3,"order","storage-ro","array<u32>"),u(1,4,"outSrc","storage","array<u32>"),u(1,5,"outDst","storage","array<u32>"),u(1,6,"outWeight","storage","array<f32>"),u(2,0,"P","uniform","CooParams")],overrideDecls:[{name:"INDEXED",type:"bool",default:!1},{name:"WEIGHTED",type:"bool",default:!1}],uniforms:[nt],needs:[],snippetSlots:[],phase:"P11"},Yu={id:"run-flags",body:Ol,entryPoint:"run_flags",bindings:[u(1,0,"keysA","storage-ro","array<u32>"),u(1,1,"keysB","storage-ro","array<u32>"),u(1,2,"flags","storage","array<u32>"),u(2,0,"P","uniform","CooParams")],overrideDecls:[],uniforms:[nt],needs:[],snippetSlots:[],phase:"P11"},Xu={id:"coo-scatter",body:rl,entryPoint:"coo_scatter",bindings:[u(1,0,"src","storage-ro","array<u32>"),u(1,1,"dst","storage-ro","array<u32>"),u(1,2,"weight","storage-ro","array<f32>"),u(1,3,"rowPtr","storage-ro","array<u32>"),u(1,4,"cursors","storage","array<atomic<u32>>"),u(1,5,"colIdx","storage","array<u32>"),u(1,6,"outWeight","storage","array<f32>"),u(2,0,"P","uniform","CooParams")],overrideDecls:[{name:"SORTED_INPUT",type:"bool",default:!1},{name:"WEIGHTED",type:"bool",default:!1}],uniforms:[nt],needs:[],snippetSlots:[],phase:"P11"},Qu={id:"orient-flags",body:Al,entryPoint:"orient_flags",bindings:[u(1,0,"rowPtr","storage-ro","array<u32>"),u(1,1,"colIdx","storage-ro","array<u32>"),u(1,2,"src","storage-ro","array<u32>"),u(1,3,"flags","storage","array<u32>"),u(2,0,"P","uniform","CooParams")],overrideDecls:[],uniforms:[nt],needs:[],snippetSlots:[],phase:"P11"},Zu={id:"tri-intersect",body:zl,entryPoint:"tri_intersect",bindings:[u(1,0,"rowPtr","storage-ro","array<u32>"),u(1,1,"colIdx","storage-ro","array<u32>"),u(1,2,"src","storage-ro","array<u32>"),u(1,3,"counts","storage","array<atomic<u32>>"),u(2,0,"P","uniform","CooParams")],overrideDecls:[{name:"SEARCH",type:"u32",default:0}],uniforms:[nt],needs:[],snippetSlots:[],phase:"P11"},Ju={id:"group-by-key-row",body:_l,entryPoint:"group_by_key_row",bindings:[u(1,0,"rowPtr","storage-ro","array<u32>"),u(1,1,"colIdx","storage-ro","array<u32>"),u(1,2,"weights","storage-ro","array<f32>"),u(1,3,"keyIn","storage-ro","array<u32>"),u(1,4,"rows","storage-ro","array<u32>"),u(1,5,"hashRegion","storage","array<atomic<u32>>"),u(1,6,"bestKey","storage","array<u32>"),u(1,7,"bestScore","storage","array<f32>"),u(2,0,"P","uniform","GroupParams")],overrideDecls:[{name:"TIER",type:"u32",default:0},{name:"WEIGHTED",type:"bool",default:!1}],uniforms:[jn],needs:[],snippetSlots:[],phase:"P11"},ed={id:"lpa-step",body:El,entryPoint:"lpa_step",bindings:[u(1,0,"labelsIn","storage-ro","array<u32>"),u(1,1,"bestKey","storage-ro","array<u32>"),u(1,2,"labelsOut","storage","array<u32>"),u(1,3,"counters","storage","array<atomic<u32>>"),u(2,0,"P","uniform","LpaParams")],overrideDecls:[],uniforms:[qn],needs:[],snippetSlots:[],phase:"P11"},td={id:"mst-best",body:Il,entryPoint:"mst_best",bindings:[u(1,0,"edgeSrc","storage-ro","array<u32>"),u(1,1,"edgeDst","storage-ro","array<u32>"),u(1,2,"edgeWeight","storage-ro","array<f32>"),u(1,3,"comp","storage-ro","array<u32>"),u(1,4,"bestKey","storage","array<atomic<u32>>"),u(1,5,"bestEdge","storage","array<atomic<u32>>"),u(2,0,"P","uniform","MstParams")],overrideDecls:[{name:"PASS",type:"u32",default:0},{name:"WEIGHTED",type:"bool",default:!1}],uniforms:[Dr],needs:[],snippetSlots:[],phase:"P11"},rd={id:"mst-link",body:kl,entryPoint:"mst_link",bindings:[u(1,0,"edgeSrc","storage-ro","array<u32>"),u(1,1,"edgeDst","storage-ro","array<u32>"),u(1,2,"bestEdge","storage-ro","array<u32>"),u(1,3,"comp","storage","array<atomic<u32>>"),u(1,4,"treeEdge","storage","array<u32>"),u(1,5,"counters","storage","array<atomic<u32>>"),u(2,0,"P","uniform","MstParams")],overrideDecls:[],uniforms:[Dr],needs:[],snippetSlots:[],phase:"P11"},Oi=Object.freeze({degree:Kl,reduce:Hl,fill:Yl,"segmented-reduce":Xl,"fa2-stats-finalize":Ql,"fa2-attraction":Zl,"fa2-repulsion-exact":Jl,"fa2-speed-finalize":eu,"fa2-integrate":tu,"fa2-to-scene":ru,"spmv-pull":su,"pr-scale":iu,"pr-finalize":nu,"wcc-link-sample":ou,"wcc-link-edges":au,"wcc-compress":cu,"wcc-sample":lu,"indirect-finalize":uu,"scan-block":du,"scan-add":fu,histogram:hu,"counting-scatter":pu,"radix-hist":gu,"radix-scatter":mu,"grid-cell-key":wu,"grid-centroid":bu,"grid-centroid-hub":vu,"grid-downsample":yu,"grid-far-field":_u,"grid-near-field":Pu,"compact-scatter":Su,"dedupe-claim":Eu,"dedupe-filter":Iu,"frontier-finalize":ku,"advance-expand":Au,"bfs-contract":xu,"sssp-pred":Ru,"bfs-fused":Lu,"bfs-bottom-up":Du,"bfs-bitset-build":Tu,"bfs-unvisited-flags":Ou,"bfs-next-degree":Nu,"sssp-relax":Bu,"bf-relax":$u,"closeness-level":Cu,"closeness-rowsum":Mu,"bc-finalize":Gu,"bc-forward":zu,"bc-backward":Fu,"bc-gather":Wu,"bc-count":ju,"bc-edge-gather":Uu,"bc-forward-edge":Vu,"apsp-init":qu,"apsp-fw":Ku,"coo-emit":Hu,"run-flags":Yu,"coo-scatter":Xu,"orient-flags":Qu,"tri-intersect":Zu,"group-by-key-row":Ju,"lpa-step":ed,"mst-best":td,"mst-link":rd}),sd=new Map;function id(r){const e=Oi[r];if(e===void 0)throw new h("E_INVALID_ARGUMENT",`unknown kernel id "${r}"`,{argument:"id",value:r,expected:Object.keys(Oi)});return e}function _(r,e,t){const s=id(r);return{id:s.id,body:sd.get(r)??s.body,bindings:s.bindings,overrideDecls:s.overrideDecls,overrides:e??{},needs:s.needs,uniforms:s.uniforms,snippets:t}}function Ze(r,e,t){const s=r.colIdx??r.rowPtr,i=t===void 0?r.weights:t;return{rowPtr:r.rowPtr,colIdx:s,weights:i??s,perm:e??r.rowPtr}}function Ge(r,e,t){const s=t===void 0?r.weights:t;return{USE_PERM:e!==null,HAS_WEIGHTS:s!==null}}const lt=32;function nd(r){const{perm:e}=r.bindings;if(e===void 0)throw new h("E_INVALID_ARGUMENT",`the ${r.view} view has no perm binding`,{argument:"view",value:r.view,expected:"a view with a perm binding (degreeOrder, reverseDegreeOrder)"});const t=r.scalars.segmentOffsets;if(!(t?.length===5&&t[0]===0&&t.every((n,o)=>Number.isInteger(n)&&(o===0||n>=t[o-1])))||t[4]!==e.size/4)throw new h("E_INVALID_ARGUMENT",`the ${r.view} view's segmentOffsets are not five ascending numbers ending at the row count`,{argument:"view",value:t===void 0?null:Array.from(t),expected:`[0, hiEnd, midEnd, lowEnd, ${e.size/4}] ascending`});const i=[t[0],t[1],t[2],t[3],t[4]];return Object.freeze({perm:e,segmentOffsets:i})}function fr(r,e){const t=r.rowPtr.size;if(t<4||t%4!==0)throw new h("E_INVALID_ARGUMENT",`${e}: a rowPtr binding of ${t} bytes is not 4(n + 1)`,{argument:"core.rowPtr",value:t,expected:"a positive multiple of 4"});return t/4-1}function Wr(r){return r.colIdx===null?0:r.colIdx.size/4}function Tr(r,e,t){const s=r.arcBuffers?.[e][t.bufferIndex];if(s===void 0)throw new h("E_INVALID_ARGUMENT",`windowBinding: the core has no window buffers of ${e}`,{argument:"core",value:e,expected:"a windowed core whose plan uploaded the array"});return{buffer:s,offset:t.offset,size:4*(t.end-t.start),window:t}}function Ss(r){return r.windows===null?[{arcBase:0,arcEnd:Wr(r),core:r}]:r.windows.map((e,t,s)=>({arcBase:e.start,arcEnd:t+1<s.length?s[t+1].start:e.end,core:{...r,colIdx:Tr(r,"colIdx",e),weights:r.weights===null?null:Tr(r,"weights",e)}}))}function od(r,e){if(r.plan==="windowed"||r.windows!==null)throw new h("E_UNSUPPORTED",`${e}: windowed cores are not executed by the pull`,{feature:`${e}.windowed`})}function Pt(r,e,t,s){if(r.plan==="windowed")throw new h("E_TOO_LARGE",`${s}: the arc arrays need a windowed upload (${r.windows?.length??0} windows), which ${s} does not execute (DEP-P4-B)`,{needed:4*e,limit:t,path:"windowed",algorithm:s})}function Ur(r,e){const{rowPtr:t}=r.bindings;if(t===void 0)throw new h("E_INVALID_ARGUMENT",`the ${r.view} view has no rowPtr binding`,{argument:"view",value:r.view,expected:"a view with a rowPtr binding (reverse)"});const s=r.bindings.colIdx===void 0||r.bindings.colIdx.size===0?null:r.bindings.colIdx,i=r.bindings.weights===void 0||r.bindings.weights.size===0?null:r.bindings.weights,n=s===null?0:s.size/4;if(n!==e)throw new h("E_INVALID_ARGUMENT",`the ${r.view} view binds ${n} arcs but its arcCount scalar says ${e}`,{argument:"arcCount",value:e,expected:n});return Object.freeze({serial:-1,plan:"perArray",rowPtr:t,colIdx:s,weights:i,arcToEdge:null,edgeToArc:null,windows:null,arcBuffers:null,hasWeights:i!==null})}class Ws{constructor(e,t,s,i){if(this.overruns=0,this.next=0,this.dirtyLo=-1,this.dirtyHi=-1,this.destroyed=!1,!Number.isInteger(s)||s<1)throw new h("E_INVALID_ARGUMENT",`UniformRing "${i}": slots must be a positive integer, got ${s}`,{argument:"slots",value:s,expected:"an integer >= 1"});this.device=e,this.allocator=t,this.slots=s,this.label=i,this.shadow=new ArrayBuffer(s*Ye),this.view=new DataView(this.shadow),this.dirty=new Uint8Array(s),this.buffer=t.createBuffer({label:i,size:s*Ye,usage:ie.UNIFORM|ie.COPY_DST})}binding(e){return this.assertLive(),this.assertFits(e),{buffer:this.buffer,offset:0,size:e.byteLength,window:null}}offsetOf(e){return this.checkSlot(e),e*Ye}reserve(e){if(this.assertLive(),!Number.isInteger(e)||e<1||e>this.slots)throw new h("E_INVALID_ARGUMENT",`UniformRing "${this.label}": cannot reserve ${e} of ${this.slots} slots`,{argument:"count",value:e,expected:`an integer in [1, ${this.slots}]`});this.next+e>this.slots&&(this.next=0);const t=this.next;return this.next+=e,this.dirty.subarray(t,t+e).includes(1)&&(this.overruns+=1),t}write(e,t,s){this.checkSlot(e),this.assertFits(t),t.write(this.view,s,e*Ye),this.dirty[e]=1,(this.dirtyLo<0||e<this.dirtyLo)&&(this.dirtyLo=e),e>this.dirtyHi&&(this.dirtyHi=e)}flush(){if(this.assertLive(),this.dirtyLo<0)return;const e=this.dirtyLo*Ye,t=(this.dirtyHi+1)*Ye;this.device.queue.writeBuffer(this.buffer,e,this.shadow,e,t-e),this.dirty.fill(0),this.dirtyLo=-1,this.dirtyHi=-1}destroy(){this.destroyed||(this.destroyed=!0,this.allocator.destroy(this.buffer))}assertLive(){if(this.destroyed)throw new h("E_DISPOSED",`UniformRing "${this.label}" is destroyed`,{label:this.label})}checkSlot(e){if(this.assertLive(),!Number.isInteger(e)||e<0||e>=this.slots)throw new h("E_INVALID_ARGUMENT",`UniformRing "${this.label}": slot ${e} is outside [0, ${this.slots})`,{argument:"slot",value:e,expected:`an integer in [0, ${this.slots})`})}assertFits(e){if(e.byteLength>Ye)throw new h("E_INVALID_ARGUMENT",`UniformRing "${this.label}": block ${e.name} is ${e.byteLength} bytes, a slot holds ${Ye}`,{argument:"block",value:e.name,expected:`byteLength <= ${Ye}`})}}const ad=4294967295;async function Rt(r){const e=await r.pipelines.kernel(_("scan-block")),t=await r.pipelines.kernel(_("scan-add")),s=r.scratch(4,"scan/zero");return r.device.queue.writeBuffer(s,0,new Uint32Array(1)),new ld(r,e,t,{buffer:s,offset:0,size:4,window:null})}function cd(r,e,t){if(!Number.isSafeInteger(e)||e<0||e>ad)throw new h("E_INVALID_ARGUMENT","scan: count must be a non-negative integer below 2^32",{argument:"count",value:e});if(r.size<4*e)throw new h("E_INVALID_ARGUMENT","scan: src is smaller than 4 x count bytes",{argument:"src",value:r.size,expected:4*e});if(t.size<4*e)throw new h("E_INVALID_ARGUMENT","scan: out is smaller than 4 x count bytes",{argument:"out",value:t.size,expected:4*e})}class ld{constructor(e,t,s,i){this.dispatches=0,this.scope=e,this.block=t,this.add=s,this.zero=i}get lastDispatches(){return this.dispatches}record(e,t,s,i){if(cd(t,s,i),s===0)return this.dispatches=0,{binding:this.zero,index:0};const n=this.scope.workgroupSize,o=[];let a=t,c=i,l=s;for(;;){const f=Math.ceil(l/n),p=z(l,n,this.scope.caps),g=Gs(p),m=this.scratch(g,`scan/sums${o.length}`);if(o.push({input:a,output:c,count:l,plan:p,sums:m}),f===1)break;a=m,c=this.scratch(g,`scan/offsets${o.length}`),l=f}for(const f of o){const p=this.scope.params(Rr,{count:f.count,pad0:0,pad1:0,pad2:0}),g=this.block.bind({src:f.input,out:f.output,blockSums:f.sums,P:p.binding});this.block.dispatch(e,g,f.plan,[p.offset])}for(let f=o.length-2;f>=0;f--){const p=o[f],g=this.scope.params(Rr,{count:p.count,pad0:0,pad1:0,pad2:0}),m=this.add.bind({out:p.output,blockOffsets:o[f+1].output,P:g.binding});this.add.dispatch(e,m,p.plan,[g.offset])}return this.dispatches=2*o.length-1,{binding:o[o.length-1].sums,index:0}}scratch(e,t){const s=4*e;return{buffer:this.scope.scratch(s,t),offset:0,size:s,window:null}}}const Or=3735928559,Ni=32,ud=8,os=new WeakMap;function Kn(r){return r+1}function dd(r,e,t){let s=0;for(let i=0;i<t;i++){const n=r[i];if(n!==s)return{where:`out[${String(i)}]`,expected:s,actual:n,poison:n===Or};s=s+Kn(i)>>>0}return e!==s?{where:"total",expected:s,actual:e,poison:e===Or}:null}function Bi(r,e){return{buffer:r,offset:0,size:e,window:null}}async function fd(r){const e=performance.now(),t=r.workgroupSize,s=Ni*t+1,i=4*s,n=r.pool.lease(),o=new Ws(r.device,r.allocator,ud,"device-check/ring");try{const a={device:r.device,caps:r.caps,pipelines:r.pipelines,pool:r.pool,workgroupSize:t,scratch:(y,I)=>n.storage(y,`device-check/${I}`),params(y,I){const S=o.reserve(1);return o.write(S,y,I),{binding:o.binding(y),offset:o.offsetOf(S)}}},c=await Rt(a),l=n.storage(i,"device-check/src"),d=n.storage(i,"device-check/out"),f=new Uint32Array(s);for(let y=0;y<s;y++)f[y]=Kn(y);r.device.queue.writeBuffer(l,0,f),r.device.queue.writeBuffer(d,0,new Uint32Array(s).fill(Or));const p=r.device.createCommandEncoder({label:"device-check"}),g=p.beginComputePass({label:"device-check"}),m=c.record(g,Bi(l,i),s,Bi(d,i));g.end(),o.flush(),r.device.queue.submit([p.finish()]);const b=new Uint32Array(await r.readback.read(d,i)),w=new Uint32Array(await r.readback.read(m.binding.buffer,4,void 0,m.binding.offset+4*m.index)),v=dd(b,w[0],s);return{check:"exclusive-scan",ok:v===null,workgroupSize:t,count:s,blocks:Ni,ms:performance.now()-e,vendor:r.caps.vendor,architecture:r.caps.architecture,description:r.caps.description,mismatch:v}}finally{o.destroy(),n.release()}}function Hn(r){const e=os.get(r.device);if(e!==void 0)return e;const t=fd(r).catch(s=>{throw os.delete(r.device),s});return os.set(r.device,t),t}async function Ve(r){const e=await Hn(r),{mismatch:t}=e;if(t===null)return;const s={vendor:e.vendor,architecture:e.architecture,device:r.caps.device,description:e.description},i=e.architecture==="warp"?"this is the Microsoft Basic Render Driver; the Microsoft.Direct3D.WARP redistributable (1.0.21 or newer) beside the host executable computes it correctly":void 0,n=t.poison?`${t.where} was never written (the ${String(Or)} the check wrote beforehand survived the dispatch)`:`${t.where} came back as ${String(t.actual)} where ${String(t.expected)} was required`,o=`this device computes multi-workgroup shaders incorrectly: an exclusive scan of ${String(e.count)} known numbers is wrong -- ${n}. Refusing to run: every number this package computed here would be unreliable. adapter: ${s.vendor}/${s.architecture} "${s.description}"`;throw new h("E_DEVICE_INCORRECT",i===void 0?o:`${o}. ${i}`,{check:e.check,where:t.where,expected:t.expected,actual:t.actual,poison:t.poison,count:e.count,blocks:e.blocks,workgroupSize:e.workgroupSize,adapter:s,...i===void 0?{}:{hint:i}})}function je(r,e,t){const s=r.pool.lease(),i=new Ws(r.device,r.allocator,t,`${e}/ring`);return{device:r.device,caps:r.caps,pipelines:r.pipelines,pool:r.pool,workgroupSize:r.workgroupSize,scratch:(n,o)=>s.storage(n,`${e}/${o}`),params(n,o){const a=i.reserve(1);return i.write(a,n,o),{binding:i.binding(n),offset:i.offsetOf(a)}},flush:()=>{i.flush()},dispose(){i.destroy(),s.release()},ringOverruns:()=>i.overruns}}async function Vr(r){const e=await r.pipelines.kernel(_("compact-scatter")),t=await r.pipelines.kernel(_("dedupe-claim")),s=await r.pipelines.kernel(_("dedupe-filter")),i=await Rt(r);return new gd(r,e,t,s,i)}function Yn(r,e){if(!Number.isSafeInteger(e)||e<0||e>ye)throw new h("E_INVALID_ARGUMENT",`${r}: count must be a non-negative integer below 2^32`,{argument:"count",value:e})}function ar(r,e,t,s){if(t.size<4*s)throw new h("E_INVALID_ARGUMENT",`${r}: ${e} is smaller than 4 x ${s} bytes`,{argument:e,value:t.size,expected:4*s})}function Es(r,e,t,s){if(!Number.isSafeInteger(t)||t<0||4*(t+1)>s.size)throw new h("E_INVALID_ARGUMENT",`${r}: ${e} ${t} is outside the block`,{argument:e,value:t,expected:`0 <= ${e} < ${Math.floor(s.size/4)}`})}function hd(r){Yn("compact",r.count),ar("compact","queue",r.queue,r.count),ar("compact","flags",r.flags,r.count),ar("compact","out",r.out,r.count),Es("compact","outIndex",r.outIndex,r.outCount)}function pd(r){if(Yn("dedupe",r.count),ar("dedupe","queue",r.queue,r.count),ar("dedupe","out",r.out,Math.min(r.count,Math.floor(r.owner.size/4))),Es("dedupe","outIndex",r.outIndex,r.outCount),r.countIndex!==ye){if(Es("dedupe","countIndex",r.countIndex,r.counters),r.countIndex===r.outIndex)throw new h("E_INVALID_ARGUMENT","dedupe: countIndex must not be the output word outIndex",{argument:"countIndex",value:r.countIndex});if(r.counters.buffer!==r.outCount.buffer||r.counters.offset!==r.outCount.offset||r.counters.size!==r.outCount.size)throw new h("E_INVALID_ARGUMENT","dedupe: counters must be the same range as outCount when countIndex names a device word",{argument:"counters",value:r.counters.size,expected:r.outCount.size})}}class gd{constructor(e,t,s,i,n){this.dispatches=0,this.scope=e,this.scatter=t,this.claim=s,this.filter=i,this.scan=n}get lastDispatches(){return this.dispatches}record(e,t){if(hd(t),t.count===0){this.dispatches=0;return}const s=4*t.count,i={buffer:this.scope.scratch(s,"compact/offsets"),offset:0,size:s,window:null};this.scan.record(e,t.flags,t.count,i);const n=this.scope.params(lr,{count:t.count,outIndex:t.outIndex,countIndex:ye,stride:0}),o=this.scatter.bind({queue:t.queue,flags:t.flags,offsets:i,out:t.out,outCount:t.outCount,P:n.binding});this.scatter.dispatch(e,o,z(t.count,this.scope.workgroupSize,this.scope.caps),[n.offset]),this.dispatches=this.scan.lastDispatches+1}recordDedupe(e,t){if(pd(t),t.count===0){this.dispatches=0;return}const s=Oe(t.count,this.scope.workgroupSize,this.scope.caps),i=this.bindDedupe(t,s.stride??this.scope.workgroupSize);this.claim.dispatch(e,i.claim,s,[i.offset]),this.filter.dispatch(e,i.filter,s,[i.offset]),this.dispatches=2}bindDedupe(e,t){const s=this.scope.params(lr,{count:e.count,outIndex:e.outIndex,countIndex:e.countIndex,stride:t});return{claim:this.claim.bind({queue:e.queue,owner:e.owner,counters:e.counters,P:s.binding}),filter:this.filter.bind({queue:e.queue,owner:e.owner,out:e.out,outCount:e.outCount,P:s.binding}),offset:s.offset}}}const st=Object.freeze({frontierCount:0,nextFrontierCount:1,frontierDegreeSum:2,prevFrontierCount:3,prevDegreeSum:4,unvisitedCount:5,unvisitedDegreeSum:6,unvisitedListLen:7,edgeCount:8,edgeCountUnclamped:9,overflowLevels:10,level:11,visitedCount:12,switches:13,direction:14,done:15,arcsScanned:16,fusedLevels:17,twoPhaseLevels:18,bottomUpLevels:19,farCount:20,nextFarCount:21,thresholdBits:22,deltaBits:23,path:24,nextDegreeSum:25,stackTop:26,sigmaOverflow:27});function Xn(r){const e={};for(const[t,s]of Object.entries(r))s!==void 0&&(e[t]=s);return e}const $i=3;function Ci(r,e){if(!Number.isSafeInteger(e)||e<0)throw new h("E_INVALID_ARGUMENT",`frontier: ${r} must be a non-negative integer`,{argument:r,value:e,expected:"a non-negative integer"})}class md{constructor(e,t,s,i,n){this.sideIndex=0,this.vertices=e,this.counters=t,this.edgeQueue=s,this.edgeCapacity=i,this.n=n}get side(){return this.sideIndex}get input(){return this.sideIndex===0?this.vertices[0]:this.vertices[1]}get output(){return this.sideIndex===0?this.vertices[1]:this.vertices[0]}swap(){this.sideIndex=this.sideIndex===0?1:0}reset(e,t,s){if(!Number.isInteger(t)||t<0||t>=this.n)throw new h("E_INVALID_ARGUMENT",`frontier: source ${t} is outside [0, ${this.n})`,{argument:"source",value:t,expected:`an integer in [0, ${this.n})`});const i=new ArrayBuffer(Ce.byteLength);Ce.write(new DataView(i),Xn(s)),this.sideIndex=0,e.writeBuffer(this.counters.buffer,this.counters.offset,i),e.writeBuffer(this.vertices[0].buffer,this.vertices[0].offset,Uint32Array.of(t))}}async function Qn(r,e,t,s){Ci("n",e),Ci("arcCount",t);const i=s??Math.max(1,Math.min(t,Math.floor(r.caps.limits.maxStorageBufferBindingSize/4)));if(!Number.isSafeInteger(i)||i<1||i>ye)throw new h("E_INVALID_ARGUMENT","frontier: edgeCapacity must be an integer >= 1",{argument:"edgeCapacity",value:i,expected:"an integer in [1, 2^32)"});const n=await r.pipelines.kernel(_("frontier-finalize")),o=4*Math.max(1,e),a=[{buffer:r.scratch(o,"frontier/vertices-0"),offset:0,size:o,window:null},{buffer:r.scratch(o,"frontier/vertices-1"),offset:0,size:o,window:null}],c={buffer:r.scratch(Ce.byteLength,"frontier/counters"),offset:0,size:Ce.byteLength,window:null},l={buffer:r.scratch(4*i,"frontier/edge-queue"),offset:0,size:4*i,window:null},d=new md(a,c,l,i,e);return new wd(r,n,d)}class wd{constructor(e,t,s){this.scope=e,this.kernel=t,this.frontier=s}recordFinalize(e,t,s,i){if(!Number.isInteger(s)||s<0||s>=ue)throw new h("E_INVALID_ARGUMENT",`frontier: level ${s} is outside the submit`,{argument:"level",value:s,expected:`an integer in [0, ${ue})`});if(!Number.isInteger(t)||t<0||t>$i)throw new h("E_INVALID_ARGUMENT",`frontier: role ${t} is not a finalize role`,{argument:"role",value:t,expected:`an integer in [0, ${$i}]`});const{scope:n,frontier:o}=this,a=n.params(ve,{...Xn(i),role:t,wg:n.workgroupSize,edgeCapacity:o.edgeCapacity,n:o.n}),c=this.kernel.bind({counters:o.counters,P:a.binding});this.kernel.dispatch(e,c,z(1,n.workgroupSize,n.caps),[a.offset])}}async function bd(r,e){const t=await r.pipelines.kernel(_("advance-expand",Ge(e,null)));return new vd(r,e,t)}class vd{constructor(e,t,s){this.scope=e,this.kernel=s,this.windows=Ss(t),this.n=fr(t,"advance")}record(e,t){if(t.n!==this.n)throw new h("E_INVALID_ARGUMENT",`advance: the frontier holds ${t.n} vertices, the core ${this.n} rows`,{argument:"frontier",value:t.n,expected:this.n});const{scope:s}=this,i=Oe(t.n,s.workgroupSize,s.caps);for(const n of this.windows){const o=s.params(ve,{wg:s.workgroupSize,edgeCapacity:t.edgeCapacity,n:t.n,arcBase:n.arcBase,arcEnd:n.arcEnd,stride:i.stride??s.workgroupSize}),a=this.kernel.bind({...Ze(n.core,null),frontierIn:t.input,counters:t.counters,edgeQueue:t.edgeQueue,P:o.binding});this.kernel.dispatch(e,a,i,[o.offset])}}}const Mi=8;function jr(r,e){return 4*kr*Math.ceil(r/e)}async function Us(r){const e=await r.pipelines.kernel(_("radix-hist")),t=await r.pipelines.kernel(_("radix-scatter")),s=await Rt(r);return new _d(r,e,t,s)}function Dt(r,e,t){if(e.size<t)throw new h("E_INVALID_ARGUMENT",`radixSort: ${r} is smaller than ${t} bytes`,{argument:r,value:e.size,expected:t})}function yd(r,e,t,s,i,n){if(s!==8&&s!==16&&s!==24&&s!==32)throw new h("E_INVALID_ARGUMENT","radixSort: bits must be 8, 16, 24 or 32",{argument:"bits",value:s,expected:[8,16,24,32]});if(!Number.isSafeInteger(t)||t<0||t>ye)throw new h("E_INVALID_ARGUMENT","radixSort: count must be a non-negative integer below 2^32",{argument:"count",value:t});const o=4*t;Dt("keys",r,o),Dt("vals",e,o),Dt("scratch.keys",i.keys,o),Dt("scratch.vals",i.vals,o);const a=jr(t,n);Dt("scratch.hist",i.hist,a),Dt("scratch.offsets",i.offsets,a)}class _d{constructor(e,t,s,i){this.dispatches=0,this.scope=e,this.hist=t,this.scatter=s,this.scan=i}get lastDispatches(){return this.dispatches}record(e,t,s,i,n,o){const a=this.scope.workgroupSize;if(yd(t,s,i,n,o,a),i===0)return this.dispatches=0,{keys:t,vals:s};const c=Math.ceil(i/a),l=z(i,a,this.scope.caps),d=kr*c,f=4*d,p={...o.hist,size:f},g={...o.offsets,size:f};let m={keys:t,vals:s},b={keys:o.keys,vals:o.vals},w=0;const v=n/Mi;for(let y=0;y<v;y++){const I=this.scope.params(Fs,{count:i,shift:Mi*y,groups:c,pad0:0}),S=this.hist.bind({keys:m.keys,hist:p,P:I.binding});this.hist.dispatch(e,S,l,[I.offset]),this.scan.record(e,p,d,g);const P=this.scatter.bind({keys:m.keys,vals:m.vals,offsets:g,keysOut:b.keys,valsOut:b.vals,P:I.binding});this.scatter.dispatch(e,P,l,[I.offset]),w+=2+this.scan.lastDispatches,[m,b]=[b,m]}return this.dispatches=w,m}}const Ie="breadthFirstSearch",Pd=128;function Sd(r,e){return Math.max((5+4*r)*e+16,Ed+r)}const Ed=34;function Id(r){return r===void 0||Number.isNaN(r)||r>=ye?ye:Math.max(0,Math.ceil(r))}function kd(r,e){if(r===void 0)return null;if(r instanceof Uint32Array&&r.length===e&&r.buffer instanceof ArrayBuffer)return r;throw new h("E_INVALID_ARGUMENT",`${Ie}: dest must be a Uint32Array of length ${e} over an ArrayBuffer`,{argument:"dest",value:`${r.constructor.name}(${r.length})`,expected:`Uint32Array(${e}) over an ArrayBuffer`})}function Ke(r,e){return{buffer:r,offset:0,size:e,window:null}}function Gi(r,e,t){const{bindings:s}=r.residency.view(e,t),{[t]:i}=s;if(i===void 0)throw new h("E_VALIDATION",`${Ie}: the ${t} view has no ${t} binding`,{label:`${Ie}/${t}`,message:`the ${t} view has no ${t} binding`});return i}function zi(r){return new h("E_ABORTED",`${Ie}: the signal was aborted`,r===void 0?{}:{batchId:r})}function tr(r,e){const t=r[e];if(typeof t!="number")throw new h("E_VALIDATION",`${Ie}: counters.${e} did not decode to a number`,{label:`${Ie}/counters`,message:`the field ${e} did not decode to a number`});return t}async function Zn(r,e,t,s,i){r.assertReady(),await Ve(r);const n=e.nodeCount;if(!Number.isInteger(t)||t<0||t>=n)throw new h("E_INVALID_ARGUMENT",`${Ie}: source ${t} is outside [0, ${n})`,{argument:"source",value:t,expected:`an integer in [0, ${n})`});const o=i.levelsPerSubmit??ue;if(!Number.isInteger(o)||o<1||o>ue)throw new h("E_INVALID_ARGUMENT",`${Ie}: levelsPerSubmit must be an integer in [1, ${ue}]`,{argument:"levelsPerSubmit",value:o,expected:`an integer in [1, ${ue}]`});const a=kd(s?.dest,n),c=Id(s?.maxDepth),l=i.predKind??1;if(s?.signal?.aborted)throw zi();const d=r.residency.core(e),f=Ss(d);if(e.directed&&4*e.arcCount>r.caps.limits.maxStorageBufferBindingSize)throw new h("E_TOO_LARGE",`${Ie}: the reverse adjacency of a directed snapshot (${4*e.arcCount} bytes) needs arc windows, which no view executes (spec 4.3); the bottom-up sweep binds it whole`,{needed:4*e.arcCount,limit:r.caps.limits.maxStorageBufferBindingSize,path:"windowed",algorithm:Ie});const p=e.directed?Ur(r.residency.view(e,"reverse"),e.arcCount):d,g=Ss(p),m=Gi(r,e,"outDegree"),b=Gi(r,e,"inDegree"),w=je(r,Ie,Sd(f.length,o));i.onScope?.(w);try{const v=4*n,y=r.workgroupSize,I=Ke(w.scratch(v,"depth"),v),S=Math.ceil(n/64)*64,P=Math.ceil(n/32),E=4*(S+P),A=Ke(w.scratch(E,"sweep-in"),E),L={buffer:A.buffer,offset:0,size:v,window:null},B={buffer:A.buffer,offset:4*S,size:4*P,window:null},D=Ke(w.scratch(v,"unvisited-flags"),v),x=Ke(w.scratch(v,"iota"),v),T=Ke(w.scratch(4,"compact-count"),4);await r.allocator.check();const k=await Qn(w,n,e.arcCount,i.edgeCapacity),$=await bd(w,d),N=await Vr(w),R=await r.pipelines.kernel(_("bfs-contract")),O=await r.pipelines.kernel(_("bfs-fused",Ge(d,null))),C=await r.pipelines.kernel(_("bfs-bitset-build")),F=await r.pipelines.kernel(_("bfs-bottom-up",Ge(p,null))),M=await r.pipelines.kernel(_("bfs-unvisited-flags")),X=await r.pipelines.kernel(_("bfs-next-degree")),K=await r.pipelines.kernel(_("sssp-pred",{...Ge(d,null),MODE:1})),q=await r.pipelines.kernel(_("fill")),j=await Us(w),{frontier:G}=k,{counters:W}=G,{queue:H}=r.device,Q=z(n,y,r.caps),V=Oe(Math.max(n,G.edgeCapacity),y,r.caps),U=Oe(n,y,r.caps),oe=Oe(n,y,r.caps,Pd),te=Oe(n*y,y,r.caps),de=z(P,y,r.caps),pe=(Pe,Se,pt,Yr)=>{const wr=w.params(Ae,{count:n,value:pt,mode:Yr,pad0:0});q.dispatch(Pe,q.bind({dst:Se,P:wr.binding}),Q,[wr.offset])},ge=Oe(n,y,r.caps),ce=Pe=>{const Se=w.params(ve,{wg:y,n,stride:ge.stride??n}),pt=M.bind({inDegree:b,depth:I,flags:D,counters:W,P:Se.binding});M.dispatch(Pe,pt,ge,[Se.offset]),N.record(Pe,{queue:x,flags:D,count:n,out:L,outCount:T,outIndex:0})},xe=Pe=>(w.flush(),Pe.submit()),ae=new ne(r,`${Ie}/setup`),Y=ae.pass("fill");pe(Y,I,dt,0),pe(Y,x,0,1),ae.endPass(),await xe(ae).readback,r.assertReady(),H.writeBuffer(I.buffer,I.offset+4*t,Uint32Array.of(0)),G.reset(H,t,{nextFrontierCount:1,level:ye});const Z={mode:{auto:0,"top-down":1,"bottom-up":2}[i.direction??"auto"],alpha:i.alpha??ta,beta:i.beta??ra,fusedMax:i.fusedMax??ea,maxDepth:c};let we=0,me=0;for(;;){H.writeBuffer(W.buffer,W.offset+4*st.unvisitedCount,new Uint32Array(3));const Pe=new ne(r,`${Ie}/levels`),Se=Pe.pass("bfs");ce(Se);const pt=w.params(Ae,{count:P,value:0,mode:0,pad0:0}),Yr=q.bind({dst:B,P:pt.binding});for(let gt=0;gt<o;gt++){k.recordFinalize(Se,0,gt,{...Z,firstOfSubmit:Math.min(gt,1)}),$.record(Se,G),k.recordFinalize(Se,1,gt,Z);const Lt=w.params(ve,{wg:y,n,edgeCapacity:G.edgeCapacity,arcBase:0,arcEnd:e.arcCount,bitsBase:S,stride:V.stride??y}),Xr=R.bind({edgeQueue:G.edgeQueue,counters:W,depth:I,frontierOut:G.output,P:Lt.binding});R.dispatch(Se,Xr,V,[Lt.offset]);for(const St of f){const Jt=w.params(ve,{wg:y,n,edgeCapacity:G.edgeCapacity,arcBase:St.arcBase,arcEnd:St.arcEnd,stride:te.x*te.y}),Qr=O.bind({...Ze(St.core,null),frontierIn:G.input,counters:W,depth:I,frontierOut:G.output,P:Jt.binding});O.dispatch(Se,Qr,te,[Jt.offset])}q.dispatch(Se,Yr,de,[pt.offset]);const To=C.bind({frontierIn:G.input,counters:W,bits:A,P:Lt.binding});C.dispatch(Se,To,V,[Lt.offset]);for(const St of g){const Jt=w.params(ve,{wg:y,n,arcBase:St.arcBase,arcEnd:St.arcEnd,bitsBase:S,stride:U.stride??y}),Qr=F.bind({...Ze(St.core,null),sweepIn:A,counters:W,depth:I,frontierOut:G.output,P:Jt.binding});F.dispatch(Se,Qr,U,[Jt.offset])}const ni=w.params(ve,{wg:y,n,stride:oe.stride??y}),Oo=X.bind({frontier:G.output,outDegree:m,inDegree:b,counters:W,P:ni.binding});X.dispatch(Se,Oo,oe,[ni.offset]),G.swap()}Pe.endPass();const wr=Pe.readback(W.buffer,W.offset+4*st.done,4),br=i.onLevel===void 0?null:{block:Pe.readback(W.buffer,W.offset,Ce.byteLength),frontier:Pe.readback(G.input.buffer,G.input.offset,G.input.size),count:Pe.readback(T.buffer,T.offset,4)},ii=xe(Pe),vr=await ii.readback;if(we+=o,me+=1,r.assertReady(),s?.signal?.aborted)throw zi(ii.id);if(s?.onProgress?.(Math.min(we,n),n),br!==null&&i.onLevel!==void 0){const gt=Ce.read(new DataView(vr),br.block.offset),Lt=new Uint32Array(vr,br.frontier.offset,tr(gt,"nextFrontierCount")),Xr=new Uint32Array(vr,br.count.offset,1)[0];i.onLevel(we-1,gt,Lt.slice(),Xr)}if(new Uint32Array(vr,wr.offset,1)[0]!==0)break;if(me>n+1)throw new h("E_VALIDATION",`${Ie}: the done flag never rose in ${me} submits (a traversal has at most ${n} levels)`,{label:Ie,message:`the done flag never rose in ${me} submits`})}const Le=Ke(w.scratch(v,"order/keys"),v),ot=Ke(w.scratch(v,"order/vals"),v),qe=jr(n,y),at={keys:Ke(w.scratch(v,"order/keys-scratch"),v),vals:Ke(w.scratch(v,"order/vals-scratch"),v),hist:Ke(w.scratch(qe,"order/hist"),qe),offsets:Ke(w.scratch(qe,"order/offsets"),qe)},rt=Ke(w.scratch(v,"parent"),v),ze=new ne(r,`${Ie}/result`);ze.copy(I,Le,v);const J=ze.pass("result");pe(J,ot,0,1);const De=j.record(J,Le,ot,n,32,at);pe(J,rt,dt,0);const ct=Oe(n,y,r.caps);for(const Pe of f){const Se=w.params(ve,{wg:y,n,arcBase:Pe.arcBase,arcEnd:Pe.arcEnd,predKind:l,source:t,stride:ct.stride??n}),pt=K.bind({...Ze(Pe.core,null),dist:I,pred:rt,P:Se.binding});K.dispatch(J,pt,ct,[Se.offset])}ze.endPass();const ht=ze.readback(I.buffer,I.offset,v),pr=ze.readback(rt.buffer,rt.offset,v),Hr=ze.readback(De.vals.buffer,De.vals.offset,v),Lo=ze.readback(W.buffer,W.offset,Ce.byteLength),gr=await xe(ze).readback;r.assertReady();const mr=Ce.read(new DataView(gr),Lo.offset),Zt=tr(mr,"visitedCount");if(Zt>n)throw new h("E_VALIDATION",`${Ie}: visitedCount ${Zt} exceeds the ${n} vertices (a duplicate claim)`,{label:`${Ie}/visitedCount`,message:`the device counted ${Zt} visits of ${n} vertices`});const ri=tr(mr,"level"),Do=tr(mr,"frontierCount")===0?ri:ri+1,si=a??new Uint32Array(n);return si.set(new Uint32Array(gr,ht.offset,n)),{depth:si,parent:new Uint32Array(gr,pr.offset,n).slice(),order:new Uint32Array(gr,Hr.offset,Zt).slice(),visitedCount:Zt,levels:Do,switches:tr(mr,"switches")}}finally{w.dispose()}}function Ad(r,e,t,s){return Zn(r,e,t,s,{})}const le="sssp",Fi=64,xd=4*ue+40;function Is(r){return new Uint32Array(Float32Array.of(r).buffer)[0]}function $e(r,e){return{buffer:r,offset:0,size:e,window:null}}function Je(r,e){return new h("E_ABORTED",`${r}: the signal was aborted`,e===void 0?{}:{batchId:e})}function Jn(r,e,t){if(!Number.isInteger(e)||e<0||e>=t)throw new h("E_INVALID_ARGUMENT",`${r}: source ${e} is outside [0, ${t})`,{argument:"source",value:e,expected:`an integer in [0, ${t})`})}function as(r,e){const t=r[e];if(typeof t!="number")throw new h("E_VALIDATION",`${le}: counters.${e} did not decode to a number`,{label:`${le}/counters`,message:`the field ${e} did not decode to a number`});return t}function Qt(r,e,t){if(e===void 0)return null;if(e instanceof Float32Array&&e.length===t&&e.buffer instanceof ArrayBuffer)return e;throw new h("E_INVALID_ARGUMENT",`${r}: dest must be a Float32Array of length ${t} over an ArrayBuffer`,{argument:"dest",value:`${e.constructor.name}(${e.length})`,expected:`Float32Array(${t}) over an ArrayBuffer`})}function eo(r,e){if(e===void 0)return 1/0;if(Number.isNaN(e))throw new h("E_INVALID_ARGUMENT",`${r}: cutoff must not be NaN`,{argument:"cutoff",value:e,expected:"a number (the CPU port and the kernel would disagree on NaN)"});return e}function to(r,e,t){if(t===void 0){if(e.weights===null)return null;let c=0;for(const l of e.weights)c+=l;return{values:e.weights,override:null,allOne:e.flags.allWeightsOne,nonNegative:e.flags.nonNegativeWeights,finite:e.flags.finiteWeights,sum:c}}if(t.length!==e.arcCount)throw new h("E_INVALID_ARGUMENT",`${r}: weights has ${t.length} entries, the snapshot ${e.arcCount} arcs`,{argument:"weights",value:t.length,expected:e.arcCount});const s=t instanceof Float32Array?t:Float32Array.from(t);let i=!0,n=!0,o=!0,a=0;for(const c of s)c!==1&&(i=!1),c<0&&(n=!1),Number.isFinite(c)||(o=!1),a+=c;return{values:s,override:s,allOne:i,nonNegative:n,finite:o,sum:a}}async function ro(r,e,t,s,i,n){const o=s===1/0?void 0:Math.floor(s),a=await Zn(r,e,t,{maxDepth:o,signal:n?.signal,onProgress:n?.onProgress},{predKind:0}),c=i??new Float32Array(e.nodeCount);for(let l=0;l<e.nodeCount;l++){const d=a.depth[l];c[l]=d===dt?1/0:d}return{dist:c,predArc:a.parent,reachedCount:a.visitedCount}}function so(r){return 2*Math.ceil(r/64)*64+64}async function io(r){const{algorithm:e,ctx:t,scope:s,predKernel:i,recordFill:n,graph:o,dist:a,pred:c,n:l,arcCount:d,source:f,mode:p}=r,g=t.workgroupSize,{queue:m}=t.device,b=4*l,w=Math.ceil(l/64)*64,v={buffer:c.buffer,offset:c.offset,size:b,window:null};m.writeBuffer(c.buffer,c.offset+4*(w+f),Uint32Array.of(0)),m.writeBuffer(c.buffer,c.offset+8*w,new Uint32Array(2));const y=Oe(l,g,t.caps),I={wg:g,n:l,arcBase:0,arcEnd:d,predKind:0,source:f,stride:y.stride??l,mode:p},S=Math.ceil((l+1)/ue)+1;let P=0;for(;;){const E=new ne(t,`${e}/pred`),A=E.pass("pred"),L=(N,R)=>{const O=s.params(ve,{...I,role:N,iteration:R}),C=i.bind({...o,dist:a,pred:c,P:O.binding});i.dispatch(A,C,y,[O.offset])};P===0&&p===0&&L(0,0);for(let N=0;N<ue;N++)L(1,N);n(A,v,l,dt),L(2,0),E.endPass();const B=E.readback(a.buffer,a.offset,b),D=E.readback(c.buffer,c.offset,b),x=E.readback(c.buffer,c.offset+8*w,8);s.flush();const T=await E.submit().readback;t.assertReady(),P+=1;const[k,$]=new Uint32Array(T,x.offset,2);if(k<ue)return{dist:new Float32Array(T,B.offset,l),predArc:new Uint32Array(T,D.offset,l).slice(),orphans:$};if(P>S)throw new h("E_VALIDATION",`${e}: the hop passes still changed something after ${P} batches (a hop count is at most ${l})`,{label:`${e}/pred`,message:`the hop passes did not converge in ${P} batches`});m.writeBuffer(c.buffer,c.offset+8*w,new Uint32Array(2))}}async function Rd(r,e,t,s,i){r.assertReady(),await Ve(r);const n=e.nodeCount;Jn(le,t,n);const o=i.roundsPerSubmit??ue;if(!Number.isInteger(o)||o<1||o>ue)throw new h("E_INVALID_ARGUMENT",`${le}: roundsPerSubmit must be an integer in [1, ${ue}]`,{argument:"roundsPerSubmit",value:o,expected:`an integer in [1, ${ue}]`});if(i.delta!==void 0&&!(Number.isFinite(i.delta)&&i.delta>0))throw new h("E_INVALID_ARGUMENT",`${le}: delta must be a finite positive number`,{argument:"delta",value:i.delta,expected:"a finite positive number"});const a=Qt(le,s?.dest,n),c=to(le,e,s?.weights),l=eo(le,s?.cutoff);if(s?.signal?.aborted)throw Je(le);if(c===null||c.allOne)return ro(r,e,t,l,a,s);if(!c.nonNegative)throw new h("E_UNSUPPORTED",`${le}: a negative weight has no shortest path here`,{feature:"sssp.negativeWeights",hint:"use bellmanFord"});if(!c.finite)throw new h("E_UNSUPPORTED",`${le}: a NaN or infinite weight has no bit-pattern order`,{feature:"sssp.nonFiniteWeights"});const{arcCount:d}=e,f=r.residency.core(e),p=r.caps.limits.maxStorageBufferBindingSize;Pt(f,d,p,le);const g=Math.ceil(Math.max(1,d)/Fi)*Fi;if(8*g>p)throw new h("E_TOO_LARGE",`${le}: the near-far queue of ${g} entries per half needs ${8*g} bytes, above the ${p}-byte binding limit (the relax is never windowed)`,{needed:8*g,limit:p,path:"sssp.queue",algorithm:le});const m=Math.fround(i.delta??(c.sum===0?1:sa*(c.sum/d)/(d/n))),b=Is(m),w=n+Math.ceil(c.sum/m)+1,v=Math.ceil((w+1)/o)+1,y=je(r,le,xd);try{const I=r.workgroupSize,S=4*n,P=$e(y.scratch(S,"dist"),S),E=$e(y.scratch(S,"owner"),S),A=$e(y.scratch(8*g,"queue-out"),8*g),L={buffer:A.buffer,offset:0,size:4*g,window:null},B={buffer:A.buffer,offset:4*g,size:4*g,window:null},D=so(n),x=$e(y.scratch(4*D,"pred"),4*D),{queue:T}=r.device;let k;if(c.override!==null){const ae=y.scratch(4*d,"weights");T.writeBuffer(ae,0,c.override),k=$e(ae,4*d)}await r.allocator.check();const $=await Qn(y,n,d,g),N=await Vr(y),R=Ge(f,null,k),O=await r.pipelines.kernel(_("sssp-relax",R)),C=await r.pipelines.kernel(_("sssp-pred",{...R,MODE:0})),F=await r.pipelines.kernel(_("fill")),M=Ze(f,null,k),{frontier:X}=$,{counters:K}=X,q=X.vertices[0],j=X.vertices[1],G=(ae,Y,Z,we)=>{const me=y.params(Ae,{count:Z,value:we,mode:0,pad0:0});F.dispatch(ae,F.bind({dst:Y,P:me.binding}),z(Z,I,r.caps),[me.offset])},W=ae=>(y.flush(),ae.submit()),H=new ne(r,`${le}/setup`),Q=H.pass("fill");G(Q,P,n,Gt),G(Q,x,D,dt),H.endPass(),await W(H).readback,r.assertReady(),T.writeBuffer(P.buffer,P.offset+4*t,Uint32Array.of(0)),X.reset(T,t,{nextFrontierCount:1,thresholdBits:b,deltaBits:b}),T.writeBuffer(A.buffer,A.offset,Uint32Array.of(t));const V={queue:L,count:g,countIndex:st.edgeCount,counters:K,owner:E,out:q,outCount:K,outIndex:st.frontierCount},U={queue:B,count:g,countIndex:st.edgeCountUnclamped,counters:K,owner:E,out:j,outCount:K,outIndex:st.farCount},oe=Oe(n,I,r.caps),te={wg:I,n,edgeCapacity:g,arcBase:0,arcEnd:d,cutoffBits:Is(l),source:t,stride:oe.stride??I};let de=0,pe=0;for(;;){const ae=new ne(r,`${le}/rounds`),Y=ae.pass("sssp"),Z=y.params(ve,{...te,role:0}),we=y.params(ve,{...te,role:1}),me=O.bind({...M,dist:P,counters:K,queueIn:q,queueOut:A,P:Z.binding}),Le=O.bind({...M,dist:P,counters:K,queueIn:j,queueOut:A,P:we.binding});for(let J=0;J<o;J++)$.recordFinalize(Y,2,J,{}),N.recordDedupe(Y,V),N.recordDedupe(Y,U),$.recordFinalize(Y,3,J,{}),O.dispatch(Y,me,oe,[Z.offset]),O.dispatch(Y,Le,oe,[we.offset]);ae.endPass();const ot=ae.readback(K.buffer,K.offset+4*st.done,4),qe=i.onRound===void 0?null:ae.readback(K.buffer,K.offset,Ce.byteLength),at=W(ae),rt=await at.readback;if(de+=o,pe+=1,r.assertReady(),s?.signal?.aborted)throw Je(le,at.id);s?.onProgress?.(Math.min(de,w),w),qe!==null&&i.onRound!==void 0&&i.onRound(de-1,Ce.read(new DataView(rt),qe.offset));const ze=new Uint32Array(rt,ot.offset,1)[0];if(ze===1)break;if(ze!==0){const J=Ce.read(new DataView(await r.readback.read(K.buffer,Ce.byteLength,void 0,K.offset)));if(ze===2){const De=Math.max(as(J,"nextFrontierCount"),as(J,"nextFarCount"));throw new h("E_TOO_LARGE",`${le}: a raw pile of ${De} entries overflowed its ${g}-entry half`,{needed:De,limit:g,path:"sssp.pile",algorithm:le})}throw new h("E_UNSUPPORTED",`${le}: the f32 threshold ${as(J,"thresholdBits")} absorbed the delta ${b} (as bit patterns); the far pile can no longer be bucketed`,{feature:"sssp.thresholdAbsorbed",hint:"the distances outgrew the delta's f32 precision"})}if(pe>v)throw new h("E_VALIDATION",`${le}: the done flag never rose in ${pe} submits (at most ${w} rounds)`,{label:`${le}/rounds`,message:`the done flag never rose in ${pe} submits`})}const ge=await io({algorithm:le,ctx:r,scope:y,predKernel:C,recordFill:G,graph:M,dist:P,pred:x,n,arcCount:d,source:t,mode:0});if(ge.orphans!==0)throw new h("E_VALIDATION",`${le}: ${ge.orphans} reached node(s) the predecessor key never reached (a kernel bug)`,{label:`${le}/pred`,message:`${ge.orphans} orphan(s) in the predecessor pass`});const ce=a??new Float32Array(n);ce.set(ge.dist);let xe=0;for(const ae of ce)ae!==1/0&&(xe+=1);return{dist:ce,predArc:ge.predArc,reachedCount:xe}}finally{y.dispose()}}function no(r,e,t,s){return Rd(r,e,t,s,{})}const wt="allPairsShortestPath",Bt=Math.floor(la/3);function oo(r){const e=r.maxStorageBufferBindingSize,t=r.maxBufferSize,s=t<e?"maxBufferSize":"maxStorageBufferBindingSize",i=Math.min(e,t);let n=Math.floor(Math.sqrt(i/4));for(;4*n*n>i;)n-=1;for(;4*(n+1)*(n+1)<=i;)n+=1;return{maxNodes:n,limit:i,limitName:s}}async function ao(r,e,t,s,i,n,o){const a=e.nodeCount,c=r.residency.core(e);Pt(c,e.arcCount,r.caps.limits.maxStorageBufferBindingSize,o);const l=Math.ceil(a/Ns),d=r.workgroupSize,f=4*a*a,p=$e(t.scratch(f,"dist"),f);await r.allocator.check();const g=s?void 0:null,m=await r.pipelines.kernel(_("fill")),b=await r.pipelines.kernel(_("apsp-init",Ge(c,null,g))),w=await Promise.all([0,1,2].map(S=>r.pipelines.kernel(_("apsp-fw",{PHASE:S})))),v=Ze(c,null,g),y=l-1,I=[Ft(1,r.caps),Ft(2*y,r.caps),Ft(y*y,r.caps)];for(let S=0;S<l;S+=i){const P=new ne(r,`${o}/sweep`),E=P.pass("apsp");if(S===0){const B=t.params(Ae,{count:a*a,value:Gt,mode:0,pad0:0});m.dispatch(E,m.bind({dst:p,P:B.binding}),z(a*a,d,r.caps),[B.offset]);const D=t.params(Lr,{n:a,round:0,blocks:l,infBits:Gt});b.dispatch(E,b.bind({...v,dist:p,P:D.binding}),z(a,d,r.caps),[D.offset])}const A=Math.min(S+i,l);for(let B=S;B<A;B++){const D=t.params(Lr,{n:a,round:B,blocks:l,infBits:Gt});for(let x=0;x<3;x++){const T=w[x];T.dispatch(E,T.bind({dist:p,P:D.binding}),I[x],[D.offset])}}P.endPass(),t.flush();const L=P.submit();if(await L.readback,r.assertReady(),n?.onProgress?.(A,l),n?.signal?.aborted)throw Je(o,L.id)}return p}async function Ld(r,e,t,s){r.assertReady();const i=e.nodeCount,n=s.roundsPerSubmit??Bt;if(!Number.isInteger(n)||n<1||n>Bt)throw new h("E_INVALID_ARGUMENT",`${wt}: roundsPerSubmit must be an integer in [1, ${Bt}]`,{argument:"roundsPerSubmit",value:n,expected:`an integer in [1, ${Bt}]`});const{maxNodes:o,limit:a,limitName:c}=oo(r.caps.limits);if(i>o)throw new h("E_TOO_LARGE",`${wt}: ${i} nodes need a ${4*i*i}-byte distance matrix in one storage binding; this device's ${c} of ${a} bytes holds at most ${o} nodes -- raise it through GpuContextOptions.limits`,{needed:4*i*i,limit:a,path:"allPairs.matrix",algorithm:wt,nodes:i,maxNodes:o,limitName:c,hint:`raise ${c} through GpuContextOptions.limits`});const l=Qt(wt,t?.dest,i*i)??new Float32Array(i*i),d=e.weights!==null&&e.weights!==null&&!e.flags.allWeightsOne;if(d&&!e.flags.nonNegativeWeights)throw new h("E_UNSUPPORTED",`${wt}: a negative weight is not supported`,{feature:"allPairs.negativeWeights",hint:"the blocked Floyd-Warshall sweep needs non-negative weights; pass weighted: false for hop counts"});if(d&&!e.flags.finiteWeights)throw new h("E_UNSUPPORTED",`${wt}: a NaN or infinite weight has no shortest path`,{feature:"allPairs.nonFiniteWeights"});if(i===0)return{dist:l,n:i};await Ve(r);const f=je(r,wt,Math.min(n,Math.ceil(i/Ns))+2);try{const p=await ao(r,e,f,d,n,t,wt),g=4*i*i;return await r.readback.read(p.buffer,g,l),r.assertReady(),{dist:l,n:i}}finally{f.dispose()}}function Dd(r,e,t){return Ld(r,e,t,{})}const Ee="bellmanFord",Td=8,Od=16,Nd=ue+16;function Bd(r,e){const{arcToEdge:t,edgeToArc:s}=r;for(let i=0;i<r.arcCount;i++){const n=s[t[i]];if(e[i]!==e[n])throw new h("E_UNSUPPORTED",`${Ee}: weights[${i}] = ${e[i]} differs from weights[${n}] = ${e[n]}, the forward arc of the same undirected edge; the kernel reads one weight per edge`,{feature:"bellmanFord.asymmetricUndirectedWeights",hint:"use a directed snapshot"})}}async function $d(r,e,t,s,i){r.assertReady(),await Ve(r);const n=e.nodeCount;Jn(Ee,t,n);const o=i.maxRetries??Od;if(!Number.isInteger(o)||o<1)throw new h("E_INVALID_ARGUMENT",`${Ee}: maxRetries must be an integer >= 1`,{argument:"maxRetries",value:o,expected:"an integer >= 1"});const a=i.roundsPerBatch??Td;if(!Number.isInteger(a)||a<1||a>ue)throw new h("E_INVALID_ARGUMENT",`${Ee}: roundsPerBatch must be an integer in [1, ${ue}]`,{argument:"roundsPerBatch",value:a,expected:`an integer in [1, ${ue}]`});const c=Qt(Ee,s?.dest,n),l=to(Ee,e,s?.weights),d=eo(Ee,s?.cutoff);if(s?.signal?.aborted)throw Je(Ee);if(l===null||l.allOne)return{result:{...await ro(r,e,t,d,c,s),hasNegativeCycle:!1},rounds:0,retryExhaustedRounds:0};if(!l.finite)throw new h("E_UNSUPPORTED",`${Ee}: a NaN or infinite weight has no shortest path`,{feature:"bellmanFord.nonFiniteWeights"});!e.directed&&l.override!==null&&Bd(e,l.override);const{arcCount:f}=e,p=r.residency.core(e,["rowPtr","colIdx","weights","edgeToArc"]);Pt(p,f,r.caps.limits.maxStorageBufferBindingSize,Ee);const g=r.residency.view(e,"edgeList"),m=g.scalars.edgeCount[0],b=je(r,Ee,Nd);try{const w=r.workgroupSize,v=4*n,y=$e(b.scratch(v,"dist"),v),I=so(n),S=$e(b.scratch(4*I,"pred"),4*I),P=$e(b.scratch(er.byteLength,"flags"),er.byteLength);let E=null,{edgeToArc:A}=p;A===null&&(E=$e(b.scratch(4*m,"iota"),4*m),A=E);const{queue:L}=r.device;let B;if(l.override!==null){const U=b.scratch(4*f,"weights");L.writeBuffer(U,0,l.override),B=$e(U,4*f)}await r.allocator.check();const D=Ge(p,null,B),x=await r.pipelines.kernel(_("bf-relax",{UNDIRECTED:!e.directed})),T=await r.pipelines.kernel(_("sssp-pred",{...D,MODE:0})),k=await r.pipelines.kernel(_("fill")),$=Ze(p,null,B),N=(U,oe,te,de,pe=0)=>{const ge=b.params(Ae,{count:te,value:de,mode:pe,pad0:0});k.dispatch(U,k.bind({dst:oe,P:ge.binding}),z(te,w,r.caps),[ge.offset])},R=U=>(b.flush(),U.submit()),O=new ne(r,`${Ee}/setup`),C=O.pass("fill");N(C,y,n,Gt),N(C,S,I,dt),E!==null&&N(C,E,m,0,1),O.endPass(),await R(O).readback,r.assertReady(),L.writeBuffer(y.buffer,y.offset+4*t,Uint32Array.of(0));const F=Oe(m,w,r.caps),M={edgeSrc:g.bindings.src,edgeDst:g.bindings.dst,edgeToArc:A,weights:$.weights,dist:y,flags:P},X={edgeCount:m,stride:F.stride??m,maxRetries:o,cutoffBits:Is(d)},K=new Uint32Array(er.byteLength/4),q=async(U,oe)=>{L.writeBuffer(P.buffer,P.offset,K);const te=new ne(r,`${Ee}/${oe}`),de=te.pass("relax"),pe=b.params(Vn,X),ge=x.bind({...M,P:pe.binding});for(let Z=0;Z<U;Z++)x.dispatch(de,ge,F,[pe.offset]);te.endPass();const ce=te.readback(P.buffer,P.offset,er.byteLength),xe=R(te),ae=await xe.readback;r.assertReady();const Y=er.read(new DataView(ae),ce.offset);return{changed:Number(Y.changed),retryExhausted:Number(Y.retryExhausted),id:xe.id}};let j=0,G=0,W=!1;for(;;){const U=n-1-j;if(U<=0){const de=await q(1,"decision");if(de.retryExhausted!==0)throw new h("E_VALIDATION",`${Ee}: a lane exhausted the ${o}-retry compare-exchange bound in the decision round, so its change is not a verdict`,{label:`${Ee}/retry`,message:"retryExhausted in the decision round",batchId:de.id});W=de.changed!==0;break}const oe=Math.min(a,U),te=await q(oe,"rounds");if(j+=oe,te.retryExhausted!==0&&(G+=1),s?.signal?.aborted)throw Je(Ee,te.id);if(s?.onProgress?.(j,n),te.changed===0&&te.retryExhausted===0)break}const H=await io({algorithm:Ee,ctx:r,scope:b,predKernel:T,recordFill:N,graph:$,dist:y,pred:S,n,arcCount:f,source:t,mode:1});if(H.orphans!==0&&!W)throw new h("E_UNSUPPORTED",`${Ee}: ${H.orphans} reached node(s) the tight subgraph never reaches (a cycle of weights below one f32 ulp relaxed once)`,{feature:"bellmanFord.roundedCycle",hint:"a cycle of weights below one f32 ulp relaxed once at a distance above 2^24; scale the weights or shorten the distances"});const Q=c??new Float32Array(n);Q.set(H.dist);let V=0;for(const U of Q)U!==1/0&&(V+=1);return{result:{dist:Q,predArc:H.predArc,reachedCount:V,hasNegativeCycle:W},rounds:j,retryExhaustedRounds:G}}finally{b.dispose()}}async function Cd(r,e,t,s){return(await $d(r,e,t,s,{})).result}class Md{constructor(e,t,s,i,n){this.dispatches=0,this.scope=e,this.tiers=t,this.perm=s,this.weights=i,this.hasWeights=n}get lastDispatches(){return this.dispatches}record(e,t,s,i){const{HAS_WEIGHTS:n}=Ge(t,this.perm,this.weights);if(n!==this.hasWeights)throw new h("E_INVALID_ARGUMENT","spmvPull: the core's weights pattern differs from the one prepared",{argument:"rev",value:n,expected:this.hasWeights});const o=fr(t,"spmvPull"),a=this.scope.workgroupSize;this.dispatches=0;for(const{tier:c,kernel:l,start:d,end:f}of this.tiers){const p=c===0?o:f,g=p-d;if(g<=0)continue;const m=c===2?1:a/lt,b=c===0?Oe(g,a,this.scope.caps):z(g,m,this.scope.caps);if(b.x===0)continue;const w=this.scope.params(Wn,{n:p,arcBase:0,arcEnd:Wr(t),stride:b.stride??g,alpha:i.alpha,beta:i.beta,uniformP:i.uniformP,start:d}),v=l.bind({...Ze(t,this.perm,this.weights),xNorm:s.xNorm,rankOut:s.rankOut,personalization:s.personalization??s.xNorm,partials:s.partials,P:w.binding});l.dispatch(e,v,b,[w.offset]),this.dispatches++}}}async function ks(r,e,t){od(e,"spmvPull");const{tiers:s}=t,i=s?.perm??null;if(s!==null&&r.workgroupSize<lt)throw new h("E_UNSUPPORTED",`spmvPull: the mid tier folds ${lt} lanes per row, more than the workgroup size ${r.workgroupSize}`,{feature:"spmvPull.tiers"});const n=s===null?[{tier:0,start:0,end:fr(e,"spmvPull")}]:[{tier:2,start:0,end:s.segmentOffsets[1]},{tier:1,start:s.segmentOffsets[1],end:s.segmentOffsets[2]},{tier:0,start:s.segmentOffsets[2],end:s.segmentOffsets[4]}],o=[];for(const c of n){if(c.tier!==0&&c.end<=c.start)continue;const l=_("spmv-pull",{...Ge(e,i,t.weights),HAS_PERSONALIZATION:t.personalization,USE_DANGLING:t.dangling,TIER:c.tier});o.push({...c,kernel:await r.pipelines.kernel(l)})}const{HAS_WEIGHTS:a}=Ge(e,i,t.weights);return new Md(r,o,i,t.weights,a)}const co=8,Gd=4*co+8;function zd(r,e,t){if(r===void 0)return null;if(r instanceof Float32Array&&r.length===e&&r.buffer instanceof ArrayBuffer)return r;throw new h("E_INVALID_ARGUMENT",`${t}: dest must be a Float32Array of length ${e} over an ArrayBuffer`,{argument:"dest",value:`${r.constructor.name}(${r.length})`,expected:`Float32Array(${e}) over an ArrayBuffer`})}function lo(r,e,t){const s=r.residency.core(e);return Pt(s,e.arcCount,r.caps.limits.maxStorageBufferBindingSize,t),s}function Vs(r,e){r.residency.core(e,["rowPtr"]);const t=r.residency.view(e,"reverse");return Ur(t,t.scalars.arcCount[0])}function uo(r,e){return new h("E_ABORTED",`${r}: the signal was aborted`,e===void 0?{}:{batchId:e})}function cs(r,e){return{buffer:r,offset:0,size:e,window:null}}async function Nr(r,e,t){await Ve(r);const s=je(r,t.label,Gd);try{const i=4*e,n=(t.alternate===null?["rankA","rankB"]:["rankA","rankB","rankC"]).map(E=>cs(s.scratch(i,E),i)),o=cs(s.scratch(i,"xNorm"),i),a=z(e,r.workgroupSize,r.caps),c=Gs(a),l=ke.byteLength*(1+c),d=s.scratch(l,"partials"),f=cs(d,l);await r.allocator.check();const p={personalization:!1,dangling:!1,weights:t.weights,tiers:null},g=[{core:t.adjacency,pull:await ks(s,t.adjacency,p)}];t.alternate!==null&&g.push({core:t.alternate,pull:await ks(s,t.alternate,p)});const m=await r.pipelines.kernel(_("pr-scale",{NORM_MODE:t.normMode})),b=t.normMode===4?null:await r.pipelines.kernel(_("pr-scale",{NORM_MODE:3})),w=await r.pipelines.kernel(_("pr-finalize",{NORM_MODE:t.normMode})),v=z(1,r.workgroupSize,r.caps),{queue:y}=r.device;y.writeBuffer(n[0].buffer,0,new Float32Array(e).fill(1/e));for(const E of n.slice(1))y.writeBuffer(E.buffer,0,new Float32Array(e));const I=new ArrayBuffer(ke.byteLength);ke.write(new DataView(I),{firstConverged:ye,iteration:0}),y.writeBuffer(d,0,I);const S={alpha:t.alpha,beta:t.beta,uniformP:t.uniformP};let P=0;for(;;){const E=Math.min(co,t.maxIterations-P),A=new ne(r,t.label),L=A.pass("iterations"),B=P+E===t.maxIterations;for(let C=0;C<E+(B?1:0);C++){const F=P+C+1,M=s.params(zr,{n:e,groups:c,iteration:F,trackConvergence:1,convergeThreshold:t.tolerance*e}),X=n[(F-1)%n.length],K=n[F%n.length],{core:q,pull:j}=g[(F-1)%g.length],G={rankIn:X,rankPrev:K,outWeightSum:X,xNorm:o,partials:f,P:M.binding};if(m.dispatch(L,m.bind(G),a,[M.offset]),w.dispatch(L,w.bind({partials:f,P:M.binding}),v,[M.offset]),C===E)break;b!==null&&b.dispatch(L,b.bind(G),a,[M.offset]),j.record(L,q,{xNorm:o,rankOut:K,personalization:null,partials:f},S)}A.endPass();const D=A.readback(d,0,ke.byteLength),x=A.readback(n[(P+E)%n.length].buffer,0,i),T=t.alternate===null?null:A.readback(n[(P+E-1)%n.length].buffer,0,i);s.flush();const k=A.submit(),$=await k.readback;if(P+=E,r.assertReady(),t.signal?.aborted===!0)throw uo(t.label,k.id);t.onProgress?.(P,t.maxIterations);const R=ke.read(new DataView($),D.offset).firstConverged,O=R!==ye;if(O||P>=t.maxIterations)return O&&P<t.maxIterations&&t.onProgress?.(t.maxIterations,t.maxIterations),{scores:new Float32Array($,x.offset,e).slice(),previous:T===null?null:new Float32Array($,T.offset,e).slice(),iterations:O?R:P,converged:O,iterationsRun:P}}}finally{s.dispose()}}const Ne="betweennessCentrality",Fd=16,Wd=2654435769,Ud=Math.max(3*ue,Sr)+16;function Wi(r,e,t){const s=4*(r+2);if(s>t.maxStorageBufferBindingSize)throw new h("E_TOO_LARGE",`${Ne}: one source needs ${s} bytes in its largest binding at n = ${r}, above maxStorageBufferBindingSize = ${t.maxStorageBufferBindingSize}; a limit of at least ${s} admits one source per batch`,{needed:s,limit:t.maxStorageBufferBindingSize,path:"binding",algorithm:Ne});const i=Math.floor(t.maxStorageBufferBindingSize/(4*r)),n=Math.floor(ia*t.maxBufferSize/(Fd*r));return Math.max(1,Math.min(i,n,na,e))}function Vd(r,e){const t=Array.from({length:r},(i,n)=>n);let s=Wd;for(let i=0;i<e;i++){s=s+1831565813>>>0;let n=Math.imul(s^s>>>15,s|1);n=n+Math.imul(n^n>>>7,n|61)^n;const o=((n^n>>>14)>>>0)/2**32,a=i+Math.floor(o*(r-i));[t[i],t[a]]=[t[a],t[i]]}return t.slice(0,e)}function Ir(r,e,t){return new h("E_INVALID_ARGUMENT",`${Ne}: ${r} must be ${t}`,{argument:r,value:e,expected:t})}function fo(r,e){const t=r?.k;if(t!==void 0&&(!Number.isInteger(t)||t<0||t>e))throw Ir("k",t,`an integer in [0, ${e}]`);const s=r?.sources;if(s!==void 0){for(const i of s)if(!Number.isInteger(i)||i<0||i>=e)throw Ir("sources",i,`node indices in [0, ${e})`);if(t!==void 0&&t!==s.length)throw Ir("k",t,`absent or equal to sources.length (${s.length})`);return[...s]}return t!==void 0?Vd(e,t):Array.from({length:e},(i,n)=>n)}async function Ui(r,e,t,s,i,n){const o=n,[a,c,l]=await Promise.all(["bc-finalize","bc-forward","bc-backward"].map(g=>r.pipelines.kernel(_(g,{SCALED:o})))),d=s?await r.pipelines.kernel(_("bc-forward-edge",{UNDIRECTED:!e.directed,SCALED:o})):null,f=i?await r.pipelines.kernel(_("bc-edge-gather",{SCALED:o})):null;let p=null;if(n){const g=e.directed?Vs(r,e):t;p={kernel:await r.pipelines.kernel(_("bc-count")),rowPtr:g.rowPtr,colIdx:g.colIdx??g.rowPtr}}return{finalize:a,forward:c,forwardEdge:d,backward:l,edgeGather:f,count:p}}function $t(r,e,t,s,i){const{scope:n,fill:o,ctx:a}=r,c=n.params(Ae,{count:s,value:i,mode:0,pad0:0});o.dispatch(e,o.bind({dst:t,P:c.binding}),z(s,a.workgroupSize,a.caps),[c.offset])}function bt(r,e,t,s,i,n,o){const{scope:a,ctx:c}=r,l=Oe(n,c.workgroupSize,c.caps,o),d=a.params(_t,{...i,stride:l.stride??0});let f=r.bound.get(t);f===void 0&&(f=t.bind({...s,P:d.binding}),r.bound.set(t,f)),t.dispatch(e,f,n===0?z(1,c.workgroupSize,c.caps):l,[d.offset])}async function Br(r,e,t){r.scope.flush();const s=e.submit(),i=await s.readback;if(r.ctx.assertReady(),t?.aborted)throw Je(Ne,s.id);return i}async function Vi(r,e,t,s,i,n,o){const{ctx:a,n:c,S:l,ends:d,levelMax:f,depthK:p,sigmaK:g,deltaK:m,counters:b}=r,w=t.length,v=c*w,y=Uint32Array.from(t,(x,T)=>T*c+x);a.device.queue.writeBuffer(l.buffer,l.offset,y);const I={n:c,k:w,count:s==="edge"?r.edgeCount:0},S=s==="edge"?r.edgeCount:v;let P=0,E=0,A=!1,L=null;for(let x=!0;L===null;x=!1){const T=new ne(a,`${Ne}/forward`),k=T.pass("forward");x&&($t(r,k,p,v,4294967295),$t(r,k,g,v,0),$t(r,k,m,v,0),e.count!==null&&$t(r,k,f,c+2,0),bt(r,k,e.finalize,{counters:b,ends:d,S:l,depthK:p,sigmaK:g,levelMax:f},{n:c,k:w,role:1},1));for(let F=0;F<i;F++)if(bt(r,k,e.finalize,{counters:b,ends:d,S:l,depthK:p,sigmaK:g,levelMax:f},{n:c,k:w,role:0},1),s==="edge"&&e.forwardEdge!==null&&r.edgeSrc!==null&&r.edgeDst!==null?bt(r,k,e.forwardEdge,{edgeSrc:r.edgeSrc,edgeDst:r.edgeDst,S:l,ends:d,counters:b,depthK:p,sigmaK:g},I,S):bt(r,k,e.forward,{rowPtr:r.rowPtr,colIdx:r.colIdx,S:l,ends:d,counters:b,depthK:p,sigmaK:g},I,S),e.count!==null){const{kernel:M,rowPtr:X,colIdx:K}=e.count;bt(r,k,M,{rowPtr:X,colIdx:K,S:l,ends:d,counters:b,depthK:p,sigmaK:g,levelMax:f},{n:c,k:w},v,ca)}P+=i,T.endPass();const $=Math.min(c+2,P+1),N=T.readback(b.buffer,b.offset,Ce.byteLength),R=T.readback(d.buffer,d.offset,4*$),O=await Br(r,T,o),C=new Uint32Array(O,N.offset,Ce.byteLength/4);if(C[st.done]!==0){if(E=C[st.level],A=C[st.sigmaOverflow]!==0,L=new Uint32Array(O,R.offset,$).slice(0,E+1),A&&e.count===null)return{levels:E,wrapped:!0,overflow:!1}}else if(P>c+2)throw new h("E_VALIDATION",`${Ne}: the done flag never rose in ${P} levels`,{label:Ne,message:`the done flag never rose in ${P} levels`})}const B=[];for(let x=E-1;x>=1;x--)B.push(x);let D=null;for(let x=0;;x+=Sr){const T=B.slice(x,x+Sr),k=x+Sr>=B.length,$=new ne(a,`${Ne}/backward`),N=$.pass("backward");for(const F of T){const M=L[F],X=L[F+1]-M;bt(r,N,e.backward,{rowPtr:r.rowPtr,colIdx:r.colIdx,S:l,depthK:p,sigmaK:g,deltaK:m,levelMax:f},{n:c,k:w,start:M,count:X},X)}k&&(bt(r,N,r.gather,{deltaK:m,bc:r.bc},{n:c,k:w},c),e.edgeGather!==null&&r.arcScores!==null&&bt(r,N,e.edgeGather,{rowPtr:r.rowPtr,colIdx:r.colIdx,depthK:p,sigmaK:g,deltaK:m,arcScores:r.arcScores,levelMax:f},{n:c,k:w,count:r.arcCount},r.arcCount)),$.endPass();const R=k&&n.readArrays===!0,O=R?[p,g,m].map(F=>$.readback(F.buffer,F.offset,4*v)):[],C=await Br(r,$,o);if(R&&(D={depthK:new Uint32Array(C,O[0].offset,v).slice(),sigmaK:new Uint32Array(C,O[1].offset,v).slice(),deltaK:new Float32Array(C,O[2].offset,v).slice()}),k)break}return n.onBatch?.({sources:t,forward:s,levels:E,ends:L.slice(),scaled:e.count!==null,sigmaOverflow:A,depthK:D?.depthK??null,sigmaK:D?.sigmaK??null,deltaK:D?.deltaK??null}),{levels:E,wrapped:!1,overflow:A}}async function ho(r,e,t,s,i,n){const o=e.nodeCount,a=i.forward??"auto",c=i.levelsPerSubmit??ue;if(!Number.isInteger(c)||c<1||c>ue)throw Ir("levelsPerSubmit",c,`an integer in [1, ${ue}]`);const l=i.limits??r.caps.limits,d=Wi(o,t.length,l),f=r.residency.core(e);Pt(f,e.arcCount,r.caps.limits.maxStorageBufferBindingSize,Ne);const{edgeCount:p}=e,m=(a==="edge"||a==="auto"&&t.length>d)&&p>0?r.residency.view(e,"edgeList"):null,b=je(r,Ne,Ud);try{const w=4*o*d,v=(R,O)=>$e(b.scratch(R,O),R),y=4*Math.max(1,e.arcCount),[I,S]=await Promise.all(["fill","bc-gather"].map(R=>r.pipelines.kernel(_(R)))),P=await Ui(r,e,f,m!==null,s,!1),E={ctx:r,scope:b,n:o,S:v(w,"S"),ends:v(4*(o+2),"ends"),levelMax:v(4*(o+2),"level-max"),depthK:v(w,"depthK"),sigmaK:v(w,"sigmaK"),deltaK:v(w,"deltaK"),counters:v(Ce.byteLength,"counters"),bc:v(4*o,"bc"),arcScores:s?v(y,"arc-scores"):null,rowPtr:f.rowPtr,colIdx:f.colIdx??f.rowPtr,edgeSrc:m?.bindings.src??null,edgeDst:m?.bindings.dst??null,edgeCount:p,arcCount:e.arcCount,fill:I,gather:S,exact:P,scaled:null,bound:new Map};await r.allocator.check();const A=new ne(r,`${Ne}/setup`),L=A.pass("setup");$t(E,L,E.bc,o,0),E.arcScores!==null&&$t(E,L,E.arcScores,y/4,0),A.endPass(),await Br(E,A,n?.signal);let B=!1,D=0,x=-1;for(let R=0;R<t.length;){const O=Wi(o,t.length-R,l);let C=a==="edge"?"edge":"frontier";a==="auto"&&x>=0&&(C=x<oa*Math.log2(o)?"edge":"frontier"),E.exact.forwardEdge===null&&(C="frontier");const F=t.slice(R,R+O);let M=await Vi(E,E.scaled??E.exact,F,C,c,i,n?.signal);M.wrapped&&(E.scaled??=await Ui(r,e,f,m!==null,s,!0),M=await Vi(E,E.scaled,F,C,c,i,n?.signal)),B=B||M.overflow,x=M.levels,R+=O,D+=1,n?.onProgress?.(R,t.length)}const T=new ne(r,`${Ne}/result`),k=T.readback(E.bc.buffer,E.bc.offset,4*o),$=E.arcScores===null?null:T.readback(E.arcScores.buffer,E.arcScores.offset,4*e.arcCount),N=await Br(E,T,n?.signal);return{vertex:new Float32Array(N,k.offset,o).slice(),perArc:$===null?null:new Float32Array(N,$.offset,e.arcCount).slice(),sourcesUsed:t.length,sigmaOverflow:B,batches:D}}finally{b.dispose()}}function po(r,e){const t=r.nodeCount,s=r.directed?(t-1)*(t-2):(t-1)*(t-2)/2;return e===!0&&s>0?s:1}async function go(r,e){if(r.assertReady(),await Ve(r),e?.endpoints===!0)throw new h("E_UNSUPPORTED",`${Ne}: endpoints: true is not supported`,{feature:"betweenness.endpoints",hint:"the CPU endpoints branch in algorithms/src/algorithms/centrality/betweenness.ts (predecessors.length === 0 && w !== source) never fires, so there is no convention to match"})}async function jd(r,e,t,s){await go(r,t);const i=e.nodeCount,n=Qt(Ne,t?.dest,i)??new Float32Array(i),o=fo(t,i);if(t?.signal?.aborted)throw Je(Ne);if(i===0||o.length===0)return n.fill(0),{scores:n,iterations:0,converged:!0,precision:"f32",sourcesUsed:0,sigmaOverflow:!1};const a=await ho(r,e,o,!1,s,t),c=(e.directed?1:2)*po(e,t?.normalized);for(let l=0;l<i;l++)n[l]=a.vertex[l]/c;return{scores:n,iterations:a.batches,converged:!0,precision:"f32",sourcesUsed:a.sourcesUsed,sigmaOverflow:a.sigmaOverflow}}async function qd(r,e,t,s,i){await go(r,t);const n=e.nodeCount,o=Qt("edgeBetweennessCentrality",t?.dest,e.edgeCount)??new Float32Array(e.edgeCount),a=fo(t,n);if(t?.signal?.aborted)throw Je(Ne);if(n===0||a.length===0||e.arcCount===0)return o.fill(0),{scores:o,precision:"f32",sourcesUsed:a.length,sigmaOverflow:!1};const c=await ho(r,e,a,!0,s,t),l=c.perArc??new Float32Array(e.arcCount),d=No(e,l,"sum"),f=(e.directed?1:2)*po(e,t?.normalized);for(let p=0;p<e.edgeCount;p++)o[p]=d[p]/f;return{scores:o,precision:"f32",sourcesUsed:c.sourcesUsed,sigmaOverflow:c.sigmaOverflow}}function Kd(r,e,t){return jd(r,e,t,{})}function Hd(r,e,t){return qd(r,e,t,{})}const fe="closenessCentrality",rr=8,Yd=64,Xd=4096,ji=12,Qd=1024;function Ht(r){return r===0?0:1/r}async function Zd(r,e,t,s,i,n){const o=e.nodeCount,a=s?.length??o,c=s===null?null:new Float64Array(o);for(let l=0;l<a;l++){if(n?.signal?.aborted)throw Je(fe);const d=s===null?l:s[l],{dist:f}=await no(r,e,d,{signal:n?.signal});let p=0;for(let g=0;g<o;g++){const m=f[g];g!==d&&m!==1/0&&(c!==null?c[g]+=m:i?m>0&&(p+=1/m):p+=m)}c===null&&(t[d]=i?p:Ht(p)),n?.onProgress?.(l+1,a)}return c?.forEach((l,d)=>{t[d]=Ht(l)}),{scores:t,iterations:a,converged:!0,precision:"f32",sourcesUsed:a}}async function Jd(r,e,t,s,i,n){const o=e.nodeCount,a=je(r,fe,Math.min(Bt,Math.ceil(o/32))+3);try{const c=await ao(r,e,a,s,Bt,{signal:n?.signal},fe),l=await r.pipelines.kernel(_("closeness-rowsum")),d=$e(a.scratch(4*o,"row-sums"),4*o),f=i?2:Number(s),p=a.params(Wt,{role:f,n:o}),g=new ne(r,`${fe}/row-sums`);l.dispatch(g.pass("row-sums"),l.bind({dist:c,out:d,P:p.binding}),Ft(o,r.caps),[p.offset]),g.endPass();const m=g.readback(d.buffer,d.offset,4*o);a.flush();const b=await g.submit().readback;return r.assertReady(),f===0?new Uint32Array(b,m.offset,o).forEach((w,v)=>{t[v]=Ht(w)}):new Float32Array(b,m.offset,o).forEach((w,v)=>{t[v]=i?w:Ht(w)}),n?.onProgress?.(o,o),{scores:t,iterations:1,converged:!0,precision:"f32",sourcesUsed:o}}finally{a.dispose()}}async function ef(r,e,t,s,i,n){const{sources:o,harmonic:a,levelsPerSubmit:c}=s,l=e.nodeCount,d=o?.length??l;if(d===0)return{scores:t,iterations:0,converged:!0,precision:"f32",sourcesUsed:0};const f=Math.min(r.caps.limits.maxStorageBufferBindingSize,r.caps.limits.maxBufferSize),p=r.residency.core(e);if(Pt(p,e.arcCount,r.caps.limits.maxStorageBufferBindingSize,fe),e.directed&&4*e.arcCount>f)throw new h("E_TOO_LARGE",`${fe}: the reverse adjacency of a directed snapshot (${4*e.arcCount} bytes) does not fit one binding`,{needed:4*e.arcCount,limit:f,path:"closeness.reverse",algorithm:fe});const g=e.directed?Ur(r.residency.view(e,"reverse"),e.arcCount):p,m=o===null?0:l,b=Math.floor((f/4-m)/(4*l)),w=o===null?rr:Math.floor(4294967295/(32*Math.max(1,l-1))),v=n.words??Math.max(1,Math.min(rr,Math.ceil(d/32),b,w));if(!Number.isInteger(v)||v<1||v>rr)throw new h("E_INVALID_ARGUMENT",`${fe}: words must be an integer in [1, ${rr}]`,{argument:"words",value:v,expected:`an integer in [1, ${rr}]`});const y=l*v,I=4*y+m;if(4*I>f)throw new h("E_TOO_LARGE",`${fe}: ${l} nodes need a ${4*I}-byte search state in one binding of at most ${f} bytes`,{needed:4*I,limit:f,path:"closeness.bits",algorithm:fe});const S=32*v,P=c*S,E=P,A=o===null?0:E+ji,L=E+ji+(o?.length??0),B=n.pullAt??Math.min(4294967295,Math.floor(v*e.arcCount/Yd)),D=je(r,fe,c+4);try{const x=r.workgroupSize,T=$e(D.scratch(4*I,"bits"),4*I),k=$e(D.scratch(4*L,"table"),4*L),$={...k,size:4*P};o!==null&&r.device.queue.writeBuffer(k.buffer,k.offset+4*A,Uint32Array.from(o)),await r.allocator.check();const N=await r.pipelines.kernel(_("closeness-level")),R=await r.pipelines.kernel(_("fill")),O={rowPtr:p.rowPtr,colIdx:p.colIdx??p.rowPtr,inRowPtr:g.rowPtr,inColIdx:g.colIdx??g.rowPtr,bits:T,table:k},C=z(Math.max(l,e.arcCount),x,r.caps),F=e.inDegree().every(q=>q<=Xd)?1:0,M=o===null?null:new Float64Array(l),X={n:l,words:v,base:y,ctrl:E,pullAt:B,perNode:o===null?0:1,sourcesAt:A,arcCount:e.arcCount,pullOk:F};let K=0;for(let q=0;q<d;q+=S){const j=Math.min(S,d-q),G=new Float64Array(j),W=new Float64Array(j),H=new Uint32Array(j);let Q=!1,V=0;for(;!Q;){if(V>l+1)throw new h("E_VALIDATION",`${fe}: the batch at ${q} still claimed at level ${V}`,{label:fe,message:`the batch still claimed at level ${V}`});const U=new ne(r,`${fe}/levels`),oe=U.pass("closeness");if(V===0){const Y=D.params(Wt,{...X,role:1,total:I,count:j}),Z=N.bind({...O,P:Y.binding});N.dispatch(oe,Z,z(I,x,r.caps),[Y.offset]);const we=D.params(Wt,{...X,role:2,count:j,source:q});N.dispatch(oe,Z,z(j,x,r.caps),[we.offset])}const te=D.params(Ae,{count:P,value:0,mode:0,pad0:0});R.dispatch(oe,R.bind({dst:$,P:te.binding}),z(P,x,r.caps),[te.offset]);let de=null;for(let Y=0;Y<c;Y++){const Z=D.params(Wt,{...X,role:0,count:j,level:V+Y,row:Y});de??=N.bind({...O,P:Z.binding}),N.dispatch(oe,de,C,[Z.offset])}U.endPass();const pe=U.readback(k.buffer,k.offset,4*P),ge=M===null?null:U.readback(T.buffer,T.offset+16*y,4*l);D.flush();const ce=U.submit(),xe=await ce.readback;if(r.assertReady(),i?.signal?.aborted)throw Je(fe,ce.id);const ae=new Uint32Array(xe,pe.offset,P);for(let Y=0;Y<c&&!Q;Y++){const Z=V+Y+1;let we=0;for(let me=0;me<j;me++){const Le=ae[Y*S+me];G[me]+=Le*Z,W[me]+=Le/Z,H[me]+=Le,we+=Le}Q=we===0}Q&&M!==null&&ge!==null&&new Uint32Array(xe,ge.offset,l).forEach((Y,Z)=>{M[Z]+=Y}),V+=c}if(o===null){for(let U=0;U<j;U++)t[q+U]=a?W[U]:Ht(G[U]);n.onBatch?.(q,G,H)}K+=1,i?.onProgress?.(q+j,d)}return M?.forEach((q,j)=>{t[j]=Ht(q)}),{scores:t,iterations:K,converged:!0,precision:"f32",sourcesUsed:d}}finally{D.dispose()}}function tf(r,e){if(e===void 0)return null;if(r.directed)throw new h("E_UNSUPPORTED",`${fe}: sampled sources need an undirected snapshot`,{feature:"closenessCentrality.directedSources",hint:"run the CPU port, which searches the in-arcs"});for(const t of e)if(!Number.isInteger(t)||t<0||t>=r.nodeCount)throw new h("E_INVALID_ARGUMENT",`${fe}: a source is not a node index`,{argument:"sources",value:t,expected:`an integer in [0, ${r.nodeCount})`});return e}async function rf(r,e,t,s){r.assertReady(),await Ve(r);for(const p of["maxIterations","tolerance"])if(t?.[p]!==void 0)throw new h("E_UNSUPPORTED",`${fe}: ${p} has no meaning for an exact traversal`,{option:p,hint:"closeness is an exact traversal; the option has no meaning here"});const i=e.nodeCount,n=tf(e,t?.sources),o=t?.harmonic===!0;if(o&&n!==null)throw new h("E_UNSUPPORTED",`${fe}: harmonic closeness from sampled sources`,{feature:"closenessCentrality.sampledHarmonic",hint:"run the CPU port, or drop the sources for the exact harmonic score"});const a=s.levelsPerSubmit??ue;if(!Number.isInteger(a)||a<1||a>ue)throw new h("E_INVALID_ARGUMENT",`${fe}: levelsPerSubmit must be an integer in [1, ${ue}]`,{argument:"levelsPerSubmit",value:a,expected:`an integer in [1, ${ue}]`});const c=Qt(fe,t?.dest,i)??new Float32Array(i),l=(t?.weighted??e.flags.weighted)&&e.weights!==null&&!e.flags.allWeightsOne;if(t?.signal?.aborted)throw Je(fe);if(l&&!e.flags.nonNegativeWeights)throw new h("E_UNSUPPORTED",`${fe}: a negative weight has no shortest-path distance to sum`,{feature:"closenessCentrality.negativeWeights",hint:"pass weighted: false to ignore the column"});if(l&&!e.flags.finiteWeights)throw new h("E_UNSUPPORTED",`${fe}: a NaN or infinite weight has no shortest path`,{feature:"closenessCentrality.nonFiniteWeights"});const d=i>0&&i<=oo(r.caps.limits).maxNodes;let f;return n!==null||!d?f=l?"per-source":"levels":f=s.route??(l||i<=Qd?"all-pairs":"levels"),f==="levels"&&l&&(f="per-source"),s.onRoute?.(f),f==="all-pairs"?Jd(r,e,c,l,o,t):f==="per-source"?Zd(r,e,c,n,o,t):ef(r,e,c,{sources:n,harmonic:o,levelsPerSubmit:a},t,s)}function sf(r,e,t){return rf(r,e,t,{})}const We="connectedComponents",As=4,ls=64,nf=1024,of=1024,af=2*As;function cf(r,e){if(r===void 0)return null;if(r instanceof Uint32Array&&r.length===e&&r.buffer instanceof ArrayBuffer)return r;throw new h("E_INVALID_ARGUMENT",`${We}: dest must be a Uint32Array of length ${e} over an ArrayBuffer`,{argument:"dest",value:`${r.constructor.name}(${r.length})`,expected:`Uint32Array(${e}) over an ArrayBuffer`})}function lf(r,e){const t=r.residency.core(e);return Pt(t,e.arcCount,r.caps.limits.maxStorageBufferBindingSize,We),t}function us(r,e){return{buffer:r,offset:0,size:e,window:null}}function Ut(r,e){let t=null;return{labels:r,count:e,groups(){if(t!==null)return t;const s=r.length,i=new Uint32Array(s).fill(ye),n=new Uint32Array(e);let o=0;for(let l=0;l<s;l++){let d=i[r[l]];d===ye&&(d=o++,i[r[l]]=d),n[d]++}const a=Array.from(n,l=>new Uint32Array(l)),c=new Uint32Array(e);for(let l=0;l<s;l++){const d=i[r[l]];a[d][c[d]++]=l}return t=a,a}}}function uf(r){const e=new Map;let t=r[0],s=0;for(const i of r){const n=(e.get(i)??0)+1;e.set(i,n),n>s&&(t=i,s=n)}return t}function df(r){const e=r.length,t=new Uint8Array(e);let s=0;for(let i=0;i<e;i++){const n=r[i];if(n>=e)throw new h("E_VALIDATION",`${We}: labels[${i}] = ${n} is not a node index`,{label:`${We}/labels`,message:`the device produced a label outside [0, ${e})`});t[n]===0&&(t[n]=1,s++)}return s}function ff(r,e,t){const s=e??new Uint32Array(r);for(let i=0;i<r;i++)s[i]=i;return t?.onProgress?.(1,1),Ut(s,r)}function hf(r,e,t,s,i){if(i?.aborted)throw new h("E_ABORTED",`${We}: the signal was aborted`,{batchId:s});if(new Uint32Array(r,e,1)[0]===0)return!0;if(t>=ls)throw new h("E_VALIDATION",`${We}: the changed flag never settled in ${ls} rounds`,{label:We,message:`the changed flag never settled in ${ls} rounds`});return!1}async function qi(r,e,t){r.assertReady(),await Ve(r);const s=e.nodeCount,i=t?.renumber!==!1,n=cf(t?.dest,s);if(t?.signal?.aborted)throw new h("E_ABORTED",`${We}: the signal was aborted before any work started`,{});if(s===0)return t?.onProgress?.(1,1),Ut(n??new Uint32Array(0),0);const o=lf(r,e);if(e.arcCount===0)return ff(s,n,t);const a=r.residency.view(e,"edgeList"),c=a.scalars.edgeCount[0],l=je(r,We,af);try{const d=4*(s+1),f=l.scratch(d,"comp"),p=Math.min(nf,s),g=l.scratch(4*p,"hist");await r.allocator.check();const m=await r.pipelines.kernel(_("fill",{})),b=await r.pipelines.kernel(_("wcc-link-sample",Ge(o,null))),w=await r.pipelines.kernel(_("wcc-link-edges",{})),v=await r.pipelines.kernel(_("wcc-compress",{})),y=await r.pipelines.kernel(_("wcc-sample",{})),{queue:I}=r.device,S=us(f,d),P=us(g,4*p),E=Oe(s,r.workgroupSize,r.caps),A=Oe(c,r.workgroupSize,r.caps),L=s,B=new Uint32Array(1),D=V=>l.params(Xt,{n:s,...V,flagIndex:L,maxSteps:of,pad0:0}),x=V=>{const U=D({items:s,stride:E.stride??s,r:0,giant:ye});v.dispatch(V,v.bind({comp:S,P:U.binding}),E,[U.offset])},T=V=>(l.flush(),V.submit());I.writeBuffer(f,4*L,B);const k=new ne(r,`${We}/setup`);let $=k.pass("sample-rounds");const N=l.params(Ae,{count:s,value:0,mode:1,pad0:0});m.dispatch($,m.bind({dst:us(f,4*s),P:N.binding}),z(s,r.workgroupSize,r.caps),[N.offset]);const R=Ze(o,null);for(let V=0;V<2;V++){const U=D({items:s,stride:E.stride??s,r:V,giant:ye}),oe=b.bind({...R,comp:S,P:U.binding});b.dispatch($,oe,E,[U.offset])}x($),k.endPass(),await T(k).readback,r.assertReady();const O=new ne(r,`${We}/sample`);$=O.pass("sample");const C=D({items:p,stride:0,r:0,giant:ye});y.dispatch($,y.bind({comp:S,hist:P,P:C.binding}),z(p,r.workgroupSize,r.caps),[C.offset]),O.endPass();const F=O.readback(g,0,4*p),M=await T(O).readback;r.assertReady();const X=uf(new Uint32Array(M,F.offset,p)),K={edgeSrc:a.bindings.src,edgeDst:a.bindings.dst,comp:S};let q=0;for(;;){I.writeBuffer(f,4*L,B);const V=new ne(r,`${We}/rounds`);$=V.pass("edge-rounds");for(let de=0;de<As;de++){const pe=D({items:c,stride:A.stride??c,r:0,giant:X}),ge=w.bind({...K,P:pe.binding});w.dispatch($,ge,A,[pe.offset]),x($)}V.endPass();const U=V.readback(f,4*L,4),oe=T(V),te=await oe.readback;if(q+=As,r.assertReady(),hf(te,U.offset,q,oe.id,t?.signal))break}const j=new ne(r,`${We}/final`);x(j.pass("compress")),j.endPass(),await T(j).readback,r.assertReady();const G=!i&&n!==null?n:new Uint32Array(s);await r.readback.read(f,4*s,G),r.assertReady();const W=df(G);if(t?.onProgress?.(1,1),!i)return Ut(G,W);const{labels:H,count:Q}=_n(G,n??void 0);return Ut(H,Q)}finally{l.dispose()}}function pf(r,e=da){if(!Number.isInteger(e)||e<0||e>Jr)throw new h("E_INVALID_ARGUMENT",`planGroupRows: threadMax must be an integer in [0, ${Jr}]`,{argument:"threadMax",value:e,expected:`[0, ${Jr}]`});const t=r.length,s=[],i=[];for(let a=0;a<t;a++)(r[a]<=e?s:i).push(a);const n=new Uint32Array(s.length+2*i.length);n.set(s,0),n.set(i,s.length);let o=1;for(let a=0;a<i.length;a++)n[s.length+i.length+a]=o,o+=2*Ln*r[i[a]];return{words:n,threadCount:s.length,hashCount:i.length,regionWords:o}}async function gf(r){const e=new Map;for(const t of[0,1])for(const s of[!1,!0]){const i=_("group-by-key-row",{TIER:t,WEIGHTED:s});e.set(`${t}/${s}`,await r.pipelines.kernel(i))}return new mf(r,e)}function ds(r,e,t){if(e.size<4*t)throw new h("E_INVALID_ARGUMENT",`groupByKeyRow: ${r} is smaller than 4 x ${t} bytes`,{argument:r,value:e.size,expected:4*t})}class mf{constructor(e,t){this.scope=e,this.kernels=t}record(e,t){const{threadCount:s,hashCount:i}=t.plan,n=s+i;if(ds("rows",t.rows,n+i),ds("hashRegion",t.hashRegion,t.plan.regionWords),ds("rowPtr",t.rowPtr,n+1),n===0)return;const o=t.weights!==null,a=[[0,0,s],[1,s,i]];for(const[c,l,d]of a){if(d===0)continue;const f=this.kernels.get(`${c}/${o}`);if(f===void 0)throw new h("E_INVALID_ARGUMENT","groupByKeyRow: no variant was prepared",{argument:"tier",value:c});const p=this.scope.params(jn,{rowsBase:l,basesBase:s+i,count:d,pad0:0}),g=f.bind({rowPtr:t.rowPtr,colIdx:t.colIdx,weights:t.weights??t.colIdx,keyIn:t.keyIn,rows:t.rows,hashRegion:t.hashRegion,bestKey:t.bestKey,bestScore:t.bestScore,P:p.binding}),m=c===0?z(d,this.scope.workgroupSize,this.scope.caps):Ft(d,this.scope.caps);f.dispatch(e,g,m,[p.offset])}}}const Ki=4294967295;async function mo(r){return wo(r)}async function wo(r){const e=await r.pipelines.kernel(_("histogram")),t=await r.pipelines.kernel(_("fill"));return new bf(r,e,t)}async function wf(r){const e=await wo(r),t=await Rt(r),s=await r.pipelines.kernel(_("counting-scatter"));return new vf(r,e,t,s)}function cr(r,e,t){if(e.size<4*t)throw new h("E_INVALID_ARGUMENT",`histogram: ${r} is smaller than 4 x ${t} bytes`,{argument:r,value:e.size,expected:4*t})}function bo(r,e,t,s){if(!Number.isSafeInteger(e)||e<0||e>Ki)throw new h("E_INVALID_ARGUMENT","histogram: count must be a non-negative integer below 2^32",{argument:"count",value:e});if(!Number.isSafeInteger(t)||t<1||t>Ki)throw new h("E_INVALID_ARGUMENT","histogram: bins must be an integer in [1, 2^32)",{argument:"bins",value:t});cr("keys",r,e),cr("hist",s,t)}class bf{constructor(e,t,s){this.dispatches=0,this.scope=e,this.histogram=t,this.fill=s}get lastDispatches(){return this.dispatches}record(e,t,s,i,n){if(bo(t,s,i,n),this.recordZero(e,n,i),this.dispatches=1,s===0)return;const o=this.scope.params(Fr,{count:s,bins:i,pad0:0,pad1:0}),a=this.histogram.bind({keys:t,hist:n,P:o.binding});this.histogram.dispatch(e,a,z(s,this.scope.workgroupSize,this.scope.caps),[o.offset]),this.dispatches=2}recordZero(e,t,s){const i=this.scope.params(Ae,{count:s,value:0,mode:0,pad0:0}),n=this.fill.bind({dst:t,P:i.binding});this.fill.dispatch(e,n,z(s,this.scope.workgroupSize,this.scope.caps),[i.offset])}}class vf{constructor(e,t,s,i){this.dispatches=0,this.scope=e,this.histogram=t,this.scan=s,this.scatter=i}get lastDispatches(){return this.dispatches}record(e,t,s,i,n,o,a){if(bo(t,s,i,n.hist),cr("cursor",n.cursor,i),cr("outIndex",o,s),cr("outStart",a,i),this.histogram.record(e,t,s,i,n.hist),this.scan.record(e,n.hist,i,a),this.histogram.recordZero(e,n.cursor,i),this.dispatches=this.histogram.lastDispatches+this.scan.lastDispatches+1,s===0)return;const c=this.scope.params(Fr,{count:s,bins:i,pad0:0,pad1:0}),l=this.scatter.bind({keys:t,start:a,cursor:n.cursor,outIndex:o,P:c.binding});this.scatter.dispatch(e,l,z(s,this.scope.workgroupSize,this.scope.caps),[c.offset]),this.dispatches+=1}}async function vo(r){const e=await mo(r),t=await Rt(r),s=await r.pipelines.kernel(_("fill")),i=new Map;for(const n of[!1,!0])for(const o of[!1,!0]){const a=_("coo-scatter",{SORTED_INPUT:n,WEIGHTED:o});i.set(`${n}/${o}`,await r.pipelines.kernel(a))}return new _f(r,e,t,s,i)}function Et(r,e,t){if(e.size<4*t)throw new h("E_INVALID_ARGUMENT",`cooToCsr: ${r} is smaller than 4 x ${t} bytes`,{argument:r,value:e.size,expected:4*t})}function yf(r){for(const[e,t]of[["count",r.count],["n",r.n]])if(!Number.isSafeInteger(t)||t<0||t>=ye)throw new h("E_INVALID_ARGUMENT",`cooToCsr: ${e} must be an integer in [0, 2^32 - 1)`,{argument:e,value:t});if(r.weights===null!=(r.out.weights===null))throw new h("E_INVALID_ARGUMENT","cooToCsr: weights and out.weights must be both present or both null",{argument:"weights",value:r.weights===null?"null":"present",expected:r.out.weights===null?"null":"present"});Et("src",r.src,r.count),Et("dst",r.dst,r.count),Et("out.rowPtr",r.out.rowPtr,r.n+1),Et("out.colIdx",r.out.colIdx,r.count),Et("out.flag",r.out.flag,1),r.weights!==null&&r.out.weights!==null&&(Et("weights",r.weights,r.count),Et("out.weights",r.out.weights,r.count))}class _f{constructor(e,t,s,i,n){this.scope=e,this.histogram=t,this.scan=s,this.fill=i,this.scatter=n}record(e,t){yf(t);const{scope:s}=this,i=s.workgroupSize,n=4*(t.n+1),o={buffer:s.scratch(n,"cooToCsr/degrees"),offset:0,size:n,window:null};this.histogram.record(e,t.src,t.count,t.n+1,o),this.scan.record(e,o,t.n+1,t.out.rowPtr);let a=t.out.flag;if(t.sortedInput)this.zero(e,t.out.flag,1);else if(t.n>0){const g=4*t.n;a={buffer:s.scratch(g,"cooToCsr/cursors"),offset:0,size:g,window:null},this.zero(e,a,t.n)}if(t.count===0)return;const c=t.weights!==null,l=this.scatter.get(`${t.sortedInput}/${c}`);if(l===void 0)throw new h("E_INVALID_ARGUMENT","cooToCsr: no scatter variant was prepared",{argument:"sortedInput",value:t.sortedInput});const d=t.out.weights??{buffer:s.scratch(4,"cooToCsr/weight-dummy"),offset:0,size:4,window:null},f=s.params(nt,{count:t.count,pad0:0,pad1:0,pad2:0}),p=l.bind({src:t.src,dst:t.dst,weight:t.weights??t.dst,rowPtr:t.out.rowPtr,cursors:a,colIdx:t.out.colIdx,outWeight:d,P:f.binding});l.dispatch(e,p,z(t.count,i,s.caps),[f.offset])}zero(e,t,s){const i=this.scope.params(Ae,{count:s,value:0,mode:0,pad0:0}),n=this.fill.bind({dst:t,P:i.binding});this.fill.dispatch(e,n,z(s,this.scope.workgroupSize,this.scope.caps),[i.offset])}}const Hi=2**128-2**104;function Pf(r){let e=0;r==="min"?e=Hi:r==="max"&&(e=-Hi);const t=new DataView(new ArrayBuffer(4));return t.setFloat32(0,e,!0),t.getUint32(0,!0)}const Sf=new Set(["row","arc","nbr","weight","v"]),Ef=new Set(["if","else","let","var","const","true","false","f32","u32","i32","bool","abs","ceil","clamp","exp","exp2","floor","fract","inverseSqrt","log","log2","max","min","mix","pow","round","select","sign","sqrt","step","trunc"]);function If(r){switch(r){case"sum":return 0;case"min":return 1;case"max":return 2;default:throw new h("E_INVALID_ARGUMENT",`segmentedReduce: unknown op ${String(r)}`,{argument:"op",value:r,expected:"sum | min | max"})}}function kf(r){return r.replace(/\/\*[\s\S]*?\*\//g," ").replace(/\/\/[^\n]*/g," ")}function Af(r){const e=kf(r);if(!/\bv\s*[-+*/]?=(?!=)/.test(e))throw new h("E_SHADER_COMPILE","segmentedReduce: the VALUE snippet never assigns v",{id:"segmented-reduce",stage:"compose",slot:"VALUE"});const t=e.match(/[A-Za-z_]\w*|\d[0-9A-Za-z_.]*/g)??[];for(const s of t)if(!(/^\d/.test(s)||Sf.has(s)||Ef.has(s)))throw new h("E_SHADER_COMPILE",`segmentedReduce: the VALUE snippet names "${s}"; the only identifiers allowed are row, arc, nbr, weight and v`,{id:"segmented-reduce",stage:"compose",slot:"VALUE",identifier:s})}function xf(r,e,t,s,i,n){return s!==null&&n?{first:s.rowFirst,last:s.rowLast+1,arcEnd:s.end}:{first:r.start,last:r.tier===0?t:r.end,arcEnd:s===null?Wr(e):Math.min(s.end,i)}}function Rf(r,e){return r===2?1:r===1?e/lt:e}class Lf{constructor(e,t,s,i,n,o,a){this.dispatches=0,this.scope=e,this.tiers=t,this.fill=s,this.perm=i,this.hasWeights=n,this.accumulate=o,this.identityWord=a}get lastDispatches(){return this.dispatches}record(e,t,s){if(t.weights!==null!==this.hasWeights)throw new h("E_INVALID_ARGUMENT","segmentedReduce: the core's weights pattern differs from the one prepared",{argument:"core",value:t.weights!==null,expected:this.hasWeights});const i=fr(t,"segmentedReduce");if(s.size<4*i)throw new h("E_INVALID_ARGUMENT",`segmentedReduce: out holds ${s.size} bytes, ${4*i} needed`,{argument:"out",value:s.size,expected:`>= ${4*i}`});if(this.dispatches=0,i===0)return;if(t.windows===null){this.recordWindow(e,t,s,i,null,0);return}if(!this.accumulate){const o=this.scope.params(Ae,{count:i,value:this.identityWord,mode:0}),a=this.fill.bind({dst:s,P:o.binding});this.fill.dispatch(e,a,z(i,this.scope.workgroupSize,this.scope.caps),[o.offset]),this.dispatches++}const{windows:n}=t;n.forEach((o,a)=>{const c={...t,colIdx:Tr(t,"colIdx",o),weights:t.weights===null?null:Tr(t,"weights",o)};this.recordWindow(e,c,s,i,o,a+1<n.length?n[a+1].start:o.end)})}recordWindow(e,t,s,i,n,o){const a=this.scope.workgroupSize;for(const c of this.tiers){const{tier:l,kernel:d}=c,{first:f,last:p,arcEnd:g}=xf(c,t,i,n,o,this.perm===null),m=p-f;if(m<=0)continue;const b=this.scope.params(zs,{start:f,end:p,arcBase:n===null?0:n.start,arcEnd:g,accumulate:n!==null||this.accumulate?1:0,n:i}),w=d.bind({...Ze(t,this.perm),out:s,P:b.binding});d.dispatch(e,w,z(m,Rf(l,a),this.scope.caps),[b.offset]),this.dispatches++}}}async function yo(r,e,t){const s=If(t.op);Af(t.valueSnippet);const{tiers:i}=t,n=i?.perm??null;if(i!==null&&r.workgroupSize<lt)throw new h("E_UNSUPPORTED",`segmentedReduce: the mid tier folds ${lt} lanes per row, more than the workgroup size ${r.workgroupSize}`,{feature:"segmentedReduce.tiers"});const o=i===null?[{tier:0,start:0,end:fr(e,"segmentedReduce")}]:[{tier:2,start:0,end:i.segmentOffsets[1]},{tier:1,start:i.segmentOffsets[1],end:i.segmentOffsets[2]},{tier:0,start:i.segmentOffsets[2],end:i.segmentOffsets[4]}],a=[];for(const l of o){if(l.tier!==0&&l.end<=l.start)continue;const d={...Ge(e,n),OP:s,TIER:l.tier},f=_("segmented-reduce",d,{VALUE:t.valueSnippet});a.push({...l,kernel:await r.pipelines.kernel(f)})}const c=await r.pipelines.kernel(_("fill"));return new Lf(r,a,c,n,e.weights!==null,t.accumulate===!0,Pf(t.op))}function Df(r){for(const e of[8,16,24])if(r<2**e)return e;return 32}function be(r,e,t){const s=4*Math.max(1,e);return{buffer:r.scratch(s,t),offset:0,size:s,window:null}}function _o(r,e,t,s){const i=r.caps.limits.maxStorageBufferBindingSize;if(e>i)throw new h("E_TOO_LARGE",`${s}: ${t} needs ${e} bytes in one binding, above the device limit of ${i}`,{needed:e,limit:i,path:t,algorithm:s})}function Tf(r,e,t,s){const i=new Uint32Array(r);for(let a=0;a<e.length;a++)e[a]!==t[a]&&(i[e[a]]++,i[t[a]]++);const n=new Map;for(let a=0;a<r;a++)i[a]>es&&n.set(a,n.size);if(n.size<2)return;const o=new Uint32Array(n.size*n.size);for(let a=0;a<e.length;a++){const c=n.get(e[a]),l=n.get(t[a]);if(c===void 0||l===void 0||c===l)continue;const d=Math.min(c,l)*n.size+Math.max(c,l);if(++o[d]>es)throw new h("E_UNSUPPORTED",`${s}: nodes ${e[a]} and ${t[a]} are joined by more than ${es} parallel edges, more than one weighted merge sums`,{feature:`${s}.parallelEdges`,hint:"run it unweighted, or merge the parallel edges first"})}}function xs(r,e,t){if(new Uint32Array(r,e.flag.offset,1)[0]!==0)throw new h("E_VALIDATION",`${t}: the simple graph's arcs reached cooToCsr out of order`,{label:`${t}/simple-graph`,message:"the sorted-input precondition of cooToCsr failed on the device"})}async function Po(r,e,t,s,i){const n=e.nodeCount,o=r.workgroupSize,a=await vo(t),c=be(t,1,"simple/flag"),l=be(t,n+1,"simple/rowPtr"),{edgeCount:d}=e,f=e.edgeList();let p=0;for(let J=0;J<d;J++)f.src[J]===f.dst[J]&&p++;const g=2*(d-p),m=()=>{const J=new ne(r,`${i}/simple-graph`);return a.record(J.pass("simple-graph"),{src:c,dst:c,weights:null,count:0,n,sortedInput:!0,out:{rowPtr:l,colIdx:c,weights:null,flag:c}}),J.endPass(),{graph:{n,arcCount:0,rowPtr:l,colIdx:null,weights:null,src:null},batch:J,flag:J.readback(c.buffer,0,4)}};if(g===0)return m();const b=2*d,w=Math.max(4,jr(b,o));_o(r,Math.max(4*(b+1),w),"the simple graph's arc arrays",i),s&&Tf(n,f.src,f.dst,i),r.residency.core(e,["rowPtr"]);const v=r.residency.view(e,"edgeList"),y=v.bindings.weights??null,I=z(b,o,r.caps),S=new Map;for(const J of[!1,!0]){const De=_("coo-emit",{INDEXED:J,WEIGHTED:y!==null});S.set(J,await r.pipelines.kernel(De))}const P=await r.pipelines.kernel(_("fill")),E=await r.pipelines.kernel(_("run-flags")),A=await Us(t),L=await Rt(t),B=await Vr(t),D=be(t,b,"simple/aSrc"),x=be(t,b,"simple/aDst"),T=be(t,b,"simple/aW"),k=be(t,b,"simple/vals"),$=be(t,b,"simple/sortKeys"),N=be(t,b,"simple/sortVals"),R={buffer:t.scratch(w,"simple/hist"),offset:0,size:w,window:null},O={buffer:t.scratch(w,"simple/offsets"),offset:0,size:w,window:null},C=be(t,b,"simple/sortedSrc"),F=be(t,b,"simple/sortedDst"),M=be(t,b,"simple/sortedW"),X=be(t,b,"simple/flags"),K=be(t,b,"simple/runIndex"),q=be(t,1,"simple/dummy");await r.allocator.check();const j=(J,De,ct)=>{const ht=S.get(De!==null);if(ht===void 0)throw new h("E_INVALID_ARGUMENT","buildSimpleSymmetric: no coo-emit variant",{argument:"order",value:De===null?"null":"present"});const pr=t.params(nt,{count:b,pad0:0,pad1:0,pad2:0}),Hr=ht.bind({edgeSrc:v.bindings.src,edgeDst:v.bindings.dst,edgeWeight:y??v.bindings.src,order:De??q,outSrc:ct[0],outDst:ct[1],outWeight:ct[2],P:pr.binding});ht.dispatch(J,Hr,I,[pr.offset])},G=(J,De,ct)=>{const ht=t.params(Ae,{count:ct,value:0,mode:1,pad0:0});P.dispatch(J,P.bind({dst:De,P:ht.binding}),z(ct,o,r.caps),[ht.offset])},W=Df(n),H=new ne(r,`${i}/simple-sort`),Q=H.pass("simple-sort");j(Q,null,[D,x,T]),G(Q,k,b);const V=A.record(Q,x,k,b,W,{keys:$,vals:N,hist:R,offsets:O});j(Q,V.vals,[D,x,T]);const U=V.vals.buffer===k.buffer?N:k,oe=A.record(Q,D,V.vals,b,W,{keys:V.keys.buffer===x.buffer?$:x,vals:U,hist:R,offsets:O});j(Q,oe.vals,[C,F,M]);const te=t.params(nt,{count:b,pad0:0,pad1:0,pad2:0});E.dispatch(Q,E.bind({keysA:C,keysB:F,flags:X,P:te.binding}),I,[te.offset]);const de=L.record(Q,X,b,K);H.endPass();const pe=H.readback(de.binding.buffer,de.binding.offset+4*de.index,4);t.flush();const ge=await H.submit().readback;r.assertReady();const ce=new Uint32Array(ge,pe.offset,1)[0];if(ce===0||ce>g)throw new h("E_VALIDATION",`${i}: ${ce} distinct arcs from ${g} valid ones`,{label:`${i}/simple-graph`,message:"the run count of the simple graph build is outside [1, valid arcs]"});const xe=be(t,b,"simple/src"),ae=be(t,b,"simple/dst"),Y=be(t,b+1,"simple/runStart"),Z=be(t,1,"simple/count"),we=be(t,ce,"simple/colIdx"),me=s?be(t,ce,"simple/weights"):null,Le=s?be(t,ce,"simple/merged"):null;r.device.queue.writeBuffer(Y.buffer,4*ce,Uint32Array.of(g));const ot=Le===null?null:await yo(t,Yi(Y,ce,F,M,g),{op:"sum",valueSnippet:"v = weight;",tiers:null}),qe=new ne(r,`${i}/simple-graph`),at=qe.pass("simple-graph"),rt=k;G(at,rt,b);for(const[J,De]of[[C,xe],[F,ae],[rt,Y]])B.record(at,{queue:J,flags:X,count:b,out:De,outCount:Z,outIndex:0});return ot!==null&&Le!==null&&ot.record(at,Yi(Y,ce,F,M,g),Le),a.record(at,{src:xe,dst:ae,weights:Le,count:ce,n,sortedInput:!0,out:{rowPtr:l,colIdx:we,weights:me,flag:c}}),qe.endPass(),{graph:{n,arcCount:ce,rowPtr:l,colIdx:we,weights:me,src:xe},batch:qe,flag:qe.readback(c.buffer,0,4)}}function Yi(r,e,t,s,i){return{serial:-1,plan:"perArray",rowPtr:{...r,size:4*(e+1)},colIdx:{...t,size:4*i},weights:{...s,size:4*i},arcToEdge:null,edgeToArc:null,windows:null,arcBuffers:null,hasWeights:!0}}const Te="labelPropagation",Of=100,Nf=1024,fs=857579651;function Bf(r){return r^=r>>>16,r=Math.imul(r,2246822507),r^=r>>>13,r=Math.imul(r,3266489909),r^=r>>>16,r>>>0}function $f(r){return r^=r>>>16,r=Math.imul(r,2127672349),r^=r>>>13^r>>>26,r=Math.imul(r,2781581891),r^=r>>>16,r>>>0}function Cf(r,e){if(r===void 0)return null;if(r instanceof Uint32Array&&r.length===e&&r.buffer instanceof ArrayBuffer)return r;throw new h("E_INVALID_ARGUMENT",`${Te}: dest must be a Uint32Array of length ${e} over an ArrayBuffer`,{argument:"dest",value:`${r.constructor.name}(${r.length})`,expected:`Uint32Array(${e}) over an ArrayBuffer`})}function Mf(r){if(r===void 0)return Of;if(!Number.isSafeInteger(r)||r<0)throw new h("E_INVALID_ARGUMENT",`${Te}: maxIterations must be a non-negative integer`,{argument:"maxIterations",value:r,expected:"a non-negative integer"});return r}function Gf(r){const e=r.outDegree();if(!r.directed)return e;const t=r.inDegree(),s=new Uint32Array(r.nodeCount);for(let i=0;i<s.length;i++)s[i]=e[i]+t[i];return s}function zf(r,e){const t=r.length;for(let n=0;n<t;n++){const o=$f(r[n]);if(o>=t)throw new h("E_VALIDATION",`${Te}: labels[${n}] = ${r[n]} is not a node's label`,{label:`${Te}/labels`,message:`the device produced a label that scrambles no index in [0, ${t})`});r[n]=o}const{labels:s,count:i}=_n(r,e??void 0);return Ut(s,i)}function Xi(r,e){const t=e??new Uint32Array(r);for(let s=0;s<r;s++)t[s]=s;return Ut(t,r)}async function Ff(r,e,t){r.assertReady(),await Ve(r);const s=e.nodeCount,i=Cf(t?.dest,s),n=Mf(t?.maxIterations),o=t?.weighted!==!1;if(t?.signal?.aborted)throw new h("E_ABORTED",`${Te}: the signal was aborted before any work started`,{});if(s===0||e.edgeCount===0||n===0)return t?.onProgress?.(1,1),Xi(s,i);if(s>fs)throw new h("E_TOO_LARGE",`${Te}: ${s} nodes, above the ${fs} the labels allow`,{needed:s,limit:fs,path:"the scrambled labels",algorithm:Te});const a=pf(Gf(e));_o(r,4*a.regionWords,"the group-by hash region",Te);const c=je(r,Te,Nf);try{const l=await Po(r,e,c,o,Te),{graph:d}=l;if(d.colIdx===null){c.flush();const R=await l.batch.submit().readback;return r.assertReady(),xs(R,l,Te),t?.onProgress?.(1,1),Xi(s,i)}const f=r.workgroupSize,p=(R,O)=>{const C=4*Math.max(1,R);return{buffer:c.scratch(C,O),offset:0,size:C,window:null}},g=p(a.words.length,"rows"),m=p(a.regionWords,"hashRegion"),b=p(s,"labelsA"),w=p(s,"labelsB"),v=p(s,"bestKey"),y=p(s,"bestScore"),I=p(hi,"counters");await r.allocator.check();const{queue:S}=r.device;S.writeBuffer(g.buffer,0,a.words),S.writeBuffer(m.buffer,0,new Uint32Array(1));const P=await gf(c),E=await r.pipelines.kernel(_("lpa-step")),A=z(s,f,r.caps),L=new Uint32Array(s);for(let R=0;R<s;R++)L[R]=Bf(R);S.writeBuffer(b.buffer,0,L);let{batch:B}=l,D=!0,x=b,T=w,k=0,$=-1;for(;;){const R=Math.min(hi,n-k);S.writeBuffer(I.buffer,0,new Uint32Array(R));const O=B.pass("passes");for(let j=0;j<R;j++){P.record(O,{rowPtr:d.rowPtr,colIdx:d.colIdx,weights:o?d.weights:null,keyIn:x,plan:a,rows:g,hashRegion:m,bestKey:v,bestScore:y});const G=c.params(qn,{n:s,direction:(k+j+1)%2,counterIndex:j,pad0:0});E.dispatch(O,E.bind({labelsIn:x,bestKey:v,labelsOut:T,counters:I,P:G.binding}),A,[G.offset]),[x,T]=[T,x]}B.endPass();const C=B.readback(I.buffer,0,4*R),F=B.readback(m.buffer,0,4);c.flush();const M=B.submit(),X=await M.readback;if(r.assertReady(),D&&(xs(X,l,Te),D=!1),new Uint32Array(X,F.offset,1)[0]!==0)throw new h("E_VALIDATION",`${Te}: a hash probe exhausted its bound`,{label:`${Te}/group-by-key`,message:"a compare-exchange loop of the workgroup tier ran out of steps"});const K=new Uint32Array(X,C.offset,R);k+=R;const q=R>=2?K[R-2]+K[R-1]:$+K[0];if($=K[R-1],t?.signal?.aborted)throw new h("E_ABORTED",`${Te}: the signal was aborted`,{batchId:M.id});if(t?.onProgress?.(k,n),q===0||k>=n)break;B=new ne(r,`${Te}/passes`)}const N=new Uint32Array(s);return await r.readback.read(x.buffer,4*s,N),r.assertReady(),zf(N,i)}finally{c.dispose()}}const Qe="minimumSpanningTree",Rs=1024,Wf=64,Uf=5+It;function Vf(r){let e=1;for(let t=Rs;t<r;t*=Rs)e++;return e}function jf(r,e,t){const s=new Uint32Array(t),{weights:i}=r.edgeList();let n=0,o=0;for(const a of e)if(a!==dt){if(a>=r.edgeCount||n===t)throw new h("E_VALIDATION",`${Qe}: the device recorded an edge it did not count`,{label:`${Qe}/treeEdge`,message:`edge ${a} at forest position ${n} of ${t}`});s[n++]=a,o+=i===null?1:i[a]}if(n!==t)throw new h("E_VALIDATION",`${Qe}: ${n} recorded edges, ${t} counted`,{label:`${Qe}/treeEdge`,message:"the per-round counts disagree with the recorded forest"});return{edges:s,totalWeight:o}}async function qf(r,e,t){r.assertReady(),await Ve(r);const s=e.nodeCount,i=e.edgeCount;if(s===0||i===0)return{edges:new Uint32Array(0),totalWeight:0};r.residency.core(e,["rowPtr"]);const n=r.residency.view(e,"edgeList"),o=n.bindings.weights??null,a=je(r,Qe,Uf);try{const c=r.workgroupSize,l=(N,R)=>{const O=4*Math.max(1,N);return{buffer:a.scratch(O,R),offset:0,size:O,window:null}},d=l(s,"comp"),f=l(s,"bestKey"),p=l(s,"bestEdge"),g=l(s,"treeEdge"),m=l(It,"counters");await r.allocator.check();const b=await r.pipelines.kernel(_("fill")),w=o!==null,v=await r.pipelines.kernel(_("mst-best",{PASS:0,WEIGHTED:w})),y=await r.pipelines.kernel(_("mst-best",{PASS:1,WEIGHTED:w})),I=await r.pipelines.kernel(_("mst-link")),S=await r.pipelines.kernel(_("wcc-compress")),P=z(s,c,r.caps),E=z(i,c,r.caps),A=Oe(s,c,r.caps),L=Vf(s),{queue:B}=r.device;let D=new ne(r,`${Qe}/rounds`),x=!0,T=0,k=0;for(;;){B.writeBuffer(m.buffer,0,new Uint32Array(It));const N=D.pass("rounds"),R=(W,H)=>a.params(Ae,{count:s,value:W,mode:H,pad0:0});if(x){const W=R(0,1);b.dispatch(N,b.bind({dst:d,P:W.binding}),P,[W.offset]);const H=R(dt,0);b.dispatch(N,b.bind({dst:g,P:H.binding}),P,[H.offset])}const O=R(ye,0),C=a.params(Dr,{count:i,counterIndex:0,pad0:0,pad1:0}),F=a.params(Xt,{n:s,items:s,stride:A.stride??s,r:0,flagIndex:0,giant:ye,maxSteps:Rs,pad0:0}),M={edgeSrc:n.bindings.src,edgeDst:n.bindings.dst,edgeWeight:o??n.bindings.src};for(let W=0;W<It;W++){for(const Q of[f,p])b.dispatch(N,b.bind({dst:Q,P:O.binding}),P,[O.offset]);for(const Q of[v,y])Q.dispatch(N,Q.bind({...M,comp:d,bestKey:f,bestEdge:p,P:C.binding}),E,[C.offset]);const H=a.params(Dr,{count:s,counterIndex:W,pad0:0,pad1:0});I.dispatch(N,I.bind({edgeSrc:M.edgeSrc,edgeDst:M.edgeDst,bestEdge:p,comp:d,treeEdge:g,counters:m,P:H.binding}),P,[H.offset]);for(let Q=0;Q<L;Q++)S.dispatch(N,S.bind({comp:d,P:F.binding}),A,[F.offset])}D.endPass();const X=D.readback(m.buffer,0,4*It);a.flush();const q=await D.submit().readback;r.assertReady(),x=!1,t?.signal?.aborted;const j=new Uint32Array(q,X.offset,It);let G=!1;for(const W of j)k+=W,G||=W===0;if(T+=It,k>s-1)throw new h("E_VALIDATION",`${Qe}: ${k} forest edges on ${s} nodes`,{label:`${Qe}/counters`,message:"a spanning forest has at most n - 1 edges"});if(G)break;if(T>=Wf)throw new h("E_VALIDATION",`${Qe}: still merging after ${T} rounds`,{label:`${Qe}/rounds`,message:"Boruvka halves the components every round; the device did not"});D=new ne(r,`${Qe}/rounds`)}const $=new Uint32Array(s);return await r.readback.read(g.buffer,4*s,$),r.assertReady(),t?.onProgress?.(1,1),jf(e,$,k)}finally{a.dispose()}}const So=8,Kf=2*So+2;function Hf(r,e,t){if(r===void 0)return null;if(r instanceof Float32Array&&r.length===e&&r.buffer instanceof ArrayBuffer)return r;throw new h("E_INVALID_ARGUMENT",`${t}: dest must be a Float32Array of length ${e} over an ArrayBuffer`,{argument:"dest",value:`${r.constructor.name}(${r.length})`,expected:`Float32Array(${e}) over an ArrayBuffer`})}function Yf(r,e,t){const s=r.residency.core(e);return Pt(s,e.arcCount,r.caps.limits.maxStorageBufferBindingSize,t),s}function Tt(r,e){return{buffer:r,offset:0,size:e,window:null}}function Qi(r,e){return new h("E_ABORTED",`${r}: the signal was aborted`,e===void 0?{}:{batchId:e})}async function Eo(r,e,t,s,i){r.assertReady(),await Ve(r);const n=e.nodeCount,o=s?.dampingFactor??.85,a=s?.maxIterations??100,c=s?.tolerance??1e-6,l=s?.weighted!==!1;if(!Number.isInteger(a)||a<1)throw new h("E_INVALID_ARGUMENT",`${i}: maxIterations must be a positive integer`,{argument:"maxIterations",value:a,expected:"a positive integer"});const d=Hf(s?.dest,n,i);if(s?.signal?.aborted)throw Qi(i);if(n===0)return s?.onProgress?.(a,a),{scores:d??new Float32Array(0),iterations:0,converged:!0,danglingMass:0,precision:"f32"};const f=Yf(r,e,i);if(e.arcCount===0){const I=d??new Float32Array(n);return t===null?I.fill(1/n):I.set(t),s?.onProgress?.(a,a),{scores:I,iterations:0,converged:!0,danglingMass:1,precision:"f32"}}const p=r.residency.view(e,"reverse"),g=Ur(p,p.scalars.arcCount[0]),m=l?void 0:null,b=l?f:{...f,weights:null,hasWeights:!1},w=l?g:{...g,weights:null,hasWeights:!1},v=je(r,i,Kf);let y=null;try{const I=4*n,S=v.scratch(I,"rankA"),P=v.scratch(I,"rankB"),E=v.scratch(I,"xNorm"),A=v.scratch(I,"outWeightSum"),L=z(n,r.workgroupSize,r.caps),B=Gs(L),D=ke.byteLength*(1+B),x=v.scratch(D,"partials"),T=v.scratch(ke.byteLength,"lastHeader");t!==null&&(y=r.residency.array(t,`${i}/personalization`)),await r.allocator.check();const k=await yo(v,b,{op:"sum",valueSnippet:"v = weight;",tiers:null}),$=await ks(v,w,{personalization:t!==null,dangling:!0,weights:m,tiers:null}),N=await r.pipelines.kernel(_("pr-scale",{NORM_MODE:0})),R=await r.pipelines.kernel(_("pr-finalize",{NORM_MODE:0})),O=z(1,r.workgroupSize,r.caps),{queue:C}=r.device;C.writeBuffer(S,0,new Float32Array(n).fill(1/n)),C.writeBuffer(P,0,new Float32Array(n));const F=new ArrayBuffer(ke.byteLength);ke.write(new DataView(F),{firstConverged:ye,iteration:0}),C.writeBuffer(x,0,F);const M=[Tt(S,I),Tt(P,I)],X=Tt(E,I),K=Tt(A,I),q=Tt(x,D),j=Tt(T,ke.byteLength),G={alpha:o,beta:1-o,uniformP:1/n};let W=0,H=0;for(;;){const Q=Math.min(So,a-H),V=new ne(r,i);let U=V.pass("iterations");H===0&&k.record(U,b,K);const oe=H+Q===a;for(let Z=0;Z<Q+(oe?1:0);Z++){const we=v.params(zr,{n,groups:B,iteration:H+Z+1,trackConvergence:1,convergeThreshold:c*n}),me=1-W;Z===Q&&(V.copy(q,j,ke.byteLength),U=V.pass("convergence"));const Le=N.bind({rankIn:M[W],rankPrev:M[me],outWeightSum:K,xNorm:X,partials:q,P:we.binding});N.dispatch(U,Le,L,[we.offset]);const ot=R.bind({partials:q,P:we.binding});if(R.dispatch(U,ot,O,[we.offset]),Z===Q)break;$.record(U,w,{xNorm:X,rankOut:M[me],personalization:y?.binding??null,partials:q},G),W=me}V.endPass();const te=V.readback(x,0,ke.byteLength),de=oe?V.readback(T,0,ke.byteLength):te,pe=V.readback(M[W].buffer,0,I);v.flush();const ge=V.submit(),ce=await ge.readback;if(H+=Q,r.assertReady(),s?.signal?.aborted)throw Qi(i,ge.id);s?.onProgress?.(H,a);const ae=ke.read(new DataView(ce),te.offset).firstConverged,Y=ae!==ye;if(Y||H>=a){const Z=d??new Float32Array(n);return Z.set(new Float32Array(ce,pe.offset,n)),Y&&H<a&&s?.onProgress?.(a,a),{scores:Z,iterations:Y?ae:H,converged:Y,danglingMass:ke.read(new DataView(ce),de.offset).danglingMass,precision:"f32"}}}}finally{y?.destroy(),v.dispose()}}function Xf(r,e,t){return Eo(r,e,null,t,"pageRank")}async function Qf(r,e,t,s){const i=e.nodeCount,n=(c,l)=>new h("E_INVALID_ARGUMENT",`personalizedPageRank: personalization must be ${l}`,{argument:"personalization",value:c,expected:l});if(!(t instanceof Float32Array)||t.length!==i)throw n(`${t.constructor.name}(${t.length})`,`a Float32Array of length ${i}`);let o=0;for(let c=0;c<i;c++){const l=t[c];if(!Number.isFinite(l)||l<0)throw n(l,"finite and non-negative in every entry");o+=l}if(i>0&&!(o>0))throw n(o,"a vector whose entries sum to a positive number");const a=new Float32Array(i);for(let c=0;c<i;c++)a[c]=t[c]/o;return Eo(r,e,a,s,"personalizedPageRank")}function js(r,e,t,s){r.assertReady();const i=e.nodeCount,n=t?.maxIterations??100,o=t?.tolerance??1e-6;if(!Number.isInteger(n)||n<1)throw new h("E_INVALID_ARGUMENT",`${s}: maxIterations must be a positive integer`,{argument:"maxIterations",value:n,expected:"a positive integer"});const a=zd(t?.dest,i,s);if(t?.signal?.aborted)throw uo(s);return{n:i,maxIterations:n,tolerance:o,weights:t?.weighted===!1?null:void 0,dest:a}}function Zi(r,e,t){const s=r??e;if(!Number.isFinite(s))throw new h("E_INVALID_ARGUMENT",`katzCentrality: ${t} must be a finite number`,{argument:t,value:s,expected:"a finite number"});return s}function Ls(r,e,t){let s=0;for(const o of r)s+=t==="sum"?Math.abs(o):o*o;const i=t==="sum"?s:Math.sqrt(s),n=e??r;for(let o=0;o<r.length;o++)n[o]=i>0?r[o]/i:r[o];return n}function Io(r,e,t){return{scores:Ls(r.scores,e,t),iterations:r.iterations,converged:r.converged,precision:"f32"}}function qs(r,e,t){return t?.(e,e),{scores:r??new Float32Array(0),iterations:0,converged:!0,precision:"f32"}}function Ji(r,e){const t=r.iterationsRun%2===0?r.iterationsRun:r.iterationsRun-1,s=e.iterationsRun%2===1?e.iterationsRun:e.iterationsRun-1,i=t>=s?r:e;return(Math.max(t,s)===i.iterationsRun?i.scores:i.previous)??i.scores}async function Zf(r,e,t){const s="eigenvectorCentrality",i=js(r,e,t,s);if(i.n===0)return qs(i.dest,i.maxIterations,t?.onProgress);const n=await Nr(r,i.n,{normMode:2,adjacency:lo(r,e,s),alternate:null,alpha:1,beta:0,uniformP:0,maxIterations:i.maxIterations,tolerance:i.tolerance,weights:i.weights,label:s,signal:t?.signal,onProgress:t?.onProgress});return Io(n,i.dest,"l2")}async function Jf(r,e,t){const s="katzCentrality",i=Zi(t?.alpha,.1,"alpha"),n=Zi(t?.beta,1,"beta"),o=js(r,e,t,s);if(o.n===0)return qs(o.dest,o.maxIterations,t?.onProgress);const a=await Nr(r,o.n,{normMode:4,adjacency:Vs(r,e),alternate:null,alpha:i,beta:n,uniformP:1,maxIterations:o.maxIterations,tolerance:o.tolerance,weights:o.weights,label:s,signal:t?.signal,onProgress:t?.onProgress});return Io(a,o.dest,"l2")}async function eh(r,e,t){const s="hits",i=js(r,e,t,s),n=2*i.maxIterations;if(i.n===0){const{scores:p,iterations:g,converged:m,precision:b}=qs(i.dest,n,t?.onProgress);return{hubs:p,authorities:new Float32Array(0),iterations:g,converged:m,precision:b}}const o=t?.onProgress,a={normMode:1,alpha:1,beta:0,uniformP:0,maxIterations:i.maxIterations,tolerance:i.tolerance,weights:i.weights,signal:t?.signal},c=lo(r,e,s),l=Vs(r,e),d=await Nr(r,i.n,{...a,adjacency:l,alternate:c,label:`${s}/hub-seeded`,onProgress:o===void 0?void 0:p=>{o(p,n)}}),f=await Nr(r,i.n,{...a,adjacency:c,alternate:l,label:`${s}/authority-seeded`,onProgress:o===void 0?void 0:p=>{o(i.maxIterations+p,n)}});return{hubs:Ls(Ji(d,f),i.dest,"sum"),authorities:Ls(Ji(f,d),null,"sum"),iterations:Math.max(d.iterations,f.iterations),converged:d.converged&&f.converged,precision:"f32"}}const sr="triangleCount",th=1024;function rh(r,e){return null}function en(r,e){const t=r.length,s=new Float32Array(t);let i=0,n=0;for(let o=0;o<t;o++){const a=e[o+1]-e[o],c=r[o];if(i+=c,a>=2){const l=a*(a-1)/2;n+=l,s[o]=c/l}}return{perNode:r,total:i/3,coefficient:s,transitivity:n===0?0:i/n}}async function sh(r,e,t){return await ih(r,e,0,t)}async function ih(r,e,t,s){r.assertReady(),await Ve(r);const i=e.nodeCount,n=rh();if(i===0)return en(n??new Uint32Array(0),new Uint32Array(1));const o=je(r,sr,th);try{const a=await Po(r,e,o,!1,sr),{graph:c,batch:l}=a,d=r.workgroupSize,f=4*i,p={buffer:o.scratch(f,"counts"),offset:0,size:f,window:null},g=await r.pipelines.kernel(_("fill")),m=l.pass("triangles");let b=null;const w=o.params(Ae,{count:i,value:0,mode:0,pad0:0});if(g.dispatch(m,g.bind({dst:p,P:w.binding}),z(i,d,r.caps),[w.offset]),c.arcCount>0&&c.colIdx!==null&&c.src!==null){const L=c.arcCount/2,B=await r.pipelines.kernel(_("orient-flags")),D=await r.pipelines.kernel(_("tri-intersect",{SEARCH:t})),x=await Vr(o),T=await vo(o),k=(j,G)=>({buffer:o.scratch(4*j,G),offset:0,size:4*j,window:null}),$=k(c.arcCount,"orient/flags"),N=k(c.arcCount,"orient/src"),R=k(c.arcCount,"orient/dst"),O=k(i+1,"orient/rowPtr"),C=k(L,"orient/colIdx"),F=k(1,"orient/count"),M=k(1,"orient/flag"),X=z(c.arcCount,d,r.caps),K=o.params(nt,{count:c.arcCount,pad0:0,pad1:0,pad2:0});B.dispatch(m,B.bind({rowPtr:c.rowPtr,colIdx:c.colIdx,src:c.src,flags:$,P:K.binding}),X,[K.offset]),x.record(m,{queue:c.src,flags:$,count:c.arcCount,out:N,outCount:F,outIndex:0}),x.record(m,{queue:c.colIdx,flags:$,count:c.arcCount,out:R,outCount:F,outIndex:0}),T.record(m,{src:N,dst:R,weights:null,count:L,n:i,sortedInput:!0,out:{rowPtr:O,colIdx:C,weights:null,flag:M}}),b=M;const q=o.params(nt,{count:L,pad0:0,pad1:0,pad2:0});D.dispatch(m,D.bind({rowPtr:O,colIdx:C,src:N,counts:p,P:q.binding}),z(L,d,r.caps),[q.offset])}l.endPass();const v=l.readback(p.buffer,0,f),y=l.readback(c.rowPtr.buffer,0,4*(i+1)),I=b===null?null:l.readback(b.buffer,0,4);o.flush();const P=await l.submit().readback;if(r.assertReady(),s?.signal?.aborted,xs(P,a,sr),I!==null&&new Uint32Array(P,I.offset,1)[0]!==0)throw new h("E_VALIDATION",`${sr}: the oriented arcs reached cooToCsr out of order`,{label:`${sr}/oriented`,message:"the sorted-input precondition of cooToCsr failed on the device"});const E=n??new Uint32Array(i);E.set(new Uint32Array(P,v.offset,i));const A=new Uint32Array(P,y.offset,i+1);return s?.onProgress?.(1,1),en(E,A)}finally{o.dispose()}}function nh(r){let e=1;for(;e<r;)e*=2;return e}function oh(r){let e=1;for(;e*2<=r;)e*=2;return e}function ah(r,e){const t=n=>e===3?n*n*n:n*n;let s=1;for(;t(s)<r;)s*=2;let i=0;for(;i<s;){const n=Math.floor((i+s)/2);t(n)>=r?s=n:i=n+1}return i}function ut(r,e,t){const s=e===3?t.gridMax3D:t.gridMax2D,i=Math.max(di,oh(Math.max(1,s))),n=Math.min(i,Math.max(di,nh(2*ah(r,e))));let o=1;for(let f=n;f>fi;f/=2)o++;const a=n**e,c=2**e,l=[0];let d=n;for(let f=0;f+1<o;f++)l.push(l[f]+d**e+(f===0?c:0)),d/=2;return{dim:e,g:n,levels:o,cells:a,outsideCells:c,histWords:a+c+1,levelOffsets:Object.freeze(l),pyramidCells:l[o-1]+fi**e,deterministic:t.deterministic}}function ch(r){return 16*r.pyramidCells}async function lh(r,e){const t=await r.pipelines.kernel(_("grid-cell-key")),s=e.deterministic?{kind:"radix",radix:await Us(r),histogram:await mo(r),scan:await Rt(r)}:{kind:"counting",counting:await wf(r)};return new uh(r,e,t,s)}class uh{constructor(e,t,s,i){this.bound=null,this.dispatches=0,this.scope=e,this.spec=t,this.cellKey=s,this.path=i}get lastDispatches(){return this.dispatches}bind(e){const t=Math.floor(e.cellKey.size/4);if(t<1)throw new h("E_INVALID_ARGUMENT","gridBuild: cellKey must hold at least one word",{argument:"cellKey",value:e.cellKey.size,expected:4});const s=this.path.kind==="radix"?jr(t,this.scope.workgroupSize)/4:this.spec.histWords;this.bound={bindings:e,capacity:t,scratchA:this.scratch(s,"grid/sort-scratch-a"),scratchB:this.scratch(s,"grid/sort-scratch-b")}}record(e,t,s,i){const{bound:n}=this;if(n===null)throw new h("E_NOT_LOADED","gridBuild: record() before bind()",{argument:"bind"});if(!Number.isSafeInteger(t)||t<1||t>n.capacity)throw new h("E_INVALID_ARGUMENT","gridBuild: n must be an integer in [1, the capacity]",{argument:"n",value:t,expected:n.capacity});const o=i??"G3",a=n.bindings,c=this.cellKey.bind({pos:a.pos,S:a.state,cellKey:a.cellKey,cellVal:a.cellVal,P:a.params});if(this.cellKey.dispatch(e,c,z(t,this.scope.workgroupSize,this.scope.caps),[s]),this.dispatches=1,o==="G1")return;const{histWords:l}=this.spec;if(this.path.kind==="counting"){const{counting:g}=this.path,m={hist:a.cellHist,cursor:n.scratchA};g.record(e,a.cellKey,t,l,m,a.sortedIdx,a.cellStart),this.dispatches+=g.lastDispatches;return}const{radix:d,histogram:f,scan:p}=this.path;d.record(e,a.cellKey,a.cellVal,t,Jo,{keys:a.sortedKey,vals:a.sortedIdx,hist:n.scratchA,offsets:n.scratchB}),this.dispatches+=d.lastDispatches,o!=="G2"&&(f.record(e,a.cellKey,t,l,a.cellHist),p.record(e,a.cellHist,l,a.cellStart),this.dispatches+=f.lastDispatches+p.lastDispatches)}scratch(e,t){const s=4*Math.max(1,e);return{buffer:this.scope.scratch(s,t),offset:0,size:s,window:null}}}const hs=34359738337,dh=185852,fh=1;class hh{constructor(e){this.seed=e===null||e===0||Number.isNaN(e)?Math.floor(Math.random()*1e6):e,this.state=this.seed%hs}next(){return this.state=(dh*this.state+fh)%hs,this.state/hs}}function ph(r){const e=[0,0,0];if(r===null)return e;for(let t=0;t<3&&t<r.length;t++){const s=r[t];if(!Number.isFinite(s))throw new h("E_INVALID_ARGUMENT",`center[${t}] is not finite`,{argument:"center",value:s,expected:"finite components"});e[t]=s}return e}function gh(r,e,t,s,i,n,o){const a=r.nodeCount;if(s!==2&&s!==3)throw new h("E_INVALID_ARGUMENT",`dim must be 2 or 3, got ${String(s)}`,{argument:"dim",value:s,expected:"2 or 3"});if(e.length!==3*a)throw new h("E_INVALID_ARGUMENT",`positions has ${e.length} entries, expected ${3*a}`,{argument:"positions",value:e.length,expected:3*a});if(!Number.isFinite(i)||i<=0)throw new h("E_INVALID_ARGUMENT",`scale must be a finite number > 0, got ${i}`,{argument:"scale",value:i,expected:"a finite number > 0"});const c=ph(n),l=[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY],d=[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY];let f=0,p=0;for(let y=0;y<a;y++){let I=!0;for(let S=0;S<s;S++){const P=e[3*y+S];Number.isFinite(P)?(P<l[S]&&(l[S]=P),P>d[S]&&(d[S]=P)):I=!1}I?f++:p++}if(p===0)return;const g=o==="fr"?0:-1,m=1,b=[g,g,g],w=[m,m,m];if(f>0){for(let y=0;y<s;y++)if(Number.isFinite(l[y])&&Number.isFinite(d[y])&&(b[y]=(l[y]-c[y])/i,w[y]=(d[y]-c[y])/i,b[y]===w[y])){const I=(m-g)/2;b[y]-=I,w[y]+=I}}const v=new hh(t);for(let y=0;y<a;y++){let I=!1;for(let S=0;S<s;S++)Number.isFinite(e[3*y+S])||(I=!0);if(I){for(let S=0;S<s;S++){const P=3*y+S;if(!Number.isFinite(e[P])){const E=v.next(),A=b[S]+E*(w[S]-b[S]);e[P]=A*i+c[S]}}s===2&&(e[3*y+2]=c[2])}}}const mh=[["n",1],["dim",1],["flags",1],["iterationIndex",1],["seed",1],["scale",1],["center",4],["settleThreshold",1]],wh=[["centroid",4],["min",4],["max",4],["rmsRadius",1],["radius",1],["meanDisplacement",1],["iteration",1],["settledCount",1]],bh=new Set(["positions","scenePositions","fixed","partials","state","trace"]),vh=new Set(["fixed","trace","cellKey","cellVal","sortedKey","sortedIdx","cellHist","cellStart","hubList","hubCounters"]),yh="toScene";let tn=0;function ee(r,e,t,s){return new h("E_INVALID_ARGUMENT",s,{argument:r,value:e,expected:t})}function rn(r){return new h("E_DISPOSED",`${r} is disposed`,{label:r})}function Ot(r){return r instanceof Error?r:new Error(typeof r=="string"?r:"unknown error")}function ps(r,e){return r[e]}function Ks(r){switch(r){case"u32":case"i32":case"f32":return 1;case"vec2f":case"vec2u":return 2;case"vec4f":case"vec4u":return 4;default:throw ee("type",r,"a UniformFieldType","unknown uniform field type")}}function ko(r,e){for(const[t,s]of r.fields)if(t===e)return s;return null}function Ao(r,e){const t=ko(r,e);if(t===null)throw ee("field",e,r.fields.map(s=>s[0]),`block ${r.name} has no field ${e}`);return t}function sn(r,e,t){for(const[s,i]of e){const n=ko(r,s);if(n===null)throw ee(t,s,e.map(o=>o[0]),`${t} block ${r.name} must declare ${s}`);if(Ks(n)!==i)throw ee(t,s,`a field of width ${i}`,`${t}.${s} must have width ${i}`)}}function nn(r){return JSON.stringify(Object.keys(r).sort().map(e=>[e,r[e]]))}function on(r,e){return{...r,msPerIteration:e}}function _h(r){return typeof SharedArrayBuffer<"u"&&r instanceof SharedArrayBuffer}function gs(r){const e=r.scale??At.scale;if(!Number.isFinite(e)||e<=0)throw ee("scale",e,"a finite number > 0",`scale must be a finite number > 0, got ${e}`);return e}function ms(r){const e=[0,0,0],{center:t}=r;if(t===void 0)return e;for(let s=0;s<3&&s<t.length;s++){const i=t[s];if(!Number.isFinite(i))throw ee("center",i,"finite components",`center[${s}] is not finite`);e[s]=i}return e}function hr(r,e){return r.repulsion==="exact"?"exact":r.repulsion==="grid"?"grid":e<=r.exactMaxNodes?"exact":"grid"}function ir(r,e){return{buffer:r,offset:0,size:e,window:null}}class Ph{constructor(e){this.shadow=new Map,this.queued=new Map,this.block=e,this.reset()}set(e,t){const s=Ks(Ao(this.block,e));if(!(s===1?typeof t=="number":typeof t!="number"&&t.length===s))throw ee("value",t,`a value of width ${s}`,`state field ${e} takes a value of width ${s}`);this.shadow.set(e,t),this.queued.set(e,t)}get(e){const t=this.shadow.get(e);if(t===void 0)throw ee("field",e,this.block.fields.map(s=>s[0]),`state block has no field ${e}`);return t}reset(){this.shadow.clear(),this.queued.clear();const e=this.block.read(new DataView(new ArrayBuffer(this.block.byteLength)));for(const[t,s]of Object.entries(e))this.shadow.set(t,s)}headerBytes(){const e=new ArrayBuffer(this.block.byteLength);return this.block.write(new DataView(e),Object.fromEntries(this.shadow)),e}absorb(e){for(const[t,s]of Object.entries(this.block.read(e)))this.queued.has(t)||this.shadow.set(t,s)}takeQueued(){const e=[...this.queued.keys()];return this.queued.clear(),e}clearQueued(){this.queued.clear()}}function Sh(r,e){let t=()=>{},s=()=>{};const i=new Promise((n,o)=>{t=n,s=o});return{k:r,generation:e,promise:i,resolve:()=>{t()},reject:n=>{s(n)},id:0,submitted:null,stale:!1,startedAt:0,sceneOffset:0,stateOffset:0,profile:null}}class Hs{constructor(e,t,s,i,n){if(this.overrideList=new Map,this.pending=[],this.stateValue="created",this.generationValue=0,this.snapshot=null,this.serial=null,this.owner=null,this.n=0,this.scale=1,this.center=[0,0,0],this.buffers=null,this.core=null,this.resources=null,this.weightsUpload=null,this.ready=Promise.resolve(),this.submitChain=Promise.resolve(),this.fixedWords=new Uint32Array(0),this.fixedDirty=!1,this.iterationsSubmitted=0,this.iterationsDoneValue=0,this.settledCountValue=0,this.settledValue=!1,this.firstPending=!0,this.statsValue=null,this.lastPassTimingsValue=null,this.lastSubmittedBatchIdValue=0,this.reheatedAfterBatchId=0,this.coalescedValue=0,this.torndown=!1,e.assertReady(),sn(t.params,mh,"model.params"),sn(t.state,wh,"model.state"),t.params.byteLength>Ye)throw ee("model.params",t.params.byteLength,`<= ${Ye} bytes`,"the params block must fit one ring slot");if(t.state.byteLength>He)throw ee("model.state",t.state.byteLength,`<= ${He} bytes`,"the state block must fit the state header");if(t.stages.length===0)throw ee("model.stages",t.stages,"at least one stage name","a model declares its stages");const o=s.maxInFlight??At.maxInFlight;if(!Number.isInteger(o)||o<1)throw ee("maxInFlight",o,"an integer >= 1",`maxInFlight must be an integer >= 1, got ${o}`);const a=s.dim??At.dim;if(a!==2&&a!==3)throw ee("dim",a,"2 or 3",`dim must be 2 or 3, got ${String(a)}`);gs(s),ms(s),tn++,this.ctx=e,this.model=t,this.tuning=i,this.resolveOptions=n,this.optionsValue=s,this.maxInFlight=o,this.dimValue=a,this.tierValue=i.repulsion==="grid"?"grid":"exact",this.label=`${t.kind}#${tn}`,this.traceBytes=t.trace.byteLength,this.writer=new Ph(t.state),this.ring=new Ws(e.device,e.allocator,(o+1)*Fe,`${this.label}/ring`),this.unregisterLost=e.onLost(c=>{this.onDeviceLost(c)}),e.debug.inspect&&(this.inspect=c=>this.inspectBuffer(c),this.debugRunStages=c=>this.runStages(c))}get state(){return this.stateValue}get options(){return this.optionsValue}get tier(){return this.tierValue}get lastPassTimings(){return this.lastPassTimingsValue}get generation(){return this.generationValue}get nodeCount(){return this.n}get dim(){return this.dimValue}get settled(){return this.settledValue}get inFlight(){return this.pending.length}get iterationsDone(){return this.iterationsDoneValue}get stats(){if(this.statsValue!==null)return this.statsValue;const e=new DataView(this.writer.headerBytes()),t=new DataView(new ArrayBuffer(0));return on(this.model.readStats(e,t),null)}get overrides(){return this.overrideList}get lastSubmittedBatchId(){return this.lastSubmittedBatchIdValue}get coalesced(){return this.coalescedValue}load(e,t){if(this.assertNotDisposed(),this.ctx.assertReady(),e.directed)throw new h("E_SNAPSHOT","a layout needs an undirected snapshot: pass toUndirected().snapshot",{reason:"directed",serial:e.serial});const s=e.nodeCount;if(s>Zr)throw new h("E_TOO_LARGE",`${s} nodes exceed ${Zr} (the third partials level is P4)`,{needed:s,limit:Zr,path:"partials",algorithm:this.model.kind});if(t.length!==3*s)throw ee("positions",t.length,3*s,`positions has ${t.length} entries, expected ${3*s}`);if(_h(t.buffer))throw ee("positions","SharedArrayBuffer","an ArrayBuffer-backed Float32Array","positions must not be backed by a SharedArrayBuffer");const i=gs(this.optionsValue),n=ms(this.optionsValue),o=hr(this.tuning,s),a=16*s;if(a>this.ctx.caps.limits.maxBufferSize)throw new h("E_TOO_LARGE",`${a} bytes of positions exceed maxBufferSize`,{needed:a,limit:this.ctx.caps.limits.maxBufferSize,path:"positions",algorithm:this.model.kind});let c=null,l=null,d=null;if(s>0){if(c=this.ctx.residency.core(e),c.plan==="windowed")throw new h("E_TOO_LARGE","a windowed core cannot be walked by the layout kernels until P4",{needed:e.arcCount,limit:this.ctx.caps.limits.maxStorageBufferBindingSize,path:"windowed",algorithm:this.model.kind});if(e.degreeOrder().segmentOffsets[2]>0&&(l=nd(this.ctx.residency.view(e,"degreeOrder"))),d=this.model.inputs(e,this.optionsValue),d.mass.length!==s)throw ee("nodeMass",d.mass.length,s,`the model resolved ${d.mass.length} masses for ${s} nodes`);const v=Math.ceil(s/32);if(d.fixed!==void 0&&d.fixed!==null&&d.fixed.length<v)throw ee("fixed",d.fixed.length,v,`the model resolved a fixed mask of ${d.fixed.length} words, ${v} needed for ${s} nodes`)}this.generationValue++,this.discardPending();const f=this.buffers===null||s!==this.n||e.serial!==this.serial;if(this.snapshot=e,this.serial=e.serial,this.owner=t,this.n=s,this.tierValue=o,this.scale=i,this.center=n,this.core=c,this.iterationsSubmitted=0,this.iterationsDoneValue=0,this.settledCountValue=0,this.firstPending=!0,this.statsValue=null,this.writer.reset(),f&&(this.destroyBuffers(),this.overrideList.clear(),this.fixedWords=Bo(s),this.fixedDirty=!1),d?.fixed!==void 0&&d.fixed!==null&&(this.fixedWords.set(d.fixed.subarray(0,Math.ceil(s/32))),this.fixedDirty=!0),s===0||c===null||d===null){this.weightsUpload=null,this.resources=null,this.ready=this.ready.catch(()=>{}),this.settledValue=!0,this.stateValue="loaded";return}const p=f?this.allocate(s):this.requireBuffers();f||this.clearKept(p),this.buffers=p;const g=this.model.kind==="fruchtermanReingold"?"fr":"fa2";gh(e,t,this.optionsValue.seed??null,this.dimValue,i,n,g),this.uploadPositions(p,t,d.mass);const m=this.resolveWeightsBinding(e,c,d.weights),b={...this.model.overrides(this.optionsValue),...Ge(c,l?.perm??null,m)},w=this.makeResources(c,l,m,p);this.resources=w,this.startBind(w,b),this.settledValue=!1,this.stateValue="loaded"}step(e){if(this.stateValue==="created")return Promise.reject(new h("E_NOT_LOADED",`${this.label}: load() first`,{state:"created"}));if(this.stateValue==="disposed")return Promise.reject(rn(this.label));try{this.ctx.assertReady()}catch(n){return Promise.reject(Ot(n))}const{snapshot:t}=this;if(t!==null&&this.ctx.residency.isReleased(t.serial))return Promise.reject(new h("E_RELEASED",`snapshot ${t.serial} was released while ${this.label} used it`,{serial:t.serial}));const s=e??this.optionsValue.iterationsPerStep??At.iterationsPerStep;if(!Number.isInteger(s)||s<1||s>Fe)return Promise.reject(ee("iterations",s,`an integer in [1, ${Fe}]`,`step(${s}): iterations must be an integer in [1, ${Fe}]`));if(this.settledValue||this.n===0)return Promise.resolve();if(this.pending.length>=this.maxInFlight)return this.coalescedValue++,this.pending[0].promise;const i=Sh(s,this.generationValue);return this.pending.push(i),this.submitChain=this.submitChain.then(()=>this.submitBatch(i)),i.promise}async flush(){for(;this.pending.length>0;)await Promise.allSettled(this.pending.map(e=>e.promise))}async run(e){this.assertNotDisposed();const t=e?.batch??8;if(!Number.isInteger(t)||t<1||t>Fe)throw ee("batch",t,`an integer in [1, ${Fe}]`,"run(): batch must be an integer in [1, MAX_ITERATIONS_PER_STEP]");const s=e?.maxIter??this.iterationBudget();if(Number.isNaN(s)||s<0)throw ee("maxIter",s,"a number >= 0","run(): maxIter must be >= 0");const i=e?.signal,n=()=>{this.discardPending()};if(i!==void 0){if(i.aborted)throw this.abortedError();i.addEventListener("abort",n,{once:!0})}try{for(;!this.settled&&this.iterationsDoneValue<s;){if(i?.aborted===!0)throw this.abortedError();const o=Math.min(t,s-this.iterationsDoneValue);await this.step(o)}if(i?.aborted===!0)throw this.abortedError();return this.stats}finally{i?.removeEventListener("abort",n)}}setFixed(e){this.assertLoaded();const{n:t}=this,s=Math.ceil(t/32);if(e.length<s)throw ee("mask",e.length,s,`setFixed: the mask has ${e.length} words, ${s} needed for ${t} nodes`);let i=!1;for(let n=0;n<s&&!i;n++){if(this.fixedWords[n]===e[n])continue;const o=Math.min(t,n*32+32);for(let a=n*32;a<o;a++)if(oi(this.fixedWords,a)&&!oi(e,a)){i=!0;break}}this.fixedWords.set(e.subarray(0,s)),this.fixedDirty=!0,i&&this.reheat()}setPosition(e,t,s,i){this.assertLoaded();const{n}=this;if(!Number.isInteger(e)||e<0||e>=n)throw ee("index",e,`an integer in [0, ${n})`,`setPosition(${e}): index out of range`);if(!Number.isFinite(t)||!Number.isFinite(s)||!Number.isFinite(i))throw ee("position",[t,s,i],"finite coordinates","setPosition: coordinates must be finite");const{owner:o,buffers:a}=this;if(o===null||a===null)throw new h("E_NOT_LOADED",`${this.label}: load() first`,{state:this.stateValue});o[3*e]=t,o[3*e+1]=s,o[3*e+2]=i;const[c,l,d]=this.center,f=new Float32Array([(t-c)/this.scale,(s-l)/this.scale,this.dimValue===2?0:(i-d)/this.scale]);this.ctx.device.queue.writeBuffer(a.positions,16*e,f),this.overrideList.set(e,this.lastSubmittedBatchIdValue),this.reheat()}reheat(){this.assertNotDisposed(),this.iterationsDoneValue=0,this.settledCountValue=0,this.reheatedAfterBatchId=this.lastSubmittedBatchIdValue,this.writer.set("settledCount",0),this.model.onReheat(this.writer),this.settledValue=this.computeSettled()}setParams(e){this.assertNotDisposed();const t=this.optionsValue,{dim:s,maxInFlight:i}=e;if(s!==void 0&&s!==this.dimValue)throw ee("dim",s,this.dimValue,"dim is fixed at construction; graphty-element re-creates the engine on a view-mode switch (spec 7.13)");if(i!==void 0&&i!==this.maxInFlight)throw ee("maxInFlight",i,this.maxInFlight,"maxInFlight is fixed at construction (the uniform ring is sized by it)");const n=ps(e,"nodeSize");if(n!=null)throw new h("E_UNSUPPORTED","nodeSize (adjustSizes) is deferred (spec 7.14)",{option:"nodeSize",hint:"leave nodeSize null; the size correction ships in a later slice"});const o=this.resolveOptions(e,t),a=gs(o),c=ms(o),l=nn(this.model.overrides(t)),d=nn(this.model.overrides(o));this.optionsValue=o,this.scale=a,this.center=c;const{resources:f,core:p}=this;if(this.stateValue==="loaded"&&f!==null&&p!==null&&l!==d){const g={...this.model.overrides(o),...Ge(p,f.tiers?.perm??null,f.weights)};this.startBind(f,g)}this.model.onSetParams(e,this.writer),this.reheat()}dispose(){this.stateValue!=="disposed"&&(this.stateValue="disposed",this.teardown())}async submitBatch(e){try{await this.ready,await this.ctx.allocator.check()}catch(s){if(this.finish(e),this.isStale(e)){e.resolve();return}Er(s,"E_OUT_OF_MEMORY")&&this.outOfMemory(),e.reject(Ot(s));return}if(this.isStale(e)){this.finish(e),e.resolve();return}let t;try{t=this.recordAndSubmit(e)}catch(s){this.finish(e),e.reject(Ot(s));return}t.readback.then(s=>{this.onLanded(e,s)},s=>{this.onFailed(e,s)})}recordAndSubmit(e){const t=this.requireBuffers(),{k:s}=e,{device:i}=this.ctx;this.fixedDirty&&(i.queue.writeBuffer(t.fixed,0,this.fixedWords),this.fixedDirty=!1),this.flushStateWrites(t.state);const n=this.ring.reserve(s);for(let g=0;g<s;g++){const m=g===0&&this.firstPending?ys:0;this.ring.write(n+g,this.model.params,this.paramsForSlot(this.iterationsSubmitted+g,g,m))}this.ring.flush();const o=new ne(this.ctx,`${this.label}/batch`,this.generationValue),a=this.lastIterationStage();for(let g=0;g<s;g++)this.model.recordIteration(o,n+g,this.tierValue,g===s-1?void 0:a);o.endPass();const c=o.readback(t.scene,0,12*this.n),l=o.readback(t.state,0,He+s*this.traceBytes),{profiler:d}=this.ctx,f=d===null?null:d.resolveInto(o),p=o.submit();return e.submitted=p,e.id=p.id,e.sceneOffset=c.offset,e.stateOffset=l.offset,e.profile=f,e.startedAt=performance.now(),this.lastSubmittedBatchIdValue=p.id,this.iterationsSubmitted+=s,this.firstPending=!1,p}onLanded(e,t){this.finish(e);const{owner:s}=this;if(this.isStale(e)||s===null){e.resolve();return}try{const{k:i}=e,{n}=this,o=Math.max(e.sceneOffset+12*n,e.stateOffset+He+i*this.traceBytes);if(t.byteLength<o){e.resolve();return}this.copyScene(s,t,e);const a=new DataView(t,e.stateOffset,He),c=new DataView(t,e.stateOffset+He,i*this.traceBytes);this.writer.absorb(a);const l=this.model.state.readField(a,"settledCount");if(typeof l!="number")throw ee("settledCount",l,"a scalar","settledCount must be a scalar field");const d=this.batchMilliseconds(e,t);this.statsValue=on(this.model.readStats(a,c),d/i),this.iterationsDoneValue+=i,this.settledCountValue=e.id<=this.reheatedAfterBatchId?0:l,this.settledValue=this.computeSettled(),e.resolve()}catch(i){e.reject(Ot(i))}}onFailed(e,t){if(this.finish(e),Er(t,"E_ABORTED")&&this.isStale(e)){e.resolve();return}if(this.ctx.state==="lost"&&!Er(t,"E_DEVICE_LOST")){e.reject(new h("E_DEVICE_LOST","the device was lost while the batch was in flight",{reason:"unknown",message:Ot(t).message}));return}e.reject(Ot(t))}copyScene(e,t,s){const{n:i}=this,n=new Float32Array(t,s.sceneOffset,3*i);if(this.overrideList.size===0){e.set(n);return}for(let o=0;o<i;o++){const a=this.overrideList.get(o);a!==void 0&&a>=s.id||(e[3*o]=n[3*o],e[3*o+1]=n[3*o+1],e[3*o+2]=n[3*o+2])}for(const[o,a]of this.overrideList)a<s.id&&this.overrideList.delete(o)}batchMilliseconds(e,t){const{profiler:s}=this.ctx;if(s!==null&&e.profile!==null){const i=s.timings(t,e.profile);if(this.lastPassTimingsValue=i,i.length>0&&!s.partial(e.profile)){let n=0;for(const o of i)n+=o.ns;return n/1e6}}return performance.now()-e.startedAt}isStale(e){return e.stale||this.stateValue!=="loaded"||e.generation!==this.generationValue}finish(e){const t=this.pending.indexOf(e);t>=0&&this.pending.splice(t,1)}discardPending(){for(const e of this.pending)e.stale=!0,e.submitted?.discard()}outOfMemory(){this.discardPending(),this.destroyBuffers(),this.ctx.allocator.reset(),this.settledValue=!1,this.stateValue="created"}abortedError(){return new h("E_ABORTED",`${this.label}: run() aborted by the signal`,{batchId:this.lastSubmittedBatchIdValue})}paramsForSlot(e,t,s){const[i,n,o]=this.center,a=this.model.paramsFor(e,this.optionsValue),c=typeof a.flags=="number"?a.flags:0,l={n:this.n,dim:this.dimValue,flags:s|c,iterationIndex:t,seed:this.seedU32(),scale:this.scale,center:[i,n,o,0],settleThreshold:this.settleThreshold()};return{...a,...l}}seedU32(){const{seed:e}=this.optionsValue;return e==null||!Number.isFinite(e)?0:Math.floor(Math.abs(e))%4294967296}flushStateWrites(e){const t=this.writer.takeQueued();if(t.length===0)return;const s=this.writer.headerBytes(),i=this.model.state;for(const n of t){const o=i.offsetOf(n),a=4*Ks(Ao(i,n));this.ctx.device.queue.writeBuffer(e,o,s,o,a)}}lastIterationStage(){const e=this.model.stages.indexOf(yh);if(!(e<=0))return this.model.stages[e-1]}settleThreshold(){return this.optionsValue.settleThreshold??At.settleThreshold}settleWindow(){return this.optionsValue.settleWindow??At.settleWindow}iterationBudget(){const e=ps(this.optionsValue,"maxIter");if(typeof e=="number")return e;const t=ps(this.optionsValue,"iterations");return typeof t=="number"?t:Number.POSITIVE_INFINITY}computeSettled(){return this.stateValue!=="loaded"?!1:this.n===0||this.iterationsDoneValue>=this.iterationBudget()||this.settledCountValue>=this.settleWindow()}allocate(e){const{allocator:t}=this.ctx,{label:s}=this,i=Math.ceil(e/this.ctx.workgroupSize),n=Fe*this.traceBytes,o=ie.STORAGE|ie.COPY_SRC|ie.COPY_DST,a=t.createBuffer({label:`${s}/positions`,size:16*e,usage:o}),c=t.createBuffer({label:`${s}/scenePositions`,size:12*e,usage:ie.STORAGE|ie.COPY_SRC}),l=t.createBuffer({label:`${s}/fixed`,size:4*Math.ceil(e/32),usage:o}),d=t.createBuffer({label:`${s}/partials`,size:ui*i,usage:o}),f=t.createBuffer({label:`${s}/state`,size:He+n,usage:o}),p=new Map;p.set("positions",ir(a,16*e)),p.set("scenePositions",ir(c,12*e)),p.set("fixed",ir(l,4*Math.ceil(e/32))),p.set("partials",ir(d,ui*i)),p.set("state",{buffer:f,offset:0,size:He,window:null}),p.set("trace",{buffer:f,offset:He,size:n,window:null});const g=new Map;for(const m of this.model.buffers(e,this.dimValue)){if(bh.has(m.name)||p.has(m.name))throw ee("BufferSpec.name",m.name,"a name no other buffer uses",`BufferSpec "${m.name}" reuses a buffer name`);if(!Number.isInteger(m.byteLength)||m.byteLength<=0||m.byteLength%4!==0)throw ee("BufferSpec.byteLength",m.byteLength,"a positive multiple of 4",`BufferSpec "${m.name}" has byteLength ${m.byteLength}`);if(m.zero&&(m.usage&ie.COPY_DST)===0)throw ee("BufferSpec.usage",m.usage,"COPY_DST on a zero: true spec",`BufferSpec "${m.name}" is zero: true but lacks COPY_DST`);const b=t.createBuffer({label:`${s}/${m.name}`,size:m.byteLength,usage:m.usage});g.set(m.name,{buffer:b,spec:m}),p.set(m.name,ir(b,m.byteLength))}return{positions:a,scene:c,fixed:l,partials:d,state:f,model:g,bindings:p,traceRegionBytes:n}}clearKept(e){const{device:t}=this.ctx,s=t.createCommandEncoder({label:`${this.label}/clear`});s.clearBuffer(e.partials),s.clearBuffer(e.state,He,e.traceRegionBytes);for(const{buffer:i,spec:n}of e.model.values())n.zero&&s.clearBuffer(i);t.queue.submit([s.finish()])}destroyBuffers(){const{buffers:e}=this;if(e===null)return;this.buffers=null,this.resources=null;const t=[e.positions,e.scene,e.fixed,e.partials,e.state];for(const{buffer:s}of e.model.values())t.push(s);this.afterInFlight(()=>{const{allocator:s}=this.ctx;for(const i of t)s.destroy(i)})}afterInFlight(e){const t=[];for(const s of this.pending)s.submitted!==null&&t.push(s.submitted.readback.catch(()=>{}));if(t.length===0){e();return}Promise.all(t).then(e,e)}requireBuffers(){const{buffers:e}=this;if(e===null)throw new h("E_NOT_LOADED",`${this.label}: load() first`,{state:this.stateValue});return e}uploadPositions(e,t,s){const{n:i,scale:n}=this,[o,a,c]=this.center,l=new Float32Array(4*i);for(let P=0;P<i;P++)l[4*P]=(t[3*P]-o)/n,l[4*P+1]=(t[3*P+1]-a)/n,l[4*P+2]=this.dimValue===2?0:(t[3*P+2]-c)/n,l[4*P+3]=s[P];this.ctx.device.queue.writeBuffer(e.positions,0,l);let d=0,f=0,p=0;const g=[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY],m=[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY];for(let P=0;P<i;P++){const E=l[4*P],A=l[4*P+1],L=l[4*P+2];d+=E,f+=A,p+=L,g[0]=Math.min(g[0],E),g[1]=Math.min(g[1],A),g[2]=Math.min(g[2],L),m[0]=Math.max(m[0],E),m[1]=Math.max(m[1],A),m[2]=Math.max(m[2],L)}const b=d/i,w=f/i,v=p/i;let y=0,I=0;for(let P=0;P<i;P++){const E=l[4*P]-b,A=l[4*P+1]-w,L=l[4*P+2]-v,B=E*E+A*A+L*L;y+=B,I=Math.max(I,B)}const{writer:S}=this;S.set("iteration",0),S.set("settledCount",0),S.set("meanDisplacement",0),S.set("centroid",[b,w,v,0]),S.set("min",[g[0],g[1],g[2],0]),S.set("max",[m[0],m[1],m[2],0]),S.set("rmsRadius",Math.sqrt(y/i)),S.set("radius",Math.sqrt(I)),this.model.onLoad(S),this.ctx.device.queue.writeBuffer(e.state,0,S.headerBytes()),S.clearQueued()}resolveWeightsBinding(e,t,s){if(s.source==="arcs")return this.weightsUpload=null,t.weights;if(s.source==="none"||s.data===null||s.column===null)return this.weightsUpload=null,null;const i=this.weightsUpload,o=i!==null&&i.serial===e.serial&&i.column===s.column&&i.version===s.column.version&&i.data.length===s.data.length&&i!==null?i.data:s.data,a=this.ctx.residency.array(o,"weights",e);return this.weightsUpload={serial:e.serial,column:s.column,version:s.column.version,data:o,upload:a},a.binding}makeResources(e,t,s,i){const{bindings:n}=i;return{device:this.ctx.device,caps:this.ctx.caps,pipelines:this.ctx.pipelines,core:e,tiers:t,weights:s,n:this.n,dim:this.dimValue,tier:this.tierValue,ring:this.ring,pool:this.ctx.pool,buffer:o=>{const a=n.get(o);if(a===void 0)throw ee("name",o,[...n.keys()],`no simulation buffer named "${o}"`);return a}}}startBind(e,t){const s=this.generationValue,i=()=>this.compileAndBind(e,t,s),n=this.ready.catch(()=>{}).then(i);n.catch(()=>{}),this.ready=n}async compileAndBind(e,t,s){await Ve(this.ctx);const i=this.ctx.caps.features.has("subgroups");await this.ctx.pipelines.warm(this.model.specs(t,i)),!(this.stateValue!=="loaded"||s!==this.generationValue)&&await this.model.bind(e,t)}async inspectBuffer(e){this.assertLoaded();const{buffers:t}=this,s=t?.bindings.get(e);if(t===null||s===void 0)throw ee("name",e,t===null?[]:[...t.bindings.keys()],`inspect("${e}"): no such buffer`);await this.flush();const i=await this.ctx.readback.read(s.buffer,s.size,void 0,s.offset);return vh.has(e)?new Uint32Array(i):new Float32Array(i)}async runStages(e){if(this.assertLoaded(),!this.model.stages.includes(e))throw ee("upTo",e,[...this.model.stages],`debugRunStages("${e}"): not a stage of ${this.model.kind}`);if(this.n===0)return;await this.flush(),await this.ready,await this.ctx.allocator.check(),this.assertLoaded();const t=this.requireBuffers(),{device:s}=this.ctx;this.fixedDirty&&(s.queue.writeBuffer(t.fixed,0,this.fixedWords),this.fixedDirty=!1),this.flushStateWrites(t.state);const i=this.ring.reserve(1);this.ring.write(i,this.model.params,this.paramsForSlot(this.iterationsSubmitted,0,this.firstPending?ys:0)),this.ring.flush();const n=new ne(this.ctx,`${this.label}/debug`,this.generationValue);this.model.recordIteration(n,i,this.tierValue,e),n.endPass(),n.readback(t.state,0,He);const o=n.submit();this.lastSubmittedBatchIdValue=o.id,await o.readback}onDeviceLost(e){if(this.stateValue==="disposed")return;const t=new h("E_DEVICE_LOST",`device lost (${e.reason}): ${e.message}`,{reason:e.reason,message:e.message}),s=this.pending.splice(0);this.stateValue="disposed";for(const i of s)i.stale=!0,i.reject(t);this.teardown()}teardown(){this.torndown||(this.torndown=!0,this.discardPending(),this.destroyBuffers(),this.afterInFlight(()=>{this.ring.destroy(),this.model.dispose?.()}),this.ctx.state==="ready"&&this.ctx.pool.trim(),this.unregisterLost(),this.core=null,this.snapshot=null,this.owner=null,this.weightsUpload=null)}assertNotDisposed(){if(this.stateValue==="disposed")throw rn(this.label)}assertLoaded(){if(this.stateValue==="created")throw new h("E_NOT_LOADED",`${this.label}: load() first`,{state:"created"});this.assertNotDisposed()}}async function Ys(r,e){const{n:t,tiers:s,pipelines:i,caps:n}=r,o=s?.segmentOffsets,a=o?.[1]??0,c=o?.[2]??0,l=[{tier:2,rows:a},{tier:1,rows:c-a},{tier:0,rows:t-c}],d=[];for(const{tier:f,rows:p}of l){if(f!==0&&p<=0)continue;const g=await i.kernel(_("fa2-attraction",{...e,TIER:f})),m=g.workgroupSize;if(f!==0&&m<lt)throw new h("E_UNSUPPORTED",`fa2-attraction: the mid tier folds ${lt} lanes per row, more than the workgroup size ${m}`,{feature:"fa2-attraction.tiers"});if(p<=0)continue;let b=m;f===2?b=1:f===1&&(b=m/lt),d.push([g,z(p,b,n)])}return{kernels:d}}function Eh(r,e,t){const{core:s,tiers:i,weights:n}=e,o={...Ze(s,i?.perm??null,n),pos:t.pos,force:t.force,P:t.params};return{kernels:r.kernels.map(([a,c])=>[a,a.bind(o),c])}}function Ih(r,e,t){for(const[s,i,n]of e.kernels)s.dispatch(r,i,n,[t])}const kh=12,an="fillParams",Ah=4294967296;function qr(r){return r===null?"null":typeof r=="number"||typeof r=="boolean"||typeof r=="string"?String(r):r===void 0?"undefined":typeof r=="object"&&"length"in r&&typeof r.length=="number"?`[${r.length} values]`:typeof r}function Re(r,e,t){return new h("E_INVALID_ARGUMENT",`${r} must be ${t}; got ${qr(e)}`,{argument:r,value:e,expected:t})}function re(r,e,t,s,i){const n=e===void 0?t:e;if(typeof n!="number"||!Number.isFinite(n)||!s(n))throw Re(r,n,i);return n}function or(r,e,t){const s=e===void 0?t:e;if(typeof s!="boolean")throw Re(r,s,"a boolean");return s}function Xs(r,e){const t=r===void 0?e:r;if(t!==2&&t!==3)throw Re("dim",t,"2 or 3");return t}function Qs(r,e){if(r===void 0)return e;const t="an array of 2 or 3 finite numbers",s=r;if(typeof s!="object"||s===null||!("length"in s))throw Re("center",r,t);const{length:i}=s;if(i!==2&&i!==3)throw Re("center",r,t);const n=r[0],o=r[1],a=i===3?r[2]:0;if(typeof n!="number"||typeof o!="number"||typeof a!="number"||!Number.isFinite(n)||!Number.isFinite(o)||!Number.isFinite(a))throw Re("center",r,t);return[n,o,a]}function Zs(r,e){if(r===void 0)return e;const t=r;if(t!==null&&(typeof t!="number"||!Number.isFinite(t)))throw Re("seed",r,"a finite number or null");return t}function Me(r){return Number.isInteger(r)&&r>=1}function xh(r){return r===null?0:Math.floor(Math.abs(r))%Ah}function he(r,e){const t=r[e];if(typeof t!="number")throw Re(e,t,"a scalar field");return t}function Rh(r,e){const t=r[e];if(typeof t=="number")throw Re(e,t,"a vector field");return t}function _e(r,e){const t={};for(const s of Object.keys(e))t[s]=s in r?r[s]:e[s];return t}function xo(r,e,t,s,i,n){const o=Math.max(1,Math.ceil(Math.ceil(i/r.workgroupSize)/En));for(let a=1;a<=o;a++)r.dispatch(e,t,{...s,z:a},[n])}class ur{constructor(e,t,s,i){this.boundRepulsion=null,this.boundSpeedFinalize=null,this.repulsion=e,this.speedFinalize=t,this.caps=s,this.overrides=Object.freeze({SWING_MODE:i.SWING_MODE,STRONG_GRAVITY:i.STRONG_GRAVITY,GRAVITY_CENTER:i.GRAVITY_CENTER}),this.finalizePlan=z(t.workgroupSize,t.workgroupSize,s)}static async create(e,t,s){const[i,n]=ur.specs(s),o=await e.kernel(i),a=await e.kernel(n);return new ur(o,a,t,s)}static specs(e){return[_("fa2-repulsion-exact",{SWING_MODE:e.SWING_MODE,STRONG_GRAVITY:e.STRONG_GRAVITY,GRAVITY_CENTER:e.GRAVITY_CENTER}),_("fa2-speed-finalize",{SWING_MODE:e.SWING_MODE})]}bind(e){this.boundRepulsion=this.repulsion.bind({pos:e.pos,S:e.state,force:e.force,oldForce:e.oldForce,fixedMask:e.fixedMask,partials:e.partials,P:e.params}),this.boundSpeedFinalize=this.speedFinalize.bind({partials:e.partials,S:e.state,T:e.trace,P:e.params})}record(e,t,s){this.recordRepulsion(e,t,s),this.recordSpeedFinalize(e,s)}recordRepulsion(e,t,s){const i=this.bound(this.boundRepulsion,"recordRepulsion"),n=z(t,this.repulsion.workgroupSize,this.caps);xo(this.repulsion,e,i,n,t,s)}recordSpeedFinalize(e,t){const s=this.bound(this.boundSpeedFinalize,"recordSpeedFinalize");this.speedFinalize.dispatch(e,s,this.finalizePlan,[t])}bound(e,t){if(e===null)throw new h("E_NOT_LOADED",`RepulsionExact.${t}(): bind() has not been called for this stage`,{state:"unbound"});return e}}async function Lh(r,e){const t=await r.pipelines.kernel(_("grid-centroid")),s=await r.pipelines.kernel(_("grid-centroid-hub")),i=await r.pipelines.kernel(_("grid-downsample"));return new Dh(r,e,{centroid:t,hub:s,downsample:i})}class Dh{constructor(e,t,s){this.bound=null,this.dispatches=0,this.scope=e,this.spec=t,this.kernels=s}get lastDispatches(){return this.dispatches}bind(e){const{centroid:t,hub:s,downsample:i}=this.kernels,{spec:n,scope:o}=this,a=e,c=[];let l=n.g;for(let d=0;d+1<n.levels;d++){l/=2;const f=l**n.dim,p=o.params(Un,{childBase:n.levelOffsets[d],parentBase:n.levelOffsets[d+1],parentSide:l,parentCells:f,depth:n.dim===3?2:1,pad0:0,pad1:0,pad2:0});c.push({bound:i.bind({pyramid:a.pyramid,P:p.binding}),offset:p.offset,parentCells:f})}this.bound={centroid:t.bind({sortedIdx:a.sortedIdx,cellStart:a.cellStart,pos:a.pos,pyramid:a.pyramid,hubList:a.hubList,hubCounters:a.hubCounters,P:a.params}),hub:s.bind({sortedIdx:a.sortedIdx,cellStart:a.cellStart,pos:a.pos,pyramid:a.pyramid,hubList:a.hubList,hubCount:a.hubCounters,P:a.params}),hubSlots:Math.floor(a.hubList.size/4),levels:c}}record(e,t,s){const{bound:i,scope:n,spec:o}=this;if(i===null)throw new h("E_NOT_LOADED","gridPyramid: record() before bind()",{argument:"bind"});const{centroid:a,hub:c,downsample:l}=this.kernels,d=o.cells+o.outsideCells;if(a.dispatch(e,i.centroid,z(d,n.workgroupSize,n.caps),[t]),c.dispatch(e,i.hub,Ft(i.hubSlots,n.caps),[t]),this.dispatches=2,(s??"G5")!=="G4")for(const f of i.levels)l.dispatch(e,f.bound,z(f.parentCells,n.workgroupSize,n.caps),[f.offset]),this.dispatches+=1}}const Th=["G1","G2","G3","G4","G5","G6","G7"],vt=ie.STORAGE|ie.COPY_SRC|ie.COPY_DST;function Pr(r){if(r.lease===null)throw new h("E_DISPOSED","RepulsionGrid: the stage was disposed",{label:"RepulsionGrid"});return r.lease}function Oh(r,e,t,s){const i=e.uniform(t.byteLength,`grid/${t.name}`),n=new ArrayBuffer(t.byteLength);return t.write(new DataView(n),s),r.queue.writeBuffer(i,0,n),{binding:{buffer:i,offset:0,size:t.byteLength,window:null},offset:0}}function cn(r){return _("grid-far-field",{LAW:r.LAW})}function ln(r){return _("grid-near-field",{SWING_MODE:r.SWING_MODE,STRONG_GRAVITY:r.STRONG_GRAVITY,GRAVITY_CENTER:r.GRAVITY_CENTER,LAW:r.LAW})}class ft{constructor(e,t,s,i,n,o,a){this.bound=null,this.caps=e.caps,this.box=n,this.workgroupSize=t,this.overrides=Object.freeze({SWING_MODE:s.SWING_MODE,STRONG_GRAVITY:s.STRONG_GRAVITY,GRAVITY_CENTER:s.GRAVITY_CENTER,LAW:s.LAW}),this.spec=i,this.build=o.build,this.pyramid=o.pyramid,this.far=a.far,this.near=a.near,this.speedFinalize=a.speedFinalize}static buffers(e,t){const s=4*Math.max(1,e);return[{name:"cellKey",byteLength:s,usage:vt,zero:!1},{name:"cellVal",byteLength:s,usage:vt,zero:!1},{name:"sortedKey",byteLength:s,usage:vt,zero:!1},{name:"sortedIdx",byteLength:s,usage:vt,zero:!1},{name:"cellHist",byteLength:4*t.histWords,usage:vt,zero:!0},{name:"cellStart",byteLength:4*t.histWords,usage:vt,zero:!0},{name:"hubList",byteLength:4*Math.max(1,Math.ceil(e/An)),usage:vt,zero:!1},{name:"pyramid",byteLength:ch(t),usage:vt,zero:!0}]}static specs(e,t){const s=t.deterministic?[_("radix-hist"),_("radix-scatter"),_("scan-block"),_("scan-add")]:[_("counting-scatter"),_("scan-block"),_("scan-add")];return[_("grid-cell-key"),...s,_("histogram"),_("fill"),_("grid-centroid"),_("grid-centroid-hub"),_("grid-downsample"),cn(e),ln(e),_("fa2-speed-finalize",{SWING_MODE:e.SWING_MODE})]}static async create(e,t,s,i){const n={lease:e.pool.lease()},o={device:e.device,caps:e.caps,pipelines:e.pipelines,pool:e.pool,workgroupSize:t,scratch:(a,c)=>Pr(n).storage(a,c),params:(a,c)=>Oh(e.device,Pr(n),a,c)};try{const a=await lh(o,i),c=await Lh(o,i),l=await e.pipelines.kernel(cn(s)),d=await e.pipelines.kernel(ln(s)),f=await e.pipelines.kernel(_("fa2-speed-finalize",{SWING_MODE:s.SWING_MODE}));return new ft(e,t,s,i,n,{build:a,pyramid:c},{far:l,near:d,speedFinalize:f})}catch(a){throw Pr(n).release(),a}}bind(e){Pr(this.box);const t=e;this.build.bind({pos:t.pos,state:t.state,params:t.params,cellKey:t.cellKey,cellVal:t.cellVal,sortedKey:t.sortedKey,sortedIdx:t.sortedIdx,cellHist:t.cellHist,cellStart:t.cellStart}),this.pyramid.bind({pos:t.pos,params:t.params,sortedIdx:t.sortedIdx,cellStart:t.cellStart,pyramid:t.pyramid,hubList:t.hubList,hubCounters:t.hubCounters}),this.bound={far:this.far.bind({pos:t.pos,sortedIdx:t.sortedIdx,pyramid:t.pyramid,S:t.state,force:t.force,P:t.params}),near:this.near.bind({pos:t.pos,sortedIdx:t.sortedIdx,cellStart:t.cellStart,S:t.state,force:t.force,oldForce:t.oldForce,fixedMask:t.fixedMask,partials:t.partials,P:t.params}),speedFinalize:this.speedFinalize.bind({partials:t.partials,S:t.state,T:t.trace,P:t.params})}}recordRepulsion(e,t,s,i){const n=this.requireBound("recordRepulsion"),o=Th.indexOf(i??"G7");let a="G3";if(o===0?a="G1":o===1&&(a="G2"),this.build.record(e,t,s,a),o<3||(this.pyramid.record(e,s,o===3?"G4":"G5"),o<5))return;const c=z(t,this.workgroupSize,this.caps);this.far.dispatch(e,n.far,c,[s]),!(o<6)&&this.near.dispatch(e,n.near,c,[s])}recordSpeedFinalize(e,t){const s=this.requireBound("recordSpeedFinalize"),i=z(this.speedFinalize.workgroupSize,this.speedFinalize.workgroupSize,this.caps);this.speedFinalize.dispatch(e,s.speedFinalize,i,[t])}dispose(){this.box.lease!==null&&(this.box.lease.release(),this.box.lease=null);for(const e of[this.far,this.near,this.speedFinalize])e.invalidate();this.bound=null}requireBound(e){if(this.bound===null)throw new h("E_NOT_LOADED",`RepulsionGrid.${e}(): bind() has not been called for this stage`,{state:"unbound"});return this.bound}}const nr=["K1","K2","K3","G1","G2","G3","G4","G5","G6","G7","K4","K5","toScene"],Nh=3,ws=10,un=11,bs=12,dn="hubCounters",Bh={x:1,y:1,z:1,items:1,stride:null},$r={STATS_MODE:0},Cr={LINLOG:!1,DISTRIBUTED:!1,TIER:0,USE_PERM:!1,HAS_WEIGHTS:!1,LAW:0},Mr={SWING_MODE:0,STRONG_GRAVITY:!1,GRAVITY_CENTER:0,LAW:0},Gr={SWING_MODE:0,APPLY:0};class Kr{constructor(e,t,s){this.kernel=e,this.bound=t,this.plan=s}static binder(e){return({pos:t,state:s,force:i,oldForce:n,fixedMask:o,partials:a,params:c},l)=>new Kr(e,e.bind({pos:t,S:s,force:i,oldForce:n,fixedMask:o,partials:a,P:c}),l)}recordRepulsion(e,t,s){xo(this.kernel,e,this.bound,this.plan,t,s)}invalidate(){this.kernel.invalidate()}}class Js{constructor(e,t){this.names=t,this.stages=nr,this.params=Ue,this.state=et,this.trace=Yt,this.resources=null,this.nextGrid=null,this.resetCarry=!1,this.bound=null,this.openPass=null,this.tuning=e}zeroesCarry(){return!1}recordSpeed(e,t,s){}buffers(e,t){const s=Math.max(1,e)*kh,i=ie.STORAGE|ie.COPY_SRC|ie.COPY_DST,n=hr(this.tuning,e)==="grid"?ft.buffers(e,ut(e,t,this.tuning)):[];return[{name:"force",byteLength:s,usage:i,zero:!0},{name:this.names.carry,byteLength:s,usage:i,zero:!0},{name:an,byteLength:Ye,usage:ie.UNIFORM|ie.COPY_DST,zero:!1},{name:dn,byteLength:16,usage:i,zero:!0},...n]}async bind(e,t){this.dropBound(),this.resources=e;const{n:s,caps:i,core:n,ring:o,device:a}=e;if(s===0)return;const c=e.buffer("positions"),l=e.buffer("force"),d=o.binding(Ue),f=n.colIdx!==null,p=await this.compile(e,t),{k1:g,k5:m,toScene:b,fill:w,grid:v}=p;if(this.resources!==e){v?.dispose();return}const y=e.buffer("scenePositions"),I=e.buffer("fixed"),S=e.buffer("partials"),P=e.buffer("state"),E=e.buffer("trace"),A=e.buffer(this.names.carry),L=e.buffer(an),B={buffer:L.buffer,offset:L.offset,size:Ae.byteLength,window:null},D=new ArrayBuffer(Ae.byteLength);Ae.write(new DataView(D),{count:3*s,value:0,mode:0}),a.queue.writeBuffer(L.buffer,L.offset,D);const x=e.buffer(dn),T={pos:c,state:P,trace:E,force:l,oldForce:A,fixedMask:I,partials:S,params:d};v?.bind({...T,cellKey:e.buffer("cellKey"),cellVal:e.buffer("cellVal"),sortedKey:e.buffer("sortedKey"),sortedIdx:e.buffer("sortedIdx"),cellHist:e.buffer("cellHist"),cellStart:e.buffer("cellStart"),hubList:e.buffer("hubList"),hubCounters:x,pyramid:e.buffer("pyramid")});const k=g.workgroupSize,$=z(s,k,i);this.bound={n:s,plan:$,fillPlan:z(3*s,k,i),k1:g,k1Bound:g.bind({partials:S,S:P,T:E,cellHist:v===null?S:e.buffer("cellHist"),hubCounters:x,P:d}),attraction:p.attraction===null?null:Eh(p.attraction,e,{pos:c,force:l,params:d}),exact:p.exact?.(T,$)??null,grid:v,k5:m,k5Bound:m.bind({force:l,oldForce:A,fixedMask:I,S:P,pos:c,partials:S,P:d}),toScene:b,toSceneBound:b.bind({pos:c,scene:y,P:d}),fill:w,fillForceBound:f?null:w.bind({dst:l,P:B}),fillCarryBound:this.zeroesCarry()?w.bind({dst:A,P:B}):null}}recordIteration(e,t,s,i){const n=this.requireResources(),o=i===void 0?bs:this.stageIndex(i),{bound:a}=this;if(a===null){if(n.n===0)return;throw new h("E_NOT_LOADED",`the ${this.names.name} model is not bound (bind() has not completed)`,{state:"loaded"})}const c=n.ring.offsetOf(t);if(s==="grid"){this.recordGridIteration(e,a,c,o);return}const{exact:l}=a;if(l===null)throw new h("E_NOT_LOADED",`the ${this.names.name} model was bound on the grid tier`,{state:"loaded"});const d=this.openPass!==null&&this.openPass.id===e.id?this.openPass.pass:e.pass(this.names.pass);this.openPass={id:e.id,pass:d},this.recordK1(d,a,c),!(o<1)&&(this.recordK2(d,a,c),!(o<2)&&(l.recordRepulsion(d,a.n,c),!(o<ws)&&(this.recordSpeed(d,l,c),!(o<un)&&(a.k5.dispatch(d,a.k5Bound,a.plan,[c]),!(o<bs)&&this.recordToScene(e,a,c)))))}recordGridIteration(e,t,s,i){const{grid:n}=t;if(n===null)throw new h("E_NOT_LOADED",`the ${this.names.name} model was bound on the exact tier`,{state:"loaded"});if(this.openPass=null,this.recordK1(e.pass(`${this.names.pass}-k1`),t,s),i<1||(this.recordK2(e.pass(`${this.names.pass}-attraction`),t,s),i<Nh))return;const o=e.pass(`${this.names.pass}-grid`),a=i<ws?nr[i]:void 0;n.recordRepulsion(o,t.n,s,a),!(i<ws)&&(this.recordSpeed(o,n,s),!(i<un)&&(t.k5.dispatch(o,t.k5Bound,t.plan,[s]),!(i<bs)&&this.recordToScene(e,t,s)))}recordK1(e,t,s){this.resetCarry&&(this.resetCarry=!1,t.fillCarryBound!==null&&t.fill.dispatch(e,t.fillCarryBound,t.fillPlan,[0])),t.k1.dispatch(e,t.k1Bound,Bh,[s])}recordK2(e,t,s){t.attraction!==null?Ih(e,t.attraction,s):t.fillForceBound!==null&&t.fill.dispatch(e,t.fillForceBound,t.fillPlan,[0])}recordToScene(e,t,s){this.openPass=null;const i=e.pass(this.names.scenePass);t.toScene.dispatch(i,t.toSceneBound,t.plan,[s])}sharedParams(e,t){const{n:s,core:i,tiers:n,tier:o,dim:a}=this.requireResources(),{nearMax:c,extentFactor:l}=this.tuning,d=o==="grid"?ut(s,a,this.tuning):null,f=n?.segmentOffsets,p=f?.[1]??0,g=f?.[2]??0;return{n:s,dim:t.dim,flags:0,tierStart:g,tierEnd:s,iterationIndex:e,seed:xh(t.seed),nearMax:c,scale:t.scale,center:[t.center[0],t.center[1],t.center[2],0],settleThreshold:t.settleThreshold,extentFactor:l,gridMax:d?.g??0,levels:d?.levels??0,arcBase:0,arcEnd:Wr(i),accumulate:0,hiEnd:p,midEnd:g}}sharedStats(e){const t=Rh(e,"centroid"),s=this.resources?.tier==="grid";return{iteration:he(e,"iteration"),meanDisplacement:he(e,"meanDisplacement"),rmsRadius:he(e,"rmsRadius"),layoutRadius:he(e,"radius"),centroid:[t[0],t[1],t[2]],repulsionTier:s?"grid":"exact",maxCellOccupancy:s?he(e,"maxCellOccupancy"):null,outsideGrid:s?he(e,"outsideGrid"):null,msPerIteration:null}}requireResources(){if(this.resources===null)throw new h("E_NOT_LOADED",`the ${this.names.name} model has not been bound (load() first)`,{state:"created"});return this.resources}stageIndex(e){for(let t=0;t<nr.length;t++)if(nr[t]===e)return t;throw Re("upTo",e,nr.join(" | "))}dispose(){this.dropBound()}dropBound(){this.openPass=null;const{bound:e}=this;if(e!==null){for(const t of[e.k1,e.k5,e.toScene,e.fill])t.invalidate();e.exact?.invalidate?.();for(const[t]of e.attraction?.kernels??[])t.invalidate();e.grid?.dispose(),this.bound=null}}}const fn="write a role 'mass' node column (nodes.set(name, vec, { role: 'mass', replaceRole: true })) or pass a Float32Array; @graphty/layout's resolveNodeVector does this on the CPU path";function it(r,e,t,s){return new h("E_INVALID_ARGUMENT",s,{argument:r,value:e,expected:t})}function Ds(r,e){for(let t=0;t<r.length;t++){const s=r[t];if(!Number.isFinite(s)||s<=0)throw new h("E_INVALID_ARGUMENT",`${e}: mass[${t}] = ${s} is not a finite number > 0`,{argument:"nodeMass",value:s,index:t,expected:"finite masses > 0"})}}function $h(r){const e=r.nodeCount,t=r.outDegree(),s=new Float32Array(e);for(let i=0;i<e;i++)s[i]=t[i]+1;return s}function hn(r,e){const{name:t}=e.meta;if(e.meta.components!==1)throw it("nodeMass",t,"a one-component numeric node column",`node column "${t}" has ${e.meta.components} components; a mass column has one`);let s;switch(e.dtype){case"f32":case"f64":{const n=r.nodes.gpuView(t),o=n instanceof Float32Array?n:new Float32Array(n);return Ds(o,`node column "${t}"`),o}case"u32":case"i32":s=r.nodes.gpuView(t);break;case"u8":s=e.data;break;default:throw it("nodeMass",t,"a numeric node column (f32, f64, u32, i32, u8)",`node column "${t}" is ${e.dtype}, not numeric`)}const i=new Float32Array(r.nodeCount);for(let n=0;n<i.length;n++)i[n]=s[n];return Ds(i,`node column "${t}"`),i}function Ch(r,e){const t=r.nodeCount;if(e==null){const s=r.nodes.byRole("mass");return s===null?$h(r):hn(r,s)}if(e instanceof Float32Array){if(e.length!==t)throw it("nodeMass",e.length,`${t} values`,`nodeMass has ${e.length} values, expected ${t}`);return Ds(e,"nodeMass"),e}if(typeof e=="string"){const s=r.nodes.get(e);if(s===null)throw it("nodeMass",e,"an existing numeric node column",`nodeMass names node column "${e}", which the snapshot does not hold`);return hn(r,s)}throw ArrayBuffer.isView(e)?it("nodeMass","typed array","a Float32Array","nodeMass must be a Float32Array, a column name or null"):new h("E_UNSUPPORTED",`nodeMass as a Record is not supported on the GPU path: ${fn}`,{option:"nodeMass",hint:fn})}function Mh(r,e){if(e===!0)return{data:r.weights,source:"arcs",column:null};if(e===!1||e===null||e===void 0)return{data:null,source:"none",column:null};if(typeof e!="string")throw it("weight",e,"true, false, null or an edge column name","weight must be true, false, null or an edge column name");const t=r.edges.get(e);if(t===null)throw it("weight",e,"an existing numeric edge column",`weight names edge column "${e}", which the snapshot does not hold`);if(t.meta.components!==1)throw it("weight",e,"a one-component numeric edge column",`edge column "${e}" has ${t.meta.components} components; a weight column has one`);switch(t.dtype){case"f32":case"f64":case"u32":case"i32":{const s=r.edges.gpuView(e),i=$o(r,s);return{data:i instanceof Float32Array?i:new Float32Array(i),source:"column",column:t}}case"string":case"list":case"json":throw r.edges.gpuView(e),it("weight",e,"a numeric edge column",`edge column "${e}" is ${t.dtype}`);default:throw it("weight",e,"an f32 / f64 / u32 / i32 edge column",`edge column "${e}" is ${t.dtype}: its gpuView is packed words, not per-edge values`)}}const pn={LINLOG:!1,DISTRIBUTED:!1,TIER:0,USE_PERM:!1,HAS_WEIGHTS:!1},gn={SWING_MODE:0},Gh=Object.freeze({...At,nodeMass:null,nodeSize:null,weight:null,center:[0,0,0],seed:null});function Ts(r){return{SWING_MODE:r.SWING_MODE===1?1:0,STRONG_GRAVITY:r.STRONG_GRAVITY===!0,GRAVITY_CENTER:r.GRAVITY_CENTER===1?1:0}}function mn(r){return{...Ts(r),LAW:0}}function xt(r,e){const t=r??{},s=e??Gh;if(e!==void 0&&t.maxInFlight!==void 0&&t.maxInFlight!==e.maxInFlight)throw new h("E_INVALID_ARGUMENT",`maxInFlight cannot change after creation (the uniform ring is sized by it): got ${qr(t.maxInFlight)}, current ${e.maxInFlight}`,{argument:"maxInFlight",value:t.maxInFlight,expected:e.maxInFlight});const i=t.nodeSize===void 0?s.nodeSize:t.nodeSize;if(i!==null)throw new h("E_UNSUPPORTED","nodeSize (the adjustSizes correction) is not supported by the GPU ForceAtlas2 yet (spec 7.14)",{option:"nodeSize",hint:"pass nodeSize: null; the size-aware repulsion is deferred (spec 7.2, Q-25)"});const n={maxIter:re("maxIter",t.maxIter,s.maxIter,Me,"an integer >= 1"),jitterTolerance:re("jitterTolerance",t.jitterTolerance,s.jitterTolerance,o=>o>0,"> 0"),scalingRatio:re("scalingRatio",t.scalingRatio,s.scalingRatio,o=>o>0,"> 0"),gravity:re("gravity",t.gravity,s.gravity,o=>o>=0,">= 0"),strongGravity:or("strongGravity",t.strongGravity,s.strongGravity),distributedAction:or("distributedAction",t.distributedAction,s.distributedAction),linlog:or("linlog",t.linlog,s.linlog),nodeMass:t.nodeMass===void 0?s.nodeMass:t.nodeMass,nodeSize:i,weight:t.weight===void 0?s.weight:t.weight,dissuadeHubs:or("dissuadeHubs",t.dissuadeHubs,s.dissuadeHubs),dim:Xs(t.dim,s.dim),scale:re("scale",t.scale,s.scale,o=>o>0,"> 0"),center:Qs(t.center,s.center),seed:Zs(t.seed,s.seed),settleThreshold:re("settleThreshold",t.settleThreshold,s.settleThreshold,o=>o>=0,">= 0"),settleWindow:re("settleWindow",t.settleWindow,s.settleWindow,Me,"an integer >= 1"),iterationsPerStep:re("iterationsPerStep",t.iterationsPerStep,s.iterationsPerStep,o=>Me(o)&&o<=Fe,`an integer in [1, ${Fe}]`),maxInFlight:re("maxInFlight",t.maxInFlight,s.maxInFlight,Me,"an integer >= 1")};return Object.freeze(n)}function ei(r){const e=r??{},t=e.repulsion??mt.repulsion;if(t!=="exact"&&t!=="grid"&&t!=="auto")throw Re("repulsion",t,'"exact", "grid" or "auto"');const s=e.compat??mt.compat;if(s!=="paper"&&s!=="networkx")throw Re("compat",s,'"paper" or "networkx"');const i={repulsion:t,exactMaxNodes:re("exactMaxNodes",e.exactMaxNodes,mt.exactMaxNodes,Me,"an integer >= 1"),nearMax:re("nearMax",e.nearMax,mt.nearMax,n=>Me(n)&&n>=2,"an integer >= 2"),deterministic:or("deterministic",e.deterministic,mt.deterministic),gridMax2D:re("gridMax2D",e.gridMax2D,mt.gridMax2D,Me,"an integer >= 1"),gridMax3D:re("gridMax3D",e.gridMax3D,mt.gridMax3D,Me,"an integer >= 1"),extentFactor:re("extentFactor",e.extentFactor,mt.extentFactor,n=>n>0,"> 0"),compat:s};return Object.freeze(i)}class zh extends Js{constructor(e,t){super(e,{name:"ForceAtlas2",pass:"fa2",scenePass:"fa2-to-scene",carry:"oldForce"}),this.kind="forceatlas2",this.current=t}get swingMode(){return this.tuning.compat==="networkx"?1:0}inputs(e,t){const s=xt(t,this.current),i=e.nodeCount,n={mass:Ch(e,s.nodeMass),weights:Mh(e,s.weight)};return this.nextGrid=hr(this.tuning,i)==="grid"?ut(i,s.dim,this.tuning):null,n}overrides(e){const t=xt(e,this.current),s=this.swingMode;return{LINLOG:t.linlog,DISTRIBUTED:t.distributedAction,TIER:0,SWING_MODE:s,STRONG_GRAVITY:t.strongGravity,GRAVITY_CENTER:s}}specs(e,t){const[s,i]=ur.specs(Ts(e)),n=this.nextGrid===null?[]:ft.specs(mn(e),ut(Pn+1,2,this.tuning));return[_("fa2-stats-finalize"),_("fa2-attraction",_e(e,pn)),s,i,_("fa2-integrate",_e(e,gn)),_("fa2-to-scene"),_("fill"),...n]}async compile(e,t){const{n:s,pipelines:i,caps:n}=e,[o,a,c,l]=await Promise.all([i.kernel(_("fa2-stats-finalize")),i.kernel(_("fa2-integrate",_e(t,gn))),i.kernel(_("fa2-to-scene")),i.kernel(_("fill"))]),d=e.tier==="grid"?null:await ur.create(i,n,Ts(t)),f=e.core.colIdx!==null?await Ys(e,_e(t,pn)):null,p=e.tier==="grid"?await ft.create(e,o.workgroupSize,mn(t),ut(s,e.dim,this.tuning)):null;return{k1:o,k5:a,toScene:c,fill:l,attraction:f,exact:d===null?null:m=>(d.bind(m),d),grid:p}}zeroesCarry(){return this.swingMode===0}recordSpeed(e,t,s){t.recordSpeedFinalize(e,s)}paramsFor(e,t){this.requireResources();const s=xt(t,this.current);return{...this.sharedParams(e,s),scalingRatio:s.scalingRatio,gravity:s.gravity,jitterTolerance:s.jitterTolerance,settleFloor:Zo}}onLoad(e){e.set("speed",1),e.set("speedEfficiency",1),e.set("swing",1),e.set("traction",1),this.resetCarry=!0,this.nextGrid!==null&&ti(e,this.nextGrid,this.tuning.extentFactor)}onReheat(e){this.swingMode===1&&(e.set("swing",1),e.set("traction",1))}onSetParams(e,t){const s=xt(e,this.current),i=s.linlog!==this.current.linlog||s.strongGravity!==this.current.strongGravity||s.distributedAction!==this.current.distributedAction;this.current=s,i&&(t.set("speed",1),t.set("speedEfficiency",1))}readStats(e,t){const s=et.read(e),i=[],n=Math.floor(t.byteLength/qt);for(let o=0;o<n;o++){const a=Yt.read(t,o*qt);i.push({swing:he(a,"swing"),traction:he(a,"traction"),speed:he(a,"speed"),speedEfficiency:he(a,"speedEfficiency"),meanDisplacement:he(a,"meanDisplacement"),settledCount:he(a,"settledCount")})}return{...this.sharedStats(s),swing:he(s,"swing"),traction:he(s,"traction"),speed:he(s,"speed"),speedEfficiency:he(s,"speedEfficiency"),trace:i}}}function ti(r,e,t){const s=Math.fround,i=b=>{const w=r.get(b);return typeof w=="number"?[w,w,w]:w},n=i("min"),o=i("max"),a=i("centroid"),c=r.get("rmsRadius"),l=[0,1,2].map(b=>s(s(s(o[b])-s(n[b]))*s(Rn))),d=e.dim===3?Math.max(l[0],l[1],l[2]):Math.max(l[0],l[1]),f=s(s(t)*s(typeof c=="number"?c:0)),p=Math.max(Math.min(d,f),s(xn)),g=s(p/e.g),m=s(.5*p);r.set("gridMin",[s(s(a[0])-m),s(s(a[1])-m),s(s(a[2])-m),g]),r.set("invCellSize",s(1/g)),r.set("eps",s(.25*g)),r.set("outsideGrid",0),r.set("maxCellOccupancy",0)}function Fh(r,e){return xt(r,xt(e))}function Wh(r,e){r.assertReady();const t=xt(e),s=ei(e),i=new zh(s,t);return new Hs(r,i,t,s,Fh)}const Uh=Object.freeze({LINLOG:!1,DISTRIBUTED:!1,TIER:0,SWING_MODE:1,STRONG_GRAVITY:!1,GRAVITY_CENTER:0,LAW:1,APPLY:1,STATS_MODE:1}),wn=Object.freeze({SWING_MODE:1,STRONG_GRAVITY:!1,GRAVITY_CENTER:0,LAW:1}),Vh=Object.freeze({...Yo,center:[0,0,0],seed:null});function jh(r,e){return r===void 0?e:r===null||r===0||Number.isNaN(r)?null:re("k",r,1,t=>t>0,"> 0 or null")}function qh(r,e){if(r===void 0)return e;if(r==="linear"||r==="adaptive")return r;throw Re("cooling",r,'"linear" | "adaptive"')}function Vt(r,e){const t=r??{},s=e??Vh;if(e!==void 0&&t.maxInFlight!==void 0&&t.maxInFlight!==e.maxInFlight)throw new h("E_INVALID_ARGUMENT",`maxInFlight cannot change after creation (the uniform ring is sized by it): got ${qr(t.maxInFlight)}, current ${e.maxInFlight}`,{argument:"maxInFlight",value:t.maxInFlight,expected:e.maxInFlight});const i=t.fixed===void 0?s.fixed:t.fixed;if(i!==null&&typeof i!="string"&&!(i instanceof Uint32Array))throw Re("fixed",i,"a NodeMask (Uint32Array), the name of a bool node column, or null");const n=qh(t.cooling,s.cooling),o=e===void 0&&n==="adaptive"&&t.iterations===void 0?Ho:s.iterations,a={k:jh(t.k,s.k),iterations:re("iterations",t.iterations,o,c=>Number.isInteger(c)&&c>=0,"an integer >= 0"),cooling:n,fixed:i,dim:Xs(t.dim,s.dim),scale:re("scale",t.scale,s.scale,c=>c>0,"> 0"),center:Qs(t.center,s.center),seed:Zs(t.seed,s.seed),settleThreshold:re("settleThreshold",t.settleThreshold,s.settleThreshold,c=>c>=0,">= 0"),settleWindow:re("settleWindow",t.settleWindow,s.settleWindow,Me,"an integer >= 1"),iterationsPerStep:re("iterationsPerStep",t.iterationsPerStep,s.iterationsPerStep,c=>Me(c)&&c<=Fe,`an integer in [1, ${Fe}]`),maxInFlight:re("maxInFlight",t.maxInFlight,s.maxInFlight,Me,"an integer >= 1")};return Object.freeze(a)}function Kh(r,e){const t=Math.ceil(r.nodeCount/32);if(typeof e=="string"){const i=r.nodes.get(e);if(i===null)throw Re("fixed",e,`the name of a bool node column (the snapshot holds no node column "${e}")`);if(i.dtype!=="bool")throw Re("fixed",e,`the name of a bool node column ("${e}" is ${i.dtype})`);return new Uint32Array(i.data.subarray(0,t))}if(e!==null){if(e.length<t)throw Re("fixed",e.length,`a mask of at least ${t} words for ${r.nodeCount} nodes`);return new Uint32Array(e.subarray(0,t))}const s=r.nodes.byRole("fixed");return s!==null&&s.dtype==="bool"?new Uint32Array(s.data.subarray(0,t)):null}class Hh extends Js{constructor(e,t){super(e,{name:"Fruchterman-Reingold",pass:"fr",scenePass:"fa2-to-scene",carry:"oldForce"}),this.kind="fruchtermanReingold",this.tempOrigin=0,this.pendingReheat=!1,this.current=t}inputs(e,t){const s=Vt(t,this.current),i={mass:new Float32Array(e.nodeCount).fill(1),weights:{data:null,source:"none",column:null},fixed:Kh(e,s.fixed)},n=e.nodeCount;return this.nextGrid=hr(this.tuning,n)==="grid"?ut(n,s.dim,this.tuning):null,i}overrides(e){return Uh}specs(e,t){const s=this.nextGrid===null?[]:ft.specs(wn,this.nextGrid);return[_("fa2-stats-finalize",_e(e,$r)),_("fa2-attraction",_e(e,Cr)),_("fa2-repulsion-exact",_e(e,Mr)),_("fa2-integrate",_e(e,Gr)),_("fa2-to-scene"),_("fill"),...s]}async compile(e,t){const{n:s,pipelines:i}=e,n=await i.kernel(_("fa2-stats-finalize",_e(t,$r))),o=e.core.colIdx!==null?await Ys(e,_e(t,Cr)):null,a=e.tier==="grid"?null:await i.kernel(_("fa2-repulsion-exact",_e(t,Mr))),c=await i.kernel(_("fa2-integrate",_e(t,Gr))),l=await i.kernel(_("fa2-to-scene")),d=await i.kernel(_("fill")),f=e.tier==="grid"?await ft.create(e,n.workgroupSize,wn,ut(s,e.dim,this.tuning)):null,p=a===null?null:Kr.binder(a);return{k1:n,k5:c,toScene:l,fill:d,attraction:o,exact:p,grid:f}}paramsFor(e,t){const{n:s}=this.requireResources(),i=Vt(t,this.current),n=i.cooling==="adaptive";return{...this.sharedParams(e,i),flags:n?In:0,scalingRatio:0,gravity:0,jitterTolerance:0,frK:i.k??1/Math.sqrt(s),settleFloor:kn.fruchtermanReingold*(i.k??1/Math.sqrt(s)),temperature:n?yr:this.temperatureAt(e,i)}}onLoad(e){this.resetAdaptive(e),e.set("kineticEnergy",0),this.tempOrigin=0,this.pendingReheat=!1,this.nextGrid!==null&&ti(e,this.nextGrid,this.tuning.extentFactor)}resetAdaptive(e){e.set("temperature",yr),e.set("frEnergy",Number.POSITIVE_INFINITY),e.set("frProgress",0)}onReheat(e){this.pendingReheat=!0,this.current.cooling==="adaptive"&&this.resetAdaptive(e)}onSetParams(e,t){this.current=Vt(e,this.current)}readStats(e,t){const s=et.read(e),i=[],n=Math.floor(t.byteLength/qt);for(let o=0;o<n;o++){const a=Yt.read(t,o*qt);i.push({temperature:he(a,"modelScalar"),meanDisplacement:he(a,"meanDisplacement"),settledCount:he(a,"settledCount")})}return{...this.sharedStats(s),temperature:he(s,"temperature"),trace:i}}temperatureAt(e,t){this.pendingReheat&&(this.tempOrigin=e-Math.floor(jo*t.iterations),this.pendingReheat=!1);const s=yr/(t.iterations+1);return Math.max(0,yr-s*(e-this.tempOrigin))}}function Yh(r,e){if("fixed"in r)throw new h("E_INVALID_ARGUMENT","setParams({ fixed }) is not a live option (spec 7.12)",{argument:"fixed",value:r.fixed,expected:"absent",hint:"use setFixed(mask); fixed is applied at load()"});return Vt(r,Vt(e))}function Xh(r,e){r.assertReady();const t=Vt(e),s=ei(e),i=new Hh(s,t);return new Hs(r,i,t,s,Yh)}const Qh=Object.freeze({LINLOG:!1,DISTRIBUTED:!1,TIER:0,SWING_MODE:1,STRONG_GRAVITY:!1,GRAVITY_CENTER:0,LAW:2,APPLY:2,STATS_MODE:2}),bn=Object.freeze({SWING_MODE:1,STRONG_GRAVITY:!1,GRAVITY_CENTER:0,LAW:2}),Zh="velocity",Jh=Object.freeze({..._s,maxIter:null,gravity:null,springCoefficient:null,center:[0,0,0],seed:null});function vn(r){return Math.min(1,Xo/Math.max(1,r))}function vs(r,e,t,s,i){return e===void 0?t:e===null?null:re(r,e,1,s,`${i} or null`)}function jt(r,e){const t=r??{},s=e??Jh;if(e!==void 0&&t.maxInFlight!==void 0&&t.maxInFlight!==e.maxInFlight)throw new h("E_INVALID_ARGUMENT",`maxInFlight cannot change after creation (the uniform ring is sized by it): got ${qr(t.maxInFlight)}, current ${e.maxInFlight}`,{argument:"maxInFlight",value:t.maxInFlight,expected:e.maxInFlight});const i={maxIter:vs("maxIter",t.maxIter,s.maxIter,Me,"an integer >= 1"),springLength:re("springLength",t.springLength,s.springLength,n=>n>0,"> 0"),springCoefficient:vs("springCoefficient",t.springCoefficient,s.springCoefficient,n=>n>0,"> 0"),gravity:vs("gravity",t.gravity,s.gravity,()=>!0,"a finite number (negative repels)"),dragCoefficient:re("dragCoefficient",t.dragCoefficient,s.dragCoefficient,n=>n>=0,">= 0"),timeStep:re("timeStep",t.timeStep,s.timeStep,n=>n>0,"> 0"),dim:Xs(t.dim,s.dim),scale:re("scale",t.scale,s.scale,n=>n>0,"> 0"),center:Qs(t.center,s.center),seed:Zs(t.seed,s.seed),settleThreshold:re("settleThreshold",t.settleThreshold,s.settleThreshold,n=>n>=0,">= 0"),settleWindow:re("settleWindow",t.settleWindow,s.settleWindow,Me,"an integer >= 1"),iterationsPerStep:re("iterationsPerStep",t.iterationsPerStep,s.iterationsPerStep,n=>Me(n)&&n<=Fe,`an integer in [1, ${Fe}]`),maxInFlight:re("maxInFlight",t.maxInFlight,s.maxInFlight,Me,"an integer >= 1")};return Object.freeze(i)}function ep(r){const e=r.outDegree(),t=new Float32Array(r.nodeCount);for(let s=0;s<r.nodeCount;s++)t[s]=1+e[s]/3;return t}class tp extends Js{constructor(e,t){super(e,{name:"spring-electrical",pass:"se",scenePass:"se-to-scene",carry:Zh}),this.kind="springElectrical",this.current=t}inputs(e,t){const{dim:s}=jt(t,this.current),i=e.nodeCount;return this.nextGrid=hr(this.tuning,i)==="grid"?ut(i,s,this.tuning):null,{mass:ep(e),weights:{data:null,source:"none",column:null}}}overrides(e){return Qh}specs(e,t){const s=this.nextGrid===null?[]:ft.specs(bn,this.nextGrid);return[_("fa2-stats-finalize",_e(e,$r)),_("fa2-attraction",_e(e,Cr)),_("fa2-repulsion-exact",_e(e,Mr)),_("fa2-integrate",_e(e,Gr)),_("fa2-to-scene"),_("fill"),...s]}async compile(e,t){const{n:s,pipelines:i}=e,[n,o,a,c]=await Promise.all([i.kernel(_("fa2-stats-finalize",_e(t,$r))),i.kernel(_("fa2-integrate",_e(t,Gr))),i.kernel(_("fa2-to-scene")),i.kernel(_("fill"))]),l=e.core.colIdx!==null?await Ys(e,_e(t,Cr)):null,d=e.tier==="grid"?null:await i.kernel(_("fa2-repulsion-exact",_e(t,Mr))),f=e.tier==="grid"?await ft.create(e,n.workgroupSize,bn,ut(s,e.dim,this.tuning)):null,p=d===null?null:Kr.binder(d);return{k1:n,k5:o,toScene:a,fill:c,attraction:l,exact:p,grid:f}}paramsFor(e,t){const{n:s}=this.requireResources(),i=jt(t,this.current);return{...this.sharedParams(e,i),scalingRatio:0,gravity:0,jitterTolerance:0,frK:0,temperature:0,springLength:i.springLength,settleFloor:kn.springElectrical*i.springLength*(Qo/Math.max(s,1))**.25,springCoefficient:i.springCoefficient??_s.springCoefficient*vn(s),coulomb:i.gravity??_s.gravity*vn(s),dragCoefficient:i.dragCoefficient,timeStep:i.timeStep}}onLoad(e){e.set("kineticEnergy",0),e.set("temperature",0),this.nextGrid!==null&&ti(e,this.nextGrid,this.tuning.extentFactor)}onReheat(e){}onSetParams(e,t){this.current=jt(e,this.current)}readStats(e,t){const s=et.read(e),i=[],n=Math.floor(t.byteLength/qt);for(let o=0;o<n;o++){const a=Yt.read(t,o*qt);i.push({kineticEnergy:he(a,"modelScalar"),meanDisplacement:he(a,"meanDisplacement"),settledCount:he(a,"settledCount")})}return{...this.sharedStats(s),kineticEnergy:he(s,"kineticEnergy"),trace:i}}}function rp(r,e){return jt(r,jt(e))}function sp(r,e){r.assertReady();const t=jt(e),s=ei(e),i=new tp(s,t);return new Hs(r,i,t,s,rp)}function yn(r,e,t){return r===void 0||e?.sources!==void 0||e?.k!==void 0?e:r.sources!==void 0?{...e,sources:r.sources.filter(s=>s<t)}:{...e,k:r.k!==void 0&&r.k<t?r.k:void 0}}function ip(r){return Object.freeze({})}function np(r,e){r.assertReady();const t=ip();return{kind:"webgpu",harmonicCloseness:!0,ctx:r,options:t,forceAtlas2(s){return r.assertReady(),Wh(r,{...s,...t.layout})},fruchtermanReingold(s){return r.assertReady(),Xh(r,{...s,...t.layout})},springElectrical(s){return r.assertReady(),sp(r,{...s,...t.layout})},async pageRank(s,i){return r.assertReady(),await Xf(r,s,i)},async personalizedPageRank(s,i,n){r.assertReady();const o=i instanceof Float32Array?i:Float32Array.from(i);return await Qf(r,s,o,n)},async hits(s,i){return r.assertReady(),await eh(r,s,i)},async eigenvectorCentrality(s,i){return r.assertReady(),await Zf(r,s,i)},async katzCentrality(s,i){return r.assertReady(),await Jf(r,s,i)},async connectedComponents(s,i){return r.assertReady(),await qi(r,s,i)},async weaklyConnectedComponents(s,i){return r.assertReady(),await qi(r,s,i)},async breadthFirstSearch(s,i,n){return r.assertReady(),await Ad(r,s,i,n)},async sssp(s,i,n){return r.assertReady(),await no(r,s,i,n)},async bellmanFord(s,i,n){return r.assertReady(),await Cd(r,s,i,n)},async betweennessCentrality(s,i){return r.assertReady(),await Kd(r,s,yn(t.algorithms?.betweenness,i,s.nodeCount))},async edgeBetweennessCentrality(s,i){return r.assertReady(),await Hd(r,s,yn(t.algorithms?.betweenness,i,s.nodeCount))},async closenessCentrality(s,i){return r.assertReady(),await sf(r,s,i)},async allPairsShortestPath(s,i){r.assertReady();for(const n of["cutoff","weights"])if(i?.[n]!==void 0)throw new h("E_UNSUPPORTED",`allPairsShortestPath: ${n} is not supported`,{option:n,hint:"all-pairs shortest paths runs over the snapshot's own weights with no cutoff"});return await Dd(r,s)},async triangleCount(s){return r.assertReady(),await sh(r,s)},async labelPropagation(s,i){if(r.assertReady(),i?.tolerance!==void 0)throw new h("E_UNSUPPORTED","labelPropagation: tolerance has no meaning here",{option:"tolerance",hint:"label propagation stops at a fixed point or after maxIterations passes"});return await Ff(r,s,{maxIterations:i?.maxIterations,weighted:i?.weighted})},async minimumSpanningTree(s,i){if(r.assertReady(),i?.weights!==void 0)throw new h("E_UNSUPPORTED","minimumSpanningTree: weights is not supported",{option:"weights",hint:"the spanning forest runs over the snapshot's own edge weights"});return await qf(r,s)},release(s){r.release(s)},dispose(){r.dispose()}}}function Ro(){return typeof navigator>"u"?void 0:navigator.gpu}function op(r){return zt.probe({gpu:Ro(),powerPreference:r?.powerPreference??"high-performance",rejectSoftware:r?.rejectSoftware})}function ap(r){const e=Ro();return e===void 0&&r?.adapter===void 0?Promise.reject(new h("E_NO_WEBGPU","navigator.gpu is undefined: this browser or context has no WebGPU",{reason:"navigator.gpu is undefined",hint:"WebGPU needs a supporting browser and a secure context (https or localhost)"})):zt.create({powerPreference:"high-performance",...r,gpu:e,runtime:"browser"})}const cp=8,lp="call configureWebGpu({ acceptSoftware: true }) to accept the software adapter";function up(r,e){return{acquire:async()=>{const t=e.acceptSoftware!==!0,s=await r.probe(t);if(!s.ok){const n=s.code==="E_SOFTWARE_ONLY"?lp:r.fixes[s.code]??null;throw new ai(s.code,s.reason??s.code,n)}const i=await r.open(s,t,cp);try{const n=await Hn(i);if(n.mismatch!==null)throw new ai("E_DEVICE_INCORRECT",`the ${n.vendor} device computes a known prefix sum incorrectly (${n.mismatch.where})`);const{caps:o}=i;return{accelerator:np(i),device:`${o.vendor} ${o.device===""?o.description:o.device}`,lost:i.lost.then(a=>a.message===""?a.reason:a.message)}}catch(n){throw i.dispose(),n}}}}function hp(r){return up({probe:e=>op({rejectSoftware:e}),open:(e,t,s)=>ap({adapter:e.adapter??void 0,rejectSoftware:t,warnUnreleasedSnapshots:s}),fixes:{}},r)}export{hp as gpuProvider};
|