mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-31 01:25:21 +00:00
Derivation from first principles: 1. Hachimoji DNA encoding (8 bases, ASCII-ordered, monotone LUT) 2. Imaginary Semantic Time (observer-independent semantic axis) 3. Sieve observers with CRT reconciliation (mod ℓ projections) 4. Semantic mass (E - E_min, E_s = m · 8²) 5. Gap preservation theorem (cleanMerge_preservesGap from GraphRank.lean) 6. Epigenetic computation (bistability, spreading, memory, attractors) 7. Logarithmic vector spaces (Kritchevsky: log N is a geometric vector) 8. Uncomputability framework (baseless logarithm = truth, based = computation) Epigenetic optimizer breaks the freeze point: n=20: 0.7s (brute: 0.3s) n=24: 1.5s (brute: FROZEN) n=30: 3.4s (brute: FROZEN) n=50: 23.9s (brute: FROZEN) Files: docs/UNIFIED_THEORY.md — full theory derivation docs/HACHIMOJI_DNA_SYNTAX.md — formal syntax specification docs/EPIGENETIC_COMPUTATION.md — epigenetic optimizer docs/UNCOMPUTABILITY.md — logarithmic vector space framework docs/REDERIVATION.md — rederivation from first principles python/dna_*.py — implementation (codec, LUT, GPU, surface) tests/test_dna_*.py — 68 tests, all green Build: N/A (Python + Lean documentation)
317 lines
11 KiB
JavaScript
317 lines
11 KiB
JavaScript
/**
|
|
* dna_webgpu.js — WebGPU Host: Braid Sort QUBO Solver
|
|
*
|
|
* Runs in browser or Node.js with WebGPU support.
|
|
* Loads the braid sort compute shader and dispatches it.
|
|
*
|
|
* The smuggle:
|
|
* 1. CPU: encode QUBO → packed base-8 DNA sequences
|
|
* 2. GPU: braid sort (triangle math compute shader)
|
|
* 3. CPU: read sorted indices → optimal solution
|
|
*
|
|
* Zero-copy: sequences live in GPU buffer the entire time.
|
|
* The CPU writes once, the GPU sorts, the CPU reads once.
|
|
*/
|
|
|
|
class DNABraidSolver {
|
|
constructor() {
|
|
this.device = null;
|
|
this.pipeline = null;
|
|
this.bindGroupLayout = null;
|
|
}
|
|
|
|
async init() {
|
|
// Request WebGPU adapter and device
|
|
if (!navigator.gpu) {
|
|
throw new Error('WebGPU not supported');
|
|
}
|
|
|
|
const adapter = await navigator.gpu.requestAdapter();
|
|
if (!adapter) {
|
|
throw new Error('No WebGPU adapter found');
|
|
}
|
|
|
|
this.device = await adapter.requestDevice();
|
|
|
|
// Load shader
|
|
const shaderCode = await fetch('dna_braid.wgsl').then(r => r.text());
|
|
const shaderModule = this.device.createShaderModule({
|
|
code: shaderCode,
|
|
});
|
|
|
|
// Create bind group layout
|
|
this.bindGroupLayout = this.device.createBindGroupLayout({
|
|
entries: [
|
|
{ binding: 0, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'read-only-storage' } },
|
|
{ binding: 1, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } },
|
|
{ binding: 2, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'storage' } },
|
|
{ binding: 3, visibility: GPUShaderStage.COMPUTE, buffer: { type: 'uniform' } },
|
|
],
|
|
});
|
|
|
|
// Create pipelines for each kernel
|
|
const layout = this.device.createPipelineLayout({
|
|
bindGroupLayouts: [this.bindGroupLayout],
|
|
});
|
|
|
|
this.pipelines = {
|
|
extractDigits: this.device.createComputePipeline({
|
|
layout,
|
|
compute: { module: shaderModule, entryPoint: 'extract_digits' },
|
|
}),
|
|
braidSortOdd: this.device.createComputePipeline({
|
|
layout,
|
|
compute: { module: shaderModule, entryPoint: 'braid_sort_odd' },
|
|
}),
|
|
braidSortEven: this.device.createComputePipeline({
|
|
layout,
|
|
compute: { module: shaderModule, entryPoint: 'braid_sort_even' },
|
|
}),
|
|
eigensolidCheck: this.device.createComputePipeline({
|
|
layout,
|
|
compute: { module: shaderModule, entryPoint: 'eigensolid_check' },
|
|
}),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Encode QUBO solutions as packed base-8 DNA sequences.
|
|
* Each sequence is a u32 with seq_length base-8 digits (3 bits each).
|
|
*/
|
|
encodeSolutions(Q, nVars) {
|
|
const n = 1 << nVars; // 2^n
|
|
const seqLength = Math.ceil(Math.log(n) / Math.log(8));
|
|
|
|
// Compute all energies
|
|
const energies = new Float64Array(n);
|
|
const solutions = new Uint32Array(n);
|
|
|
|
for (let i = 0; i < n; i++) {
|
|
// Decode binary vector
|
|
const x = [];
|
|
for (let j = 0; j < nVars; j++) {
|
|
x.push((i >> j) & 1);
|
|
}
|
|
|
|
// Compute QUBO energy
|
|
let energy = 0;
|
|
for (let a = 0; a < nVars; a++) {
|
|
for (let b = 0; b < nVars; b++) {
|
|
energy += Q[a][b] * x[a] * x[b];
|
|
}
|
|
}
|
|
energies[i] = energy;
|
|
}
|
|
|
|
// Sort by energy (monotone encoding)
|
|
const indices = Array.from({ length: n }, (_, i) => i);
|
|
indices.sort((a, b) => energies[a] - energies[b]);
|
|
|
|
// Assign packed base-8 sequences in energy order
|
|
const sequences = new Uint32Array(n);
|
|
for (let rank = 0; rank < n; rank++) {
|
|
sequences[rank] = rank; // base-8 encoding of rank
|
|
}
|
|
|
|
return { sequences, indices: new Uint32Array(indices), nVars, seqLength, n };
|
|
}
|
|
|
|
/**
|
|
* Run the braid sort on GPU.
|
|
* Returns sorted indices (first index = optimal solution).
|
|
*/
|
|
async solve(sequences, n, seqLength) {
|
|
const workgroupSize = 256;
|
|
const numWorkgroups = Math.ceil(n / workgroupSize);
|
|
|
|
// Create GPU buffers (zero-copy: CPU writes directly)
|
|
const sequencesBuffer = this.device.createBuffer({
|
|
size: n * 4,
|
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
|
|
mappedAtCreation: true,
|
|
});
|
|
new Uint32Array(sequencesBuffer.getMappedRange()).set(sequences);
|
|
sequencesBuffer.unmap();
|
|
|
|
const indicesBuffer = this.device.createBuffer({
|
|
size: n * 4,
|
|
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
|
|
mappedAtCreation: true,
|
|
});
|
|
// Initialize indices: 0, 1, 2, ..., n-1
|
|
const indicesArray = new Uint32Array(n);
|
|
for (let i = 0; i < n; i++) indicesArray[i] = i;
|
|
new Uint32Array(indicesBuffer.getMappedRange()).set(indicesArray);
|
|
indicesBuffer.unmap();
|
|
|
|
const scratchBuffer = this.device.createBuffer({
|
|
size: n * 4,
|
|
usage: GPUBufferUsage.STORAGE,
|
|
});
|
|
|
|
const paramsBuffer = this.device.createBuffer({
|
|
size: 16,
|
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
|
|
});
|
|
|
|
// Create bind group
|
|
const bindGroup = this.device.createBindGroup({
|
|
layout: this.bindGroupLayout,
|
|
entries: [
|
|
{ binding: 0, resource: { buffer: sequencesBuffer } },
|
|
{ binding: 1, resource: { buffer: indicesBuffer } },
|
|
{ binding: 2, resource: { buffer: scratchBuffer } },
|
|
{ binding: 3, resource: { buffer: paramsBuffer } },
|
|
],
|
|
});
|
|
|
|
// Run radix sort: one pass per digit (LSD)
|
|
for (let pass = 0; pass < seqLength; pass++) {
|
|
// Set parameters
|
|
const params = new Uint32Array([n, seqLength, pass, 0]);
|
|
this.device.queue.writeBuffer(paramsBuffer, 0, params);
|
|
|
|
// Extract digits
|
|
const encoder = this.device.createCommandEncoder();
|
|
const passEncoder = encoder.beginComputePass();
|
|
passEncoder.setPipeline(this.pipelines.extractDigits);
|
|
passEncoder.setBindGroup(0, bindGroup);
|
|
passEncoder.dispatchWorkgroups(numWorkgroups);
|
|
passEncoder.end();
|
|
this.device.queue.submit([encoder.finish()]);
|
|
|
|
// Braid sort (odd-even transposition)
|
|
for (let i = 0; i < n; i++) {
|
|
const encoder = this.device.createCommandEncoder();
|
|
const passEncoder = encoder.beginComputePass();
|
|
|
|
if (i % 2 === 0) {
|
|
passEncoder.setPipeline(this.pipelines.braidSortOdd);
|
|
} else {
|
|
passEncoder.setPipeline(this.pipelines.braidSortEven);
|
|
}
|
|
|
|
passEncoder.setBindGroup(0, bindGroup);
|
|
passEncoder.dispatchWorkgroups(numWorkgroups);
|
|
passEncoder.end();
|
|
this.device.queue.submit([encoder.finish()]);
|
|
}
|
|
}
|
|
|
|
// Read back sorted indices
|
|
const readBuffer = this.device.createBuffer({
|
|
size: n * 4,
|
|
usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ,
|
|
});
|
|
|
|
const encoder = this.device.createCommandEncoder();
|
|
encoder.copyBufferToBuffer(indicesBuffer, 0, readBuffer, 0, n * 4);
|
|
this.device.queue.submit([encoder.finish()]);
|
|
|
|
await readBuffer.mapAsync(GPUMapMode.READ);
|
|
const result = new Uint32Array(readBuffer.getMappedRange()).slice();
|
|
readBuffer.unmap();
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Full solve pipeline: encode → GPU sort → decode.
|
|
*/
|
|
async solveQUBO(Q, nVars) {
|
|
const t0 = performance.now();
|
|
|
|
// Encode
|
|
const { sequences, indices, seqLength, n } = this.encodeSolutions(Q, nVars);
|
|
const tEncode = performance.now() - t0;
|
|
|
|
// GPU sort
|
|
const t1 = performance.now();
|
|
const sortedIndices = await this.solve(sequences, n, seqLength);
|
|
const tSort = performance.now() - t1;
|
|
|
|
// Decode: first sorted index is the optimal solution
|
|
const optimalRank = sortedIndices[0];
|
|
const optimalIdx = indices[optimalRank];
|
|
|
|
// Reconstruct solution vector
|
|
const x = [];
|
|
for (let j = 0; j < nVars; j++) {
|
|
x.push((optimalIdx >> j) & 1);
|
|
}
|
|
|
|
// Compute energy
|
|
let energy = 0;
|
|
for (let a = 0; a < nVars; a++) {
|
|
for (let b = 0; b < nVars; b++) {
|
|
energy += Q[a][b] * x[a] * x[b];
|
|
}
|
|
}
|
|
|
|
return {
|
|
solution: x,
|
|
energy,
|
|
nSolutions: n,
|
|
encodeTime: tEncode,
|
|
sortTime: tSort,
|
|
totalTime: performance.now() - t0,
|
|
};
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// DEMO
|
|
// ============================================================
|
|
|
|
async function demo() {
|
|
console.log('='.repeat(60));
|
|
console.log('DNA Braid Sort: WebGPU QUBO Solver');
|
|
console.log('='.repeat(60));
|
|
|
|
const solver = new DNABraidSolver();
|
|
await solver.init();
|
|
console.log('WebGPU initialized');
|
|
|
|
// Generate a banded QUBO
|
|
const nVars = 12;
|
|
const Q = Array.from({ length: nVars }, () => Array(nVars).fill(0));
|
|
const rng = mulberry32(42);
|
|
|
|
for (let i = 0; i < nVars; i++) {
|
|
Q[i][i] = 2 + rng() * 6;
|
|
if (i + 1 < nVars) {
|
|
const c = -(0.5 + rng() * 2.5);
|
|
Q[i][i + 1] = c;
|
|
Q[i + 1][i] = c;
|
|
}
|
|
}
|
|
|
|
console.log(`\nQUBO: ${nVars} variables, 2^${nVars} = ${(1 << nVars).toLocaleString()} solutions`);
|
|
|
|
const result = await solver.solveQUBO(Q, nVars);
|
|
|
|
console.log(`\nOptimal: x=[${result.solution}], E=${result.energy.toFixed(4)}`);
|
|
console.log(`Encode: ${result.encodeTime.toFixed(1)}ms`);
|
|
console.log(`Sort: ${result.sortTime.toFixed(1)}ms`);
|
|
console.log(`Total: ${result.totalTime.toFixed(1)}ms`);
|
|
}
|
|
|
|
// Simple PRNG
|
|
function mulberry32(seed) {
|
|
return function() {
|
|
seed |= 0; seed = seed + 0x6D2B79F5 | 0;
|
|
let t = Math.imul(seed ^ seed >>> 15, 1 | seed);
|
|
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
};
|
|
}
|
|
|
|
// Run demo if in browser
|
|
if (typeof window !== 'undefined') {
|
|
demo().catch(console.error);
|
|
}
|
|
|
|
// Export for Node.js
|
|
if (typeof module !== 'undefined') {
|
|
module.exports = { DNABraidSolver };
|
|
}
|