@wenathlan/saddle 1.8.18 → 2.0.2
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/Dockerfile +1339 -0
- package/README.md +33 -12
- package/alternatives.ts +1218 -0
- package/biome.json +153 -0
- package/boards.json +1517 -0
- package/compute.ts +3144 -0
- package/cores.json +1801 -0
- package/dist/acquisition.d.ts +359 -0
- package/dist/acquisition.d.ts.map +1 -0
- package/dist/acquisition.js +629 -0
- package/dist/acquisition.js.map +1 -0
- package/dist/alternatives.d.ts +509 -0
- package/dist/alternatives.d.ts.map +1 -0
- package/dist/alternatives.js +951 -0
- package/dist/alternatives.js.map +1 -0
- package/dist/automation.d.ts +426 -0
- package/dist/automation.d.ts.map +1 -0
- package/dist/automation.js +561 -0
- package/dist/automation.js.map +1 -0
- package/dist/browser.d.ts +562 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +1057 -0
- package/dist/browser.js.map +1 -0
- package/dist/capacitor.config.d.ts +13 -0
- package/dist/capacitor.config.d.ts.map +1 -0
- package/dist/capacitor.config.js +26 -0
- package/dist/capacitor.config.js.map +1 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +111 -0
- package/dist/cli.js.map +1 -0
- package/dist/communication.d.ts +192 -0
- package/dist/communication.d.ts.map +1 -0
- package/dist/communication.js +418 -0
- package/dist/communication.js.map +1 -0
- package/dist/compute.d.ts +1161 -0
- package/dist/compute.d.ts.map +1 -0
- package/dist/compute.js +2273 -0
- package/dist/compute.js.map +1 -0
- package/dist/distribution.d.ts +529 -0
- package/dist/distribution.d.ts.map +1 -0
- package/dist/distribution.js +811 -0
- package/dist/distribution.js.map +1 -0
- package/dist/execution.d.ts +530 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +761 -0
- package/dist/execution.js.map +1 -0
- package/dist/format.d.ts +19 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +60 -0
- package/dist/format.js.map +1 -0
- package/dist/foundation.d.ts +176 -0
- package/dist/foundation.d.ts.map +1 -0
- package/dist/foundation.js +309 -0
- package/dist/foundation.js.map +1 -0
- package/dist/index.d.ts +733 -123
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1018 -123
- package/dist/index.js.map +1 -1
- package/dist/integration.d.ts +236 -0
- package/dist/integration.d.ts.map +1 -0
- package/dist/integration.js +236 -0
- package/dist/integration.js.map +1 -0
- package/dist/intelligence.d.ts +94 -0
- package/dist/intelligence.d.ts.map +1 -0
- package/dist/intelligence.js +145 -0
- package/dist/intelligence.js.map +1 -0
- package/dist/{isolation/contracts.d.ts → isolation.d.ts} +12 -1
- package/dist/isolation.d.ts.map +1 -0
- package/dist/{isolation/contracts.js → isolation.js} +13 -1
- package/dist/isolation.js.map +1 -0
- package/dist/media.d.ts +344 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +986 -0
- package/dist/media.js.map +1 -0
- package/dist/modes.d.ts +359 -0
- package/dist/modes.d.ts.map +1 -0
- package/dist/modes.js +134 -0
- package/dist/modes.js.map +1 -0
- package/dist/{library/public.d.ts → operations.d.ts} +15 -1
- package/dist/operations.d.ts.map +1 -0
- package/dist/{library/public.js → operations.js} +18 -7
- package/dist/operations.js.map +1 -0
- package/dist/orchestrator.d.ts +1339 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +4308 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/performance.d.ts +416 -0
- package/dist/performance.d.ts.map +1 -0
- package/dist/performance.js +827 -0
- package/dist/performance.js.map +1 -0
- package/dist/quantum.d.ts +810 -0
- package/dist/quantum.d.ts.map +1 -0
- package/dist/quantum.js +2073 -0
- package/dist/quantum.js.map +1 -0
- package/dist/render.d.ts +381 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +1073 -0
- package/dist/render.js.map +1 -0
- package/dist/scheduler.d.ts +596 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +1259 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/security.d.ts +501 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +1170 -0
- package/dist/security.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/{server/node.js → server.js} +16 -2
- package/dist/server.js.map +1 -0
- package/dist/tiers.d.ts +1002 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +2165 -0
- package/dist/tiers.js.map +1 -0
- package/dist/virtual.d.ts +641 -0
- package/dist/virtual.d.ts.map +1 -0
- package/dist/virtual.js +1285 -0
- package/dist/virtual.js.map +1 -0
- package/dist/virtualcpu.d.ts +316 -0
- package/dist/virtualcpu.d.ts.map +1 -0
- package/dist/virtualcpu.js +1180 -0
- package/dist/virtualcpu.js.map +1 -0
- package/dist/virtualgpu.d.ts +345 -0
- package/dist/virtualgpu.d.ts.map +1 -0
- package/dist/virtualgpu.js +1000 -0
- package/dist/virtualgpu.js.map +1 -0
- package/dist/virtualization.d.ts +607 -0
- package/dist/virtualization.d.ts.map +1 -0
- package/dist/virtualization.js +1187 -0
- package/dist/virtualization.js.map +1 -0
- package/dist/virtualmemory.d.ts +818 -0
- package/dist/virtualmemory.d.ts.map +1 -0
- package/dist/virtualmemory.js +1796 -0
- package/dist/virtualmemory.js.map +1 -0
- package/dist/webscrape.d.ts +720 -0
- package/dist/webscrape.d.ts.map +1 -0
- package/dist/webscrape.js +2697 -0
- package/dist/webscrape.js.map +1 -0
- package/docker.config +769 -0
- package/docs/CONVERSA.txt +1523 -0
- package/docs/alternatives.md +499 -0
- package/docs/architecture-1.8.19.md +76 -0
- package/docs/architecture-1.8.19.mmd +33 -0
- package/docs/architecture-1.8.19.png +0 -0
- package/docs/architecture.md +355 -0
- package/docs/brancharchive-2026-08-18.md +18 -0
- package/docs/browser.md +7 -0
- package/docs/consolidation.md +70 -0
- package/docs/e2ugh-engine.md +528 -0
- package/{dist/examples/localjob.js → docs/example-localjob.ts} +3 -3
- package/{dist/examples/publicapi.js → docs/example-publicapi.ts} +2 -2
- package/docs/hardware.md +342 -0
- package/docs/optimization.md +126 -0
- package/docs/performance.md +499 -0
- package/docs/planning.1.8.18.md +94 -0
- package/docs/planning.1.8.19.md +49 -0
- package/docs/releasenotes-1.8.19.md +35 -0
- package/docs/research-1.8.19-virtual-browser.md +27 -0
- package/docs/security.md +454 -0
- package/docs/todo-1.8.18.md +12 -0
- package/docs/todo-1.8.19.md +241 -0
- package/docs/viability.md +777 -0
- package/docs/virtualization.md +335 -0
- package/docs/web-duplicatecleanup.md +9 -0
- package/docs/web-ideas.md +106 -0
- package/docs/workflowimprovements-2026-08-19.md +70 -0
- package/gpumonitor.cpp +1543 -0
- package/gpus.json +2408 -0
- package/index.ts +1478 -0
- package/media.ts +1337 -0
- package/mttg.config +1549 -0
- package/orchestrator.ts +5462 -0
- package/package.json +304 -56
- package/passage.config +1331 -0
- package/performance.ts +1049 -0
- package/processors.json +2217 -0
- package/qemu.config +1062 -0
- package/qemubridge.py +1340 -0
- package/quantum.ts +2393 -0
- package/render.ts +1325 -0
- package/scheduler.ts +1600 -0
- package/security.ts +1449 -0
- package/tiers.ts +2871 -0
- package/tsconfig.json +29 -0
- package/virtualcpu.ts +1273 -0
- package/virtualgpu.ts +1181 -0
- package/virtualhardware.c +1011 -0
- package/virtualhardware.json +739 -0
- package/virtualization.ts +1628 -0
- package/virtualizationcore.cpp +4635 -0
- package/virtualmemory.ts +2303 -0
- package/vm.config.json +1910 -0
- package/{extension → web/extension}/manifest.json +1 -1
- package/web/readme.md +382 -0
- package/web/tsconfig.json +25 -0
- package/dist/adapters/forge.d.ts +0 -17
- package/dist/adapters/forge.d.ts.map +0 -1
- package/dist/adapters/forge.js +0 -19
- package/dist/adapters/forge.js.map +0 -1
- package/dist/adapters/forgejo.d.ts +0 -49
- package/dist/adapters/forgejo.d.ts.map +0 -1
- package/dist/adapters/forgejo.js +0 -8
- package/dist/adapters/forgejo.js.map +0 -1
- package/dist/adapters/github.d.ts +0 -12
- package/dist/adapters/github.d.ts.map +0 -1
- package/dist/adapters/github.js +0 -20
- package/dist/adapters/github.js.map +0 -1
- package/dist/adapters/gitlab.d.ts +0 -17
- package/dist/adapters/gitlab.d.ts.map +0 -1
- package/dist/adapters/gitlab.js +0 -10
- package/dist/adapters/gitlab.js.map +0 -1
- package/dist/adapters/huggingface.d.ts +0 -17
- package/dist/adapters/huggingface.d.ts.map +0 -1
- package/dist/adapters/huggingface.js +0 -6
- package/dist/adapters/huggingface.js.map +0 -1
- package/dist/adapters/socket.d.ts +0 -11
- package/dist/adapters/socket.d.ts.map +0 -1
- package/dist/adapters/socket.js +0 -17
- package/dist/adapters/socket.js.map +0 -1
- package/dist/adapters/transport.d.ts +0 -7
- package/dist/adapters/transport.d.ts.map +0 -1
- package/dist/adapters/transport.js +0 -36
- package/dist/adapters/transport.js.map +0 -1
- package/dist/ai/chunk.d.ts +0 -2
- package/dist/ai/chunk.d.ts.map +0 -1
- package/dist/ai/chunk.js +0 -36
- package/dist/ai/chunk.js.map +0 -1
- package/dist/ai/llmstxt.d.ts +0 -6
- package/dist/ai/llmstxt.d.ts.map +0 -1
- package/dist/ai/llmstxt.js +0 -12
- package/dist/ai/llmstxt.js.map +0 -1
- package/dist/ai/provenance.d.ts +0 -22
- package/dist/ai/provenance.d.ts.map +0 -1
- package/dist/ai/provenance.js +0 -25
- package/dist/ai/provenance.js.map +0 -1
- package/dist/ai/rag.d.ts +0 -26
- package/dist/ai/rag.d.ts.map +0 -1
- package/dist/ai/rag.js +0 -22
- package/dist/ai/rag.js.map +0 -1
- package/dist/ai/tokens.d.ts +0 -15
- package/dist/ai/tokens.d.ts.map +0 -1
- package/dist/ai/tokens.js +0 -9
- package/dist/ai/tokens.js.map +0 -1
- package/dist/api/auth.d.ts +0 -16
- package/dist/api/auth.d.ts.map +0 -1
- package/dist/api/auth.js +0 -19
- package/dist/api/auth.js.map +0 -1
- package/dist/api/contracts.d.ts +0 -28
- package/dist/api/contracts.d.ts.map +0 -1
- package/dist/api/contracts.js +0 -14
- package/dist/api/contracts.js.map +0 -1
- package/dist/api/control.d.ts +0 -10
- package/dist/api/control.d.ts.map +0 -1
- package/dist/api/control.js +0 -35
- package/dist/api/control.js.map +0 -1
- package/dist/api/http.d.ts +0 -7
- package/dist/api/http.d.ts.map +0 -1
- package/dist/api/http.js +0 -12
- package/dist/api/http.js.map +0 -1
- package/dist/api/rate.d.ts +0 -28
- package/dist/api/rate.d.ts.map +0 -1
- package/dist/api/rate.js +0 -36
- package/dist/api/rate.js.map +0 -1
- package/dist/api/security.d.ts +0 -12
- package/dist/api/security.d.ts.map +0 -1
- package/dist/api/security.js +0 -56
- package/dist/api/security.js.map +0 -1
- package/dist/api/service.d.ts +0 -5
- package/dist/api/service.d.ts.map +0 -1
- package/dist/api/service.js +0 -70
- package/dist/api/service.js.map +0 -1
- package/dist/apps/registry.d.ts +0 -33
- package/dist/apps/registry.d.ts.map +0 -1
- package/dist/apps/registry.js +0 -24
- package/dist/apps/registry.js.map +0 -1
- package/dist/binary/archive.d.ts +0 -27
- package/dist/binary/archive.d.ts.map +0 -1
- package/dist/binary/archive.js +0 -46
- package/dist/binary/archive.js.map +0 -1
- package/dist/binary/build.d.ts +0 -29
- package/dist/binary/build.d.ts.map +0 -1
- package/dist/binary/build.js +0 -17
- package/dist/binary/build.js.map +0 -1
- package/dist/binary/transform.d.ts +0 -70
- package/dist/binary/transform.d.ts.map +0 -1
- package/dist/binary/transform.js +0 -105
- package/dist/binary/transform.js.map +0 -1
- package/dist/bot/adapter.d.ts +0 -5
- package/dist/bot/adapter.d.ts.map +0 -1
- package/dist/bot/adapter.js +0 -11
- package/dist/bot/adapter.js.map +0 -1
- package/dist/bot/bot.d.ts +0 -28
- package/dist/bot/bot.d.ts.map +0 -1
- package/dist/bot/bot.js +0 -52
- package/dist/bot/bot.js.map +0 -1
- package/dist/bot/commands.d.ts +0 -8
- package/dist/bot/commands.d.ts.map +0 -1
- package/dist/bot/commands.js +0 -19
- package/dist/bot/commands.js.map +0 -1
- package/dist/bot/permissions.d.ts +0 -13
- package/dist/bot/permissions.d.ts.map +0 -1
- package/dist/bot/permissions.js +0 -16
- package/dist/bot/permissions.js.map +0 -1
- package/dist/browser/actions.d.ts +0 -33
- package/dist/browser/actions.d.ts.map +0 -1
- package/dist/browser/actions.js +0 -44
- package/dist/browser/actions.js.map +0 -1
- package/dist/browser/agent.d.ts +0 -21
- package/dist/browser/agent.d.ts.map +0 -1
- package/dist/browser/agent.js +0 -13
- package/dist/browser/agent.js.map +0 -1
- package/dist/browser/context.d.ts +0 -43
- package/dist/browser/context.d.ts.map +0 -1
- package/dist/browser/context.js +0 -54
- package/dist/browser/context.js.map +0 -1
- package/dist/browser/fingerprint.d.ts +0 -4
- package/dist/browser/fingerprint.d.ts.map +0 -1
- package/dist/browser/fingerprint.js +0 -12
- package/dist/browser/fingerprint.js.map +0 -1
- package/dist/browser/index.d.ts +0 -11
- package/dist/browser/index.d.ts.map +0 -1
- package/dist/browser/index.js +0 -11
- package/dist/browser/index.js.map +0 -1
- package/dist/browser/playwright.d.ts +0 -11
- package/dist/browser/playwright.d.ts.map +0 -1
- package/dist/browser/playwright.js +0 -24
- package/dist/browser/playwright.js.map +0 -1
- package/dist/browser/recorder.d.ts +0 -21
- package/dist/browser/recorder.d.ts.map +0 -1
- package/dist/browser/recorder.js +0 -58
- package/dist/browser/recorder.js.map +0 -1
- package/dist/browser/session.d.ts +0 -14
- package/dist/browser/session.d.ts.map +0 -1
- package/dist/browser/session.js +0 -21
- package/dist/browser/session.js.map +0 -1
- package/dist/browser/snapshot.d.ts +0 -69
- package/dist/browser/snapshot.d.ts.map +0 -1
- package/dist/browser/snapshot.js +0 -133
- package/dist/browser/snapshot.js.map +0 -1
- package/dist/captcha/contract.d.ts +0 -35
- package/dist/captcha/contract.d.ts.map +0 -1
- package/dist/captcha/contract.js +0 -17
- package/dist/captcha/contract.js.map +0 -1
- package/dist/captcha/evidence.d.ts +0 -10
- package/dist/captcha/evidence.d.ts.map +0 -1
- package/dist/captcha/evidence.js +0 -9
- package/dist/captcha/evidence.js.map +0 -1
- package/dist/captcha/guard.d.ts +0 -23
- package/dist/captcha/guard.d.ts.map +0 -1
- package/dist/captcha/guard.js +0 -12
- package/dist/captcha/guard.js.map +0 -1
- package/dist/cli/main.d.ts +0 -4
- package/dist/cli/main.d.ts.map +0 -1
- package/dist/cli/main.js +0 -46
- package/dist/cli/main.js.map +0 -1
- package/dist/core/errors.d.ts +0 -84
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/errors.js +0 -54
- package/dist/core/errors.js.map +0 -1
- package/dist/core/events.d.ts +0 -9
- package/dist/core/events.d.ts.map +0 -1
- package/dist/core/events.js +0 -21
- package/dist/core/events.js.map +0 -1
- package/dist/core/hash.d.ts +0 -10
- package/dist/core/hash.d.ts.map +0 -1
- package/dist/core/hash.js +0 -82
- package/dist/core/hash.js.map +0 -1
- package/dist/core/ids.d.ts +0 -10
- package/dist/core/ids.d.ts.map +0 -1
- package/dist/core/ids.js +0 -15
- package/dist/core/ids.js.map +0 -1
- package/dist/delivery/manifest.d.ts +0 -35
- package/dist/delivery/manifest.d.ts.map +0 -1
- package/dist/delivery/manifest.js +0 -68
- package/dist/delivery/manifest.js.map +0 -1
- package/dist/deploy/index.d.ts +0 -6
- package/dist/deploy/index.d.ts.map +0 -1
- package/dist/deploy/index.js +0 -6
- package/dist/deploy/index.js.map +0 -1
- package/dist/dispatch/resumable.d.ts +0 -102
- package/dist/dispatch/resumable.d.ts.map +0 -1
- package/dist/dispatch/resumable.js +0 -64
- package/dist/dispatch/resumable.js.map +0 -1
- package/dist/dispatch/workflow.d.ts +0 -6
- package/dist/dispatch/workflow.d.ts.map +0 -1
- package/dist/dispatch/workflow.js +0 -37
- package/dist/dispatch/workflow.js.map +0 -1
- package/dist/domain/artifacts.d.ts +0 -12
- package/dist/domain/artifacts.d.ts.map +0 -1
- package/dist/domain/artifacts.js +0 -14
- package/dist/domain/artifacts.js.map +0 -1
- package/dist/domain/jobs.d.ts +0 -12
- package/dist/domain/jobs.d.ts.map +0 -1
- package/dist/domain/jobs.js +0 -20
- package/dist/domain/jobs.js.map +0 -1
- package/dist/domain/providers.d.ts +0 -10
- package/dist/domain/providers.d.ts.map +0 -1
- package/dist/domain/providers.js +0 -8
- package/dist/domain/providers.js.map +0 -1
- package/dist/domain/runtime.d.ts +0 -14
- package/dist/domain/runtime.d.ts.map +0 -1
- package/dist/domain/runtime.js +0 -10
- package/dist/domain/runtime.js.map +0 -1
- package/dist/domain/sessions.d.ts +0 -12
- package/dist/domain/sessions.d.ts.map +0 -1
- package/dist/domain/sessions.js +0 -57
- package/dist/domain/sessions.js.map +0 -1
- package/dist/examples/localjob.d.ts +0 -2
- package/dist/examples/localjob.d.ts.map +0 -1
- package/dist/examples/localjob.js.map +0 -1
- package/dist/examples/publicapi.d.ts +0 -2
- package/dist/examples/publicapi.d.ts.map +0 -1
- package/dist/examples/publicapi.js.map +0 -1
- package/dist/extension/build.d.ts +0 -9
- package/dist/extension/build.d.ts.map +0 -1
- package/dist/extension/build.js +0 -85
- package/dist/extension/build.js.map +0 -1
- package/dist/extension/content.d.ts +0 -6
- package/dist/extension/content.d.ts.map +0 -1
- package/dist/extension/content.js +0 -135
- package/dist/extension/content.js.map +0 -1
- package/dist/extension/index.d.ts +0 -7
- package/dist/extension/index.d.ts.map +0 -1
- package/dist/extension/index.js +0 -7
- package/dist/extension/index.js.map +0 -1
- package/dist/extension/pagebridge.d.ts +0 -6
- package/dist/extension/pagebridge.d.ts.map +0 -1
- package/dist/extension/pagebridge.js +0 -42
- package/dist/extension/pagebridge.js.map +0 -1
- package/dist/extension/permissions.d.ts +0 -26
- package/dist/extension/permissions.d.ts.map +0 -1
- package/dist/extension/permissions.js +0 -28
- package/dist/extension/permissions.js.map +0 -1
- package/dist/extension/popup.d.ts +0 -5
- package/dist/extension/popup.d.ts.map +0 -1
- package/dist/extension/popup.js +0 -26
- package/dist/extension/popup.js.map +0 -1
- package/dist/extension/protocol.d.ts +0 -58
- package/dist/extension/protocol.d.ts.map +0 -1
- package/dist/extension/protocol.js +0 -101
- package/dist/extension/protocol.js.map +0 -1
- package/dist/extension/serviceworker.d.ts +0 -25
- package/dist/extension/serviceworker.d.ts.map +0 -1
- package/dist/extension/serviceworker.js +0 -102
- package/dist/extension/serviceworker.js.map +0 -1
- package/dist/extension/worker.d.ts +0 -28
- package/dist/extension/worker.d.ts.map +0 -1
- package/dist/extension/worker.js +0 -22
- package/dist/extension/worker.js.map +0 -1
- package/dist/format/check.d.ts +0 -8
- package/dist/format/check.d.ts.map +0 -1
- package/dist/format/check.js +0 -38
- package/dist/format/check.js.map +0 -1
- package/dist/isolation/contracts.d.ts.map +0 -1
- package/dist/isolation/contracts.js.map +0 -1
- package/dist/library/public.d.ts.map +0 -1
- package/dist/library/public.js.map +0 -1
- package/dist/mcp/browser.d.ts +0 -9
- package/dist/mcp/browser.d.ts.map +0 -1
- package/dist/mcp/browser.js +0 -13
- package/dist/mcp/browser.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -35
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -40
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/transport.d.ts +0 -7
- package/dist/mcp/transport.d.ts.map +0 -1
- package/dist/mcp/transport.js +0 -17
- package/dist/mcp/transport.js.map +0 -1
- package/dist/memory/bridge.d.ts +0 -14
- package/dist/memory/bridge.d.ts.map +0 -1
- package/dist/memory/bridge.js +0 -16
- package/dist/memory/bridge.js.map +0 -1
- package/dist/memory/engine.d.ts +0 -38
- package/dist/memory/engine.d.ts.map +0 -1
- package/dist/memory/engine.js +0 -109
- package/dist/memory/engine.js.map +0 -1
- package/dist/memory/modes.d.ts +0 -43
- package/dist/memory/modes.d.ts.map +0 -1
- package/dist/memory/modes.js +0 -66
- package/dist/memory/modes.js.map +0 -1
- package/dist/memory/objects.d.ts +0 -21
- package/dist/memory/objects.d.ts.map +0 -1
- package/dist/memory/objects.js +0 -20
- package/dist/memory/objects.js.map +0 -1
- package/dist/memory/planner.d.ts +0 -66
- package/dist/memory/planner.d.ts.map +0 -1
- package/dist/memory/planner.js +0 -108
- package/dist/memory/planner.js.map +0 -1
- package/dist/memory/targets.d.ts +0 -78
- package/dist/memory/targets.d.ts.map +0 -1
- package/dist/memory/targets.js +0 -29
- package/dist/memory/targets.js.map +0 -1
- package/dist/memory/transforms.d.ts +0 -19
- package/dist/memory/transforms.d.ts.map +0 -1
- package/dist/memory/transforms.js +0 -14
- package/dist/memory/transforms.js.map +0 -1
- package/dist/modes/matrix.d.ts +0 -23
- package/dist/modes/matrix.d.ts.map +0 -1
- package/dist/modes/matrix.js +0 -18
- package/dist/modes/matrix.js.map +0 -1
- package/dist/modes/modes.d.ts +0 -33
- package/dist/modes/modes.d.ts.map +0 -1
- package/dist/modes/modes.js +0 -15
- package/dist/modes/modes.js.map +0 -1
- package/dist/modes/resolve.d.ts +0 -96
- package/dist/modes/resolve.d.ts.map +0 -1
- package/dist/modes/resolve.js +0 -48
- package/dist/modes/resolve.js.map +0 -1
- package/dist/observability/metrics.d.ts +0 -14
- package/dist/observability/metrics.d.ts.map +0 -1
- package/dist/observability/metrics.js +0 -18
- package/dist/observability/metrics.js.map +0 -1
- package/dist/packager/manifest.d.ts +0 -66
- package/dist/packager/manifest.d.ts.map +0 -1
- package/dist/packager/manifest.js +0 -94
- package/dist/packager/manifest.js.map +0 -1
- package/dist/packager/publish.d.ts +0 -51
- package/dist/packager/publish.d.ts.map +0 -1
- package/dist/packager/publish.js +0 -18
- package/dist/packager/publish.js.map +0 -1
- package/dist/packager/targetcli.d.ts +0 -7
- package/dist/packager/targetcli.d.ts.map +0 -1
- package/dist/packager/targetcli.js +0 -22
- package/dist/packager/targetcli.js.map +0 -1
- package/dist/persistence/adapter.d.ts +0 -5
- package/dist/persistence/adapter.d.ts.map +0 -1
- package/dist/persistence/adapter.js +0 -11
- package/dist/persistence/adapter.js.map +0 -1
- package/dist/persistence/drizzle.d.ts +0 -2
- package/dist/persistence/drizzle.d.ts.map +0 -1
- package/dist/persistence/drizzle.js +0 -12
- package/dist/persistence/drizzle.js.map +0 -1
- package/dist/persistence/memory.d.ts +0 -2
- package/dist/persistence/memory.d.ts.map +0 -1
- package/dist/persistence/memory.js +0 -27
- package/dist/persistence/memory.js.map +0 -1
- package/dist/persistence/migrations.d.ts +0 -19
- package/dist/persistence/migrations.d.ts.map +0 -1
- package/dist/persistence/migrations.js +0 -13
- package/dist/persistence/migrations.js.map +0 -1
- package/dist/persistence/prisma.d.ts +0 -2
- package/dist/persistence/prisma.d.ts.map +0 -1
- package/dist/persistence/prisma.js +0 -25
- package/dist/persistence/prisma.js.map +0 -1
- package/dist/persistence/schema.d.ts +0 -63
- package/dist/persistence/schema.d.ts.map +0 -1
- package/dist/persistence/schema.js +0 -28
- package/dist/persistence/schema.js.map +0 -1
- package/dist/persistence/sql.d.ts +0 -3
- package/dist/persistence/sql.d.ts.map +0 -1
- package/dist/persistence/sql.js +0 -37
- package/dist/persistence/sql.js.map +0 -1
- package/dist/protocol/blocks.d.ts +0 -10
- package/dist/protocol/blocks.d.ts.map +0 -1
- package/dist/protocol/blocks.js +0 -26
- package/dist/protocol/blocks.js.map +0 -1
- package/dist/protocol/json.d.ts +0 -6
- package/dist/protocol/json.d.ts.map +0 -1
- package/dist/protocol/json.js +0 -6
- package/dist/protocol/json.js.map +0 -1
- package/dist/protocol/ndjson.d.ts +0 -3
- package/dist/protocol/ndjson.d.ts.map +0 -1
- package/dist/protocol/ndjson.js +0 -20
- package/dist/protocol/ndjson.js.map +0 -1
- package/dist/protocol/sse.d.ts +0 -7
- package/dist/protocol/sse.d.ts.map +0 -1
- package/dist/protocol/sse.js +0 -25
- package/dist/protocol/sse.js.map +0 -1
- package/dist/proxy/pool.d.ts +0 -9
- package/dist/proxy/pool.d.ts.map +0 -1
- package/dist/proxy/pool.js +0 -27
- package/dist/proxy/pool.js.map +0 -1
- package/dist/queue/idempotency.d.ts +0 -10
- package/dist/queue/idempotency.d.ts.map +0 -1
- package/dist/queue/idempotency.js +0 -13
- package/dist/queue/idempotency.js.map +0 -1
- package/dist/queue/persistent.d.ts +0 -14
- package/dist/queue/persistent.d.ts.map +0 -1
- package/dist/queue/persistent.js +0 -66
- package/dist/queue/persistent.js.map +0 -1
- package/dist/queue/queue.d.ts +0 -7
- package/dist/queue/queue.d.ts.map +0 -1
- package/dist/queue/queue.js +0 -55
- package/dist/queue/queue.js.map +0 -1
- package/dist/queue/saga.d.ts +0 -5
- package/dist/queue/saga.d.ts.map +0 -1
- package/dist/queue/saga.js +0 -20
- package/dist/queue/saga.js.map +0 -1
- package/dist/release/assets.d.ts +0 -144
- package/dist/release/assets.d.ts.map +0 -1
- package/dist/release/assets.js +0 -157
- package/dist/release/assets.js.map +0 -1
- package/dist/release/evidence.d.ts +0 -92
- package/dist/release/evidence.d.ts.map +0 -1
- package/dist/release/evidence.js +0 -148
- package/dist/release/evidence.js.map +0 -1
- package/dist/release/verify.d.ts +0 -12
- package/dist/release/verify.d.ts.map +0 -1
- package/dist/release/verify.js +0 -112
- package/dist/release/verify.js.map +0 -1
- package/dist/runners/chain.d.ts +0 -125
- package/dist/runners/chain.d.ts.map +0 -1
- package/dist/runners/chain.js +0 -95
- package/dist/runners/chain.js.map +0 -1
- package/dist/runners/health.d.ts +0 -36
- package/dist/runners/health.d.ts.map +0 -1
- package/dist/runners/health.js +0 -26
- package/dist/runners/health.js.map +0 -1
- package/dist/runners/heartbeat.d.ts +0 -23
- package/dist/runners/heartbeat.d.ts.map +0 -1
- package/dist/runners/heartbeat.js +0 -29
- package/dist/runners/heartbeat.js.map +0 -1
- package/dist/runners/inprocess.d.ts +0 -17
- package/dist/runners/inprocess.d.ts.map +0 -1
- package/dist/runners/inprocess.js +0 -20
- package/dist/runners/inprocess.js.map +0 -1
- package/dist/runners/scheduler.d.ts +0 -5
- package/dist/runners/scheduler.d.ts.map +0 -1
- package/dist/runners/scheduler.js +0 -19
- package/dist/runners/scheduler.js.map +0 -1
- package/dist/runtime/abort.d.ts +0 -8
- package/dist/runtime/abort.d.ts.map +0 -1
- package/dist/runtime/abort.js +0 -13
- package/dist/runtime/abort.js.map +0 -1
- package/dist/runtime/compatibility.d.ts +0 -37
- package/dist/runtime/compatibility.d.ts.map +0 -1
- package/dist/runtime/compatibility.js +0 -10
- package/dist/runtime/compatibility.js.map +0 -1
- package/dist/runtime/detect.d.ts +0 -13
- package/dist/runtime/detect.d.ts.map +0 -1
- package/dist/runtime/detect.js +0 -18
- package/dist/runtime/detect.js.map +0 -1
- package/dist/runtime/engine.d.ts +0 -20
- package/dist/runtime/engine.d.ts.map +0 -1
- package/dist/runtime/engine.js +0 -61
- package/dist/runtime/engine.js.map +0 -1
- package/dist/runtime/retry.d.ts +0 -19
- package/dist/runtime/retry.d.ts.map +0 -1
- package/dist/runtime/retry.js +0 -54
- package/dist/runtime/retry.js.map +0 -1
- package/dist/runtime/worker.d.ts +0 -10
- package/dist/runtime/worker.d.ts.map +0 -1
- package/dist/runtime/worker.js +0 -25
- package/dist/runtime/worker.js.map +0 -1
- package/dist/scrape/cache.d.ts +0 -45
- package/dist/scrape/cache.d.ts.map +0 -1
- package/dist/scrape/cache.js +0 -45
- package/dist/scrape/cache.js.map +0 -1
- package/dist/scrape/crawl.d.ts +0 -47
- package/dist/scrape/crawl.d.ts.map +0 -1
- package/dist/scrape/crawl.js +0 -114
- package/dist/scrape/crawl.js.map +0 -1
- package/dist/scrape/extract.d.ts +0 -63
- package/dist/scrape/extract.d.ts.map +0 -1
- package/dist/scrape/extract.js +0 -47
- package/dist/scrape/extract.js.map +0 -1
- package/dist/scrape/normalize.d.ts +0 -52
- package/dist/scrape/normalize.d.ts.map +0 -1
- package/dist/scrape/normalize.js +0 -102
- package/dist/scrape/normalize.js.map +0 -1
- package/dist/scrape/robots.d.ts +0 -37
- package/dist/scrape/robots.d.ts.map +0 -1
- package/dist/scrape/robots.js +0 -71
- package/dist/scrape/robots.js.map +0 -1
- package/dist/scrape/schema.d.ts +0 -23
- package/dist/scrape/schema.d.ts.map +0 -1
- package/dist/scrape/schema.js +0 -92
- package/dist/scrape/schema.js.map +0 -1
- package/dist/scrape/scraper.d.ts +0 -6
- package/dist/scrape/scraper.d.ts.map +0 -1
- package/dist/scrape/scraper.js +0 -46
- package/dist/scrape/scraper.js.map +0 -1
- package/dist/scrape/semantic.d.ts +0 -25
- package/dist/scrape/semantic.d.ts.map +0 -1
- package/dist/scrape/semantic.js +0 -27
- package/dist/scrape/semantic.js.map +0 -1
- package/dist/server/node.d.ts +0 -7
- package/dist/server/node.d.ts.map +0 -1
- package/dist/server/node.js.map +0 -1
- package/dist/sessions/file.d.ts +0 -25
- package/dist/sessions/file.d.ts.map +0 -1
- package/dist/sessions/file.js +0 -13
- package/dist/sessions/file.js.map +0 -1
- package/dist/sessions/replay.d.ts +0 -9
- package/dist/sessions/replay.d.ts.map +0 -1
- package/dist/sessions/replay.js +0 -70
- package/dist/sessions/replay.js.map +0 -1
- package/dist/sessions/store.d.ts +0 -25
- package/dist/sessions/store.d.ts.map +0 -1
- package/dist/sessions/store.js +0 -13
- package/dist/sessions/store.js.map +0 -1
- package/dist/storage/adapter.d.ts +0 -5
- package/dist/storage/adapter.d.ts.map +0 -1
- package/dist/storage/adapter.js +0 -11
- package/dist/storage/adapter.js.map +0 -1
- package/dist/storage/cache.d.ts +0 -23
- package/dist/storage/cache.d.ts.map +0 -1
- package/dist/storage/cache.js +0 -79
- package/dist/storage/cache.js.map +0 -1
- package/dist/storage/checksum.d.ts +0 -3
- package/dist/storage/checksum.d.ts.map +0 -1
- package/dist/storage/checksum.js +0 -19
- package/dist/storage/checksum.js.map +0 -1
- package/dist/storage/chunked.d.ts +0 -21
- package/dist/storage/chunked.d.ts.map +0 -1
- package/dist/storage/chunked.js +0 -67
- package/dist/storage/chunked.js.map +0 -1
- package/dist/storage/content.d.ts +0 -24
- package/dist/storage/content.d.ts.map +0 -1
- package/dist/storage/content.js +0 -44
- package/dist/storage/content.js.map +0 -1
- package/dist/storage/filehosting.d.ts +0 -2
- package/dist/storage/filehosting.d.ts.map +0 -1
- package/dist/storage/filehosting.js +0 -24
- package/dist/storage/filehosting.js.map +0 -1
- package/dist/storage/githubcontents.d.ts +0 -2
- package/dist/storage/githubcontents.d.ts.map +0 -1
- package/dist/storage/githubcontents.js +0 -29
- package/dist/storage/githubcontents.js.map +0 -1
- package/dist/storage/index.d.ts +0 -12
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js +0 -12
- package/dist/storage/index.js.map +0 -1
- package/dist/storage/local.d.ts +0 -2
- package/dist/storage/local.d.ts.map +0 -1
- package/dist/storage/local.js +0 -72
- package/dist/storage/local.js.map +0 -1
- package/dist/storage/memory.d.ts +0 -6
- package/dist/storage/memory.d.ts.map +0 -1
- package/dist/storage/memory.js +0 -36
- package/dist/storage/memory.js.map +0 -1
- package/dist/storage/pool.d.ts +0 -95
- package/dist/storage/pool.d.ts.map +0 -1
- package/dist/storage/pool.js +0 -202
- package/dist/storage/pool.js.map +0 -1
- package/dist/storage/s3compatible.d.ts +0 -3
- package/dist/storage/s3compatible.d.ts.map +0 -1
- package/dist/storage/s3compatible.js +0 -70
- package/dist/storage/s3compatible.js.map +0 -1
- package/dist/storage/sync.d.ts +0 -47
- package/dist/storage/sync.d.ts.map +0 -1
- package/dist/storage/sync.js +0 -75
- package/dist/storage/sync.js.map +0 -1
- package/dist/surfaces/adapters.d.ts +0 -97
- package/dist/surfaces/adapters.d.ts.map +0 -1
- package/dist/surfaces/adapters.js +0 -50
- package/dist/surfaces/adapters.js.map +0 -1
- package/dist/surfaces/controls.d.ts +0 -18
- package/dist/surfaces/controls.d.ts.map +0 -1
- package/dist/surfaces/controls.js +0 -45
- package/dist/surfaces/controls.js.map +0 -1
- package/dist/surfaces/manifest.d.ts +0 -68
- package/dist/surfaces/manifest.d.ts.map +0 -1
- package/dist/surfaces/manifest.js +0 -42
- package/dist/surfaces/manifest.js.map +0 -1
- package/dist/surfaces/n8n.d.ts +0 -25
- package/dist/surfaces/n8n.d.ts.map +0 -1
- package/dist/surfaces/n8n.js +0 -31
- package/dist/surfaces/n8n.js.map +0 -1
- package/dist/surfaces/operations.d.ts +0 -32
- package/dist/surfaces/operations.d.ts.map +0 -1
- package/dist/surfaces/operations.js +0 -53
- package/dist/surfaces/operations.js.map +0 -1
- package/dist/surfaces/requirements.d.ts +0 -29
- package/dist/surfaces/requirements.d.ts.map +0 -1
- package/dist/surfaces/requirements.js +0 -50
- package/dist/surfaces/requirements.js.map +0 -1
- package/dist/surfaces/targets.d.ts +0 -196
- package/dist/surfaces/targets.d.ts.map +0 -1
- package/dist/surfaces/targets.js +0 -27
- package/dist/surfaces/targets.js.map +0 -1
- package/dist/webhook/delivery.d.ts +0 -18
- package/dist/webhook/delivery.d.ts.map +0 -1
- package/dist/webhook/delivery.js +0 -36
- package/dist/webhook/delivery.js.map +0 -1
- package/dist/webhook/receiver.d.ts +0 -26
- package/dist/webhook/receiver.d.ts.map +0 -1
- package/dist/webhook/receiver.js +0 -24
- package/dist/webhook/receiver.js.map +0 -1
- package/dist/webhook/signature.d.ts +0 -3
- package/dist/webhook/signature.d.ts.map +0 -1
- package/dist/webhook/signature.js +0 -7
- package/dist/webhook/signature.js.map +0 -1
- package/dist/workflow/manifest.d.ts +0 -22
- package/dist/workflow/manifest.d.ts.map +0 -1
- package/dist/workflow/manifest.js +0 -21
- package/dist/workflow/manifest.js.map +0 -1
- package/dist/workflow/registry.d.ts +0 -7
- package/dist/workflow/registry.d.ts.map +0 -1
- package/dist/workflow/registry.js +0 -18
- package/dist/workflow/registry.js.map +0 -1
- package/dist/workflow/templates.d.ts +0 -6
- package/dist/workflow/templates.d.ts.map +0 -1
- package/dist/workflow/templates.js +0 -18
- package/dist/workflow/templates.js.map +0 -1
- package/dist/workflow/triggers.d.ts +0 -64
- package/dist/workflow/triggers.d.ts.map +0 -1
- package/dist/workflow/triggers.js +0 -99
- package/dist/workflow/triggers.js.map +0 -1
- /package/{extension/README.md → docs/extension.md} +0 -0
package/quantum.ts
ADDED
|
@@ -0,0 +1,2393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* quantum.ts — classical quantum-simulation layer for saddle v18.
|
|
3
|
+
*
|
|
4
|
+
* the module is a 100 percent classical software statevector simulator:
|
|
5
|
+
* no hardware, no photons, no qpu. the engine "supports" quantum the same
|
|
6
|
+
* way it virtualizes cores, memory and gpus — as one more virtualization
|
|
7
|
+
* layer: every "qubit" is a pair of float64 lanes inside a 2^n amplitude
|
|
8
|
+
* vector (interleaved re/im), every "gate" is an in-place index-pair
|
|
9
|
+
* transformation and every "measurement" is a crypto-seeded collapse of
|
|
10
|
+
* that vector.
|
|
11
|
+
*
|
|
12
|
+
* contexts (22):
|
|
13
|
+
* 01 version anchors (algorithm dates, simulator envelope)
|
|
14
|
+
* 02 error catcher (quantumerror envelope)
|
|
15
|
+
* 03 complex math kernel (2x2 unitary type + gate matrices)
|
|
16
|
+
* 04 quantumsim statevector core (1-20 qubits, Disposable)
|
|
17
|
+
* 05 multi-qubit gate kernels (cnot, cz, toffoli, swap, ccz)
|
|
18
|
+
* 06 canonical circuits (bellstate, ghzstate)
|
|
19
|
+
* 07 grover search (grover2 provable, grover3 statistical)
|
|
20
|
+
* 08 deutsch oracle discrimination (deutsch)
|
|
21
|
+
* 09 quantum teleportation sketch (qteleport)
|
|
22
|
+
* 10 bb84 stations (alice, bob, eve over a simulated channel)
|
|
23
|
+
* 11 bb84 protocol runner (runbb84: sifting, qber, intercept detection)
|
|
24
|
+
* 12 e91 entangled pairs + chsh (rune91)
|
|
25
|
+
* 13 qrng (quantuminspiredrandom, honest anu fallback)
|
|
26
|
+
* 14 dnavault codec (goldman 2013 ternary rotation, framing, strands)
|
|
27
|
+
* 15 dnavault decode (checksum verification, redundancy, roundtrip)
|
|
28
|
+
* 16 dnavault capacity planner (215 pb/g, planodefault)
|
|
29
|
+
* 17 optical 5d quartz planner (360 tb/disc, femtosecond write plan)
|
|
30
|
+
* 18 holographic note (microsoft hsd) and storage compare
|
|
31
|
+
* 19 qram bucket brigade (giovannetti 2008, o(log n) routing, n=8)
|
|
32
|
+
* 20 shor plan (honest: theoretical 7-qubit circuit, never executed)
|
|
33
|
+
* 21 registry (quantumregistry capability catalog)
|
|
34
|
+
* 22 self check (quantumselfcheck: every claim, machine-verified)
|
|
35
|
+
*
|
|
36
|
+
* rules: lowercase identifiers, english jsdoc third person, no emoji,
|
|
37
|
+
* try/catch catcher on every fallible path, node:* built-ins only
|
|
38
|
+
* (node:crypto for all randomness), zero runtime dependencies, no
|
|
39
|
+
* filesystem, network or process coupling beyond the optional,
|
|
40
|
+
* always-fallback anu qrng fetch. ascii only.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { randomBytes, randomInt } from 'node:crypto';
|
|
44
|
+
|
|
45
|
+
/* ------------------------------------------------------------------ */
|
|
46
|
+
/* context: version anchors (quantum simulation envelope) */
|
|
47
|
+
/* ------------------------------------------------------------------ */
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* frozen quantum layer anchors. every algorithm keeps its literature
|
|
51
|
+
* year so the registry can cite primary sources instead of folklore.
|
|
52
|
+
*/
|
|
53
|
+
export const quantumversions = {
|
|
54
|
+
date: '2026-08-23',
|
|
55
|
+
simulator: 'statevector',
|
|
56
|
+
precision: 'float64 interleaved re/im',
|
|
57
|
+
maxqubits: 20,
|
|
58
|
+
amplitudelanes: 2097152,
|
|
59
|
+
grover: '1996 stoc',
|
|
60
|
+
deutschjozsa: '1992 proc r soc a',
|
|
61
|
+
bb84: '1984 bangalore proceedings',
|
|
62
|
+
e91: '1991 prl 67 661',
|
|
63
|
+
teleportation: '1993 prl 70 1895',
|
|
64
|
+
qram: '2008 prl 100 160501',
|
|
65
|
+
} as const satisfies Record<string, string | number>;
|
|
66
|
+
|
|
67
|
+
/* ------------------------------------------------------------------ */
|
|
68
|
+
/* context: error catcher */
|
|
69
|
+
/* ------------------------------------------------------------------ */
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* quantum layer failure envelope. every guard in this module throws it
|
|
73
|
+
* instead of a bare error so callers can branch on the code.
|
|
74
|
+
*/
|
|
75
|
+
export class quantumerror extends Error {
|
|
76
|
+
readonly code: string;
|
|
77
|
+
readonly detail?: string;
|
|
78
|
+
|
|
79
|
+
constructor(params: { code: string; message: string; detail?: string }) {
|
|
80
|
+
super(params.message);
|
|
81
|
+
this.name = 'quantumerror';
|
|
82
|
+
this.code = params.code;
|
|
83
|
+
this.detail = params.detail;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* ------------------------------------------------------------------ */
|
|
88
|
+
/* context: complex math kernel (2x2 unitaries) */
|
|
89
|
+
/* ------------------------------------------------------------------ */
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* a 2x2 complex unitary with rows and columns interleaved as
|
|
93
|
+
* [u00re, u00im, u01re, u01im, u10re, u10im, u11re, u11im].
|
|
94
|
+
* gate matrices are plain float tuples so the hot loop stays monomorphic.
|
|
95
|
+
*/
|
|
96
|
+
export type gate2x2 = readonly [number, number, number, number, number, number, number, number];
|
|
97
|
+
|
|
98
|
+
const invsqrt2 = Math.SQRT1_2;
|
|
99
|
+
|
|
100
|
+
/** hadamard matrix ((1/sqrt 2) [[1,1],[1,1]]). */
|
|
101
|
+
export const hmatrix: gate2x2 = [invsqrt2, 0, invsqrt2, 0, invsqrt2, 0, invsqrt2, 0];
|
|
102
|
+
|
|
103
|
+
/** pauli x matrix [[0,1],[1,0]]. */
|
|
104
|
+
export const xmatrix: gate2x2 = [0, 0, 1, 0, 1, 0, 0, 0];
|
|
105
|
+
|
|
106
|
+
/** pauli y matrix [[0,-i],[i,0]]. */
|
|
107
|
+
export const ymatrix: gate2x2 = [0, 0, 0, -1, 0, 1, 0, 0];
|
|
108
|
+
|
|
109
|
+
/** pauli z matrix diag(1,-1). */
|
|
110
|
+
export const zmatrix: gate2x2 = [1, 0, 0, 0, 0, 0, -1, 0];
|
|
111
|
+
|
|
112
|
+
/** phase gate s = diag(1, i). */
|
|
113
|
+
export const smatrix: gate2x2 = [1, 0, 0, 0, 0, 0, 0, 1];
|
|
114
|
+
|
|
115
|
+
/** inverse phase gate sdg = diag(1, -i). */
|
|
116
|
+
export const sdgmatrix: gate2x2 = [1, 0, 0, 0, 0, 0, 0, -1];
|
|
117
|
+
|
|
118
|
+
/** t gate = diag(1, exp(i pi/4)). */
|
|
119
|
+
export const tmatrix: gate2x2 = [1, 0, 0, 0, 0, 0, invsqrt2, invsqrt2];
|
|
120
|
+
|
|
121
|
+
/** inverse t gate = diag(1, exp(-i pi/4)). */
|
|
122
|
+
export const tdgmatrix: gate2x2 = [1, 0, 0, 0, 0, 0, invsqrt2, -invsqrt2];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* builds the rotation-x matrix rx(theta) =
|
|
126
|
+
* [[cos(t/2), -i sin(t/2)], [-i sin(t/2), cos(t/2)]].
|
|
127
|
+
*/
|
|
128
|
+
export function rxmatrix(theta: number): gate2x2 {
|
|
129
|
+
const c = Math.cos(theta / 2);
|
|
130
|
+
const s = Math.sin(theta / 2);
|
|
131
|
+
return [c, 0, 0, -s, 0, -s, c, 0];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* builds the rotation-y matrix ry(theta) =
|
|
136
|
+
* [[cos(t/2), -sin(t/2)], [sin(t/2), cos(t/2)]]; ry(2a)|0> equals the
|
|
137
|
+
* polarization state cos(a)|0> + sin(a)|1>, which the e91 context uses
|
|
138
|
+
* to prepare and measure at arbitrary analyzer angles.
|
|
139
|
+
*/
|
|
140
|
+
export function rymatrix(theta: number): gate2x2 {
|
|
141
|
+
const c = Math.cos(theta / 2);
|
|
142
|
+
const s = Math.sin(theta / 2);
|
|
143
|
+
return [c, 0, -s, 0, s, 0, c, 0];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* builds the rotation-z matrix rz(theta) = diag(e^{-i t/2}, e^{i t/2}).
|
|
148
|
+
*/
|
|
149
|
+
export function rzmatrix(theta: number): gate2x2 {
|
|
150
|
+
const c = Math.cos(theta / 2);
|
|
151
|
+
const s = Math.sin(theta / 2);
|
|
152
|
+
return [c, -s, 0, 0, 0, 0, c, s];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* ------------------------------------------------------------------ */
|
|
156
|
+
/* context: quantumsim statevector core (1-20 qubits, Disposable) */
|
|
157
|
+
/* ------------------------------------------------------------------ */
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* pure-software statevector simulator. the state is one Float64Array of
|
|
161
|
+
* 2 * 2^n lanes holding interleaved real and imaginary parts; qubit q is
|
|
162
|
+
* bit q of the basis index (qubit 0 is the least significant bit), so
|
|
163
|
+
* for a single-qubit gate on qubit q the kernel iterates every index i
|
|
164
|
+
* with bit q = 0, takes j = i | 2^q and replaces the pair by
|
|
165
|
+
* u @ [amp(i), amp(j)] exactly as specified. the class is Disposable and
|
|
166
|
+
* meant to be used with `using` so amplitude memory is logically freed
|
|
167
|
+
* even though the gc owns it.
|
|
168
|
+
*/
|
|
169
|
+
export class quantumsim implements Disposable {
|
|
170
|
+
#amp: Float64Array;
|
|
171
|
+
readonly #n: number;
|
|
172
|
+
#disposed = false;
|
|
173
|
+
|
|
174
|
+
constructor(nqubits: number) {
|
|
175
|
+
if (!Number.isInteger(nqubits) || nqubits < 1 || nqubits > 20) {
|
|
176
|
+
throw new quantumerror({
|
|
177
|
+
code: 'qubitrange',
|
|
178
|
+
message: `quantumsim supports 1 to 20 qubits, got ${nqubits}`,
|
|
179
|
+
detail: '2^n amplitudes with n > 20 exceed the float64 lane budget',
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
this.#n = nqubits;
|
|
183
|
+
this.#amp = new Float64Array(2 << nqubits);
|
|
184
|
+
this.#amp[0] = 1;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** number of qubits of the register. */
|
|
188
|
+
get nqubits(): number {
|
|
189
|
+
return this.#n;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** hilbert space dimension 2^n. */
|
|
193
|
+
get dim(): number {
|
|
194
|
+
return 1 << this.#n;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** true once Symbol.dispose released the state. */
|
|
198
|
+
get disposed(): boolean {
|
|
199
|
+
return this.#disposed;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#guardlive(): void {
|
|
203
|
+
if (this.#disposed) {
|
|
204
|
+
throw new quantumerror({
|
|
205
|
+
code: 'disposed',
|
|
206
|
+
message: 'quantumsim state was disposed; create a new register',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#guardqubit(q: number, gate: string): void {
|
|
212
|
+
if (!Number.isInteger(q) || q < 0 || q >= this.#n) {
|
|
213
|
+
throw new quantumerror({
|
|
214
|
+
code: 'qubitrange',
|
|
215
|
+
message: `gate ${gate} needs a qubit in [0, ${this.#n - 1}], got ${q}`,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* applies a 2x2 unitary to qubit q by visiting each amplitude pair
|
|
222
|
+
* (i, i|2^q) once; this is the mathematical core every named
|
|
223
|
+
* single-qubit gate forwards to.
|
|
224
|
+
*/
|
|
225
|
+
#apply1(q: number, m: gate2x2, gate: string): this {
|
|
226
|
+
this.#guardlive();
|
|
227
|
+
this.#guardqubit(q, gate);
|
|
228
|
+
const amp = this.#amp;
|
|
229
|
+
const mask = 1 << q;
|
|
230
|
+
const [u00re, u00im, u01re, u01im, u10re, u10im, u11re, u11im] = m;
|
|
231
|
+
for (let i = 0; i < this.dim; i++) {
|
|
232
|
+
if ((i & mask) !== 0) continue;
|
|
233
|
+
const j = i | mask;
|
|
234
|
+
const i2 = i << 1;
|
|
235
|
+
const j2 = j << 1;
|
|
236
|
+
const are = amp[i2];
|
|
237
|
+
const aim = amp[i2 + 1];
|
|
238
|
+
const bre = amp[j2];
|
|
239
|
+
const bim = amp[j2 + 1];
|
|
240
|
+
amp[i2] = u00re * are - u00im * aim + u01re * bre - u01im * bim;
|
|
241
|
+
amp[i2 + 1] = u00re * aim + u00im * are + u01re * bim + u01im * bre;
|
|
242
|
+
amp[j2] = u10re * are - u10im * aim + u11re * bre - u11im * bim;
|
|
243
|
+
amp[j2 + 1] = u10re * aim + u10im * are + u11re * bim + u11im * bre;
|
|
244
|
+
}
|
|
245
|
+
return this;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** hadamard on qubit q. */
|
|
249
|
+
h(q: number): this {
|
|
250
|
+
return this.#apply1(q, hmatrix, 'h');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** pauli x (bit flip) on qubit q. */
|
|
254
|
+
x(q: number): this {
|
|
255
|
+
return this.#apply1(q, xmatrix, 'x');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** pauli y on qubit q. */
|
|
259
|
+
y(q: number): this {
|
|
260
|
+
return this.#apply1(q, ymatrix, 'y');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** pauli z (phase flip) on qubit q. */
|
|
264
|
+
z(q: number): this {
|
|
265
|
+
return this.#apply1(q, zmatrix, 'z');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** phase gate s = sqrt(z) on qubit q. */
|
|
269
|
+
s(q: number): this {
|
|
270
|
+
return this.#apply1(q, smatrix, 's');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/** inverse phase gate on qubit q. */
|
|
274
|
+
sdg(q: number): this {
|
|
275
|
+
return this.#apply1(q, sdgmatrix, 'sdg');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** t = pi/8 gate on qubit q. */
|
|
279
|
+
t(q: number): this {
|
|
280
|
+
return this.#apply1(q, tmatrix, 't');
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** inverse t gate on qubit q. */
|
|
284
|
+
tdg(q: number): this {
|
|
285
|
+
return this.#apply1(q, tdgmatrix, 'tdg');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** rotation-x by theta radians on qubit q. */
|
|
289
|
+
rx(theta: number, q: number): this {
|
|
290
|
+
return this.#apply1(q, rxmatrix(theta), `rx(${theta})`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** rotation-y by theta radians on qubit q. */
|
|
294
|
+
ry(theta: number, q: number): this {
|
|
295
|
+
return this.#apply1(q, rymatrix(theta), `ry(${theta})`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** rotation-z by theta radians on qubit q. */
|
|
299
|
+
rz(theta: number, q: number): this {
|
|
300
|
+
return this.#apply1(q, rzmatrix(theta), `rz(${theta})`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* cnot: for every index i with control bit 1 and target bit 0,
|
|
305
|
+
* swaps amplitudes i and i|2^target; each pair is visited once.
|
|
306
|
+
*/
|
|
307
|
+
cnot(control: number, target: number): this {
|
|
308
|
+
this.#guardlive();
|
|
309
|
+
this.#guardqubit(control, 'cnot');
|
|
310
|
+
this.#guardqubit(target, 'cnot');
|
|
311
|
+
if (control === target) {
|
|
312
|
+
throw new quantumerror({
|
|
313
|
+
code: 'qubitcollision',
|
|
314
|
+
message: `cnot control and target are both qubit ${control}`,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
const amp = this.#amp;
|
|
318
|
+
const maskc = 1 << control;
|
|
319
|
+
const maskt = 1 << target;
|
|
320
|
+
for (let i = 0; i < this.dim; i++) {
|
|
321
|
+
if ((i & maskc) === 0 || (i & maskt) !== 0) continue;
|
|
322
|
+
this.#swappair(amp, i, i | maskt);
|
|
323
|
+
}
|
|
324
|
+
return this;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* cz: flips the sign of every amplitude whose control and target
|
|
329
|
+
* bits are both 1.
|
|
330
|
+
*/
|
|
331
|
+
cz(control: number, target: number): this {
|
|
332
|
+
this.#guardlive();
|
|
333
|
+
this.#guardqubit(control, 'cz');
|
|
334
|
+
this.#guardqubit(target, 'cz');
|
|
335
|
+
if (control === target) {
|
|
336
|
+
throw new quantumerror({
|
|
337
|
+
code: 'qubitcollision',
|
|
338
|
+
message: `cz control and target are both qubit ${control}`,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
const amp = this.#amp;
|
|
342
|
+
const mask = (1 << control) | (1 << target);
|
|
343
|
+
for (let i = 0; i < this.dim; i++) {
|
|
344
|
+
if ((i & mask) !== mask) continue;
|
|
345
|
+
const i2 = i << 1;
|
|
346
|
+
amp[i2] = -amp[i2];
|
|
347
|
+
amp[i2 + 1] = -amp[i2 + 1];
|
|
348
|
+
}
|
|
349
|
+
return this;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* toffoli (ccnot): swaps amplitude pairs only when both control bits
|
|
354
|
+
* are 1 and the target bit is 0.
|
|
355
|
+
*/
|
|
356
|
+
toffoli(control1: number, control2: number, target: number): this {
|
|
357
|
+
this.#guardlive();
|
|
358
|
+
this.#guardqubit(control1, 'toffoli');
|
|
359
|
+
this.#guardqubit(control2, 'toffoli');
|
|
360
|
+
this.#guardqubit(target, 'toffoli');
|
|
361
|
+
if (control1 === target || control2 === target || control1 === control2) {
|
|
362
|
+
throw new quantumerror({
|
|
363
|
+
code: 'qubitcollision',
|
|
364
|
+
message: `toffoli needs three distinct qubits, got ${control1}, ${control2}, ${target}`,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
const amp = this.#amp;
|
|
368
|
+
const mask1 = 1 << control1;
|
|
369
|
+
const mask2 = 1 << control2;
|
|
370
|
+
const maskt = 1 << target;
|
|
371
|
+
for (let i = 0; i < this.dim; i++) {
|
|
372
|
+
if ((i & mask1) === 0 || (i & mask2) === 0 || (i & maskt) !== 0) continue;
|
|
373
|
+
this.#swappair(amp, i, i | maskt);
|
|
374
|
+
}
|
|
375
|
+
return this;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* swap: exchanges qubits a and b by visiting each index pair once
|
|
380
|
+
* (bit a = 0 with bit b = 1 maps to its complement bit a = 1, bit
|
|
381
|
+
* b = 0), so no pair is touched twice.
|
|
382
|
+
*/
|
|
383
|
+
swap(a: number, b: number): this {
|
|
384
|
+
this.#guardlive();
|
|
385
|
+
this.#guardqubit(a, 'swap');
|
|
386
|
+
this.#guardqubit(b, 'swap');
|
|
387
|
+
if (a === b) {
|
|
388
|
+
throw new quantumerror({
|
|
389
|
+
code: 'qubitcollision',
|
|
390
|
+
message: `swap needs two distinct qubits, got ${a} twice`,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
const amp = this.#amp;
|
|
394
|
+
const maska = 1 << a;
|
|
395
|
+
const maskb = 1 << b;
|
|
396
|
+
for (let i = 0; i < this.dim; i++) {
|
|
397
|
+
if ((i & maska) !== 0 || (i & maskb) === 0) continue;
|
|
398
|
+
this.#swappair(amp, i, (i | maska) & ~maskb);
|
|
399
|
+
}
|
|
400
|
+
return this;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/** swaps the two interleaved complex amplitudes at basis indexes. */
|
|
404
|
+
#swappair(amp: Float64Array, i: number, j: number): void {
|
|
405
|
+
const i2 = i << 1;
|
|
406
|
+
const j2 = j << 1;
|
|
407
|
+
const re = amp[i2];
|
|
408
|
+
const im = amp[i2 + 1];
|
|
409
|
+
amp[i2] = amp[j2];
|
|
410
|
+
amp[i2 + 1] = amp[j2 + 1];
|
|
411
|
+
amp[j2] = re;
|
|
412
|
+
amp[j2 + 1] = im;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* measurement outcome sampler: draws one uniform from node:crypto
|
|
417
|
+
* (randomInt over 2^30 buckets, finer than any probability gap this
|
|
418
|
+
* module produces), compares it with the |1> branch probability and
|
|
419
|
+
* returns the observed branch.
|
|
420
|
+
*/
|
|
421
|
+
#sample(pone: number): 0 | 1 {
|
|
422
|
+
const roll = randomInt(0, 0x40000000) / 0x40000000;
|
|
423
|
+
return roll < pone ? 1 : 0;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* measures qubit q in the computational basis, collapsing the state.
|
|
428
|
+
* returns the observed classical bit.
|
|
429
|
+
*/
|
|
430
|
+
measure(q: number): 0 | 1 {
|
|
431
|
+
this.#guardlive();
|
|
432
|
+
this.#guardqubit(q, 'measure');
|
|
433
|
+
const amp = this.#amp;
|
|
434
|
+
const mask = 1 << q;
|
|
435
|
+
let pone = 0;
|
|
436
|
+
for (let i = 0; i < this.dim; i++) {
|
|
437
|
+
if ((i & mask) === 0) continue;
|
|
438
|
+
const i2 = i << 1;
|
|
439
|
+
pone += amp[i2] * amp[i2] + amp[i2 + 1] * amp[i2 + 1];
|
|
440
|
+
}
|
|
441
|
+
const outcome = this.#sample(pone);
|
|
442
|
+
const keep = outcome === 1 ? mask : 0;
|
|
443
|
+
let mass = 0;
|
|
444
|
+
for (let i = 0; i < this.dim; i++) {
|
|
445
|
+
const i2 = i << 1;
|
|
446
|
+
if ((i & mask) !== keep) {
|
|
447
|
+
amp[i2] = 0;
|
|
448
|
+
amp[i2 + 1] = 0;
|
|
449
|
+
} else {
|
|
450
|
+
mass += amp[i2] * amp[i2] + amp[i2 + 1] * amp[i2 + 1];
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (mass <= 0) {
|
|
454
|
+
throw new quantumerror({
|
|
455
|
+
code: 'zerovector',
|
|
456
|
+
message: `collapse of qubit ${q} hit a zero-mass branch`,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
const scale = 1 / Math.sqrt(mass);
|
|
460
|
+
for (let i = 0; i < this.#amp.length; i++) {
|
|
461
|
+
this.#amp[i] *= scale;
|
|
462
|
+
}
|
|
463
|
+
return outcome;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* measures the full register in the computational basis and returns
|
|
468
|
+
* the observed basis index (qubit 0 in bit 0).
|
|
469
|
+
*/
|
|
470
|
+
measureall(): number {
|
|
471
|
+
this.#guardlive();
|
|
472
|
+
const amp = this.#amp;
|
|
473
|
+
const roll = randomInt(0, 0x40000000) / 0x40000000;
|
|
474
|
+
let acc = 0;
|
|
475
|
+
let pick = this.dim - 1;
|
|
476
|
+
for (let i = 0; i < this.dim; i++) {
|
|
477
|
+
const i2 = i << 1;
|
|
478
|
+
acc += amp[i2] * amp[i2] + amp[i2 + 1] * amp[i2 + 1];
|
|
479
|
+
if (roll < acc) {
|
|
480
|
+
pick = i;
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
amp.fill(0);
|
|
485
|
+
amp[pick << 1] = 1;
|
|
486
|
+
return pick;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* returns the [p0, p1] distribution of qubit q without collapsing;
|
|
491
|
+
* p1 = sum of |amplitude|^2 over indices with bit q set.
|
|
492
|
+
*/
|
|
493
|
+
probs(q: number): [number, number] {
|
|
494
|
+
this.#guardlive();
|
|
495
|
+
this.#guardqubit(q, 'probs');
|
|
496
|
+
const amp = this.#amp;
|
|
497
|
+
const mask = 1 << q;
|
|
498
|
+
let pone = 0;
|
|
499
|
+
for (let i = 0; i < this.dim; i++) {
|
|
500
|
+
if ((i & mask) === 0) continue;
|
|
501
|
+
const i2 = i << 1;
|
|
502
|
+
pone += amp[i2] * amp[i2] + amp[i2 + 1] * amp[i2 + 1];
|
|
503
|
+
}
|
|
504
|
+
return [1 - pone, pone];
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* returns the full |amplitude|^2 distribution over all 2^n basis
|
|
509
|
+
* states as a fresh Float64Array (index order: qubit 0 in bit 0).
|
|
510
|
+
*/
|
|
511
|
+
probabilities(): Float64Array {
|
|
512
|
+
this.#guardlive();
|
|
513
|
+
const amp = this.#amp;
|
|
514
|
+
const out = new Float64Array(this.dim);
|
|
515
|
+
for (let i = 0; i < this.dim; i++) {
|
|
516
|
+
const i2 = i << 1;
|
|
517
|
+
out[i] = amp[i2] * amp[i2] + amp[i2 + 1] * amp[i2 + 1];
|
|
518
|
+
}
|
|
519
|
+
return out;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** returns the complex amplitude of one basis index as [re, im]. */
|
|
523
|
+
amplitude(index: number): readonly [number, number] {
|
|
524
|
+
this.#guardlive();
|
|
525
|
+
if (!Number.isInteger(index) || index < 0 || index >= this.dim) {
|
|
526
|
+
throw new quantumerror({
|
|
527
|
+
code: 'indexrange',
|
|
528
|
+
message: `amplitude index must be in [0, ${this.dim - 1}], got ${index}`,
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
const i2 = index << 1;
|
|
532
|
+
return [this.#amp[i2], this.#amp[i2 + 1]];
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/** returns a detached readonly snapshot copy of the state lanes. */
|
|
536
|
+
state(): Readonly<Float64Array> {
|
|
537
|
+
this.#guardlive();
|
|
538
|
+
return this.#amp.slice();
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/** euclidean norm of the state vector (should stay within 1e-12 of 1). */
|
|
542
|
+
norm(): number {
|
|
543
|
+
this.#guardlive();
|
|
544
|
+
let sum = 0;
|
|
545
|
+
for (let i = 0; i < this.#amp.length; i++) {
|
|
546
|
+
sum += this.#amp[i] * this.#amp[i];
|
|
547
|
+
}
|
|
548
|
+
return Math.sqrt(sum);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/** renormalizes the state to unit norm (fixes float drift). */
|
|
552
|
+
normalize(): this {
|
|
553
|
+
this.#guardlive();
|
|
554
|
+
const n = this.norm();
|
|
555
|
+
if (n <= 0) {
|
|
556
|
+
throw new quantumerror({
|
|
557
|
+
code: 'zerovector',
|
|
558
|
+
message: 'cannot normalize a zero vector',
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
for (let i = 0; i < this.#amp.length; i++) {
|
|
562
|
+
this.#amp[i] /= n;
|
|
563
|
+
}
|
|
564
|
+
return this;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/** resets the register to |0...0>. */
|
|
568
|
+
reset(): this {
|
|
569
|
+
this.#guardlive();
|
|
570
|
+
this.#amp.fill(0);
|
|
571
|
+
this.#amp[0] = 1;
|
|
572
|
+
return this;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/** releases the amplitude lanes; any further gate call throws. */
|
|
576
|
+
[Symbol.dispose](): void {
|
|
577
|
+
this.#amp = new Float64Array(0);
|
|
578
|
+
this.#disposed = true;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* ------------------------------------------------------------------ */
|
|
583
|
+
/* context: multi-qubit gate kernels (module-level forms) */
|
|
584
|
+
/* ------------------------------------------------------------------ */
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* applies cnot(control, target) to sim; functional spelling of the
|
|
588
|
+
* quantumsim method for composition outside the class.
|
|
589
|
+
*/
|
|
590
|
+
export function cnot(sim: quantumsim, control: number, target: number): quantumsim {
|
|
591
|
+
return sim.cnot(control, target);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* applies cz(control, target) to sim; the grover oracle for two qubits
|
|
596
|
+
* is exactly this gate on the |11> amplitude.
|
|
597
|
+
*/
|
|
598
|
+
export function cz(sim: quantumsim, control: number, target: number): quantumsim {
|
|
599
|
+
return sim.cz(control, target);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* applies toffoli(control1, control2, target) to sim, the universal
|
|
604
|
+
* reversible gate of toffoli 1980 as used in every compiled oracle.
|
|
605
|
+
*/
|
|
606
|
+
export function toffoli(
|
|
607
|
+
sim: quantumsim,
|
|
608
|
+
control1: number,
|
|
609
|
+
control2: number,
|
|
610
|
+
target: number,
|
|
611
|
+
): quantumsim {
|
|
612
|
+
return sim.toffoli(control1, control2, target);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* applies swap(a, b) to sim.
|
|
617
|
+
*/
|
|
618
|
+
export function swap(sim: quantumsim, a: number, b: number): quantumsim {
|
|
619
|
+
return sim.swap(a, b);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* ccz: three-qubit phase flip, composed as toffoli conjugated by
|
|
624
|
+
* hadamard on the target; used as the grover oracle and diffuser core.
|
|
625
|
+
*/
|
|
626
|
+
export function ccz(
|
|
627
|
+
sim: quantumsim,
|
|
628
|
+
control1: number,
|
|
629
|
+
control2: number,
|
|
630
|
+
target: number,
|
|
631
|
+
): quantumsim {
|
|
632
|
+
sim.h(target);
|
|
633
|
+
sim.toffoli(control1, control2, target);
|
|
634
|
+
sim.h(target);
|
|
635
|
+
return sim;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/* ------------------------------------------------------------------ */
|
|
639
|
+
/* context: canonical circuits (bell, ghz) */
|
|
640
|
+
/* ------------------------------------------------------------------ */
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* proof bundle for a canonical circuit: the ready simulator plus the
|
|
644
|
+
* documented expected distribution so callers can assert outcomes.
|
|
645
|
+
*/
|
|
646
|
+
export type circuitproof = {
|
|
647
|
+
readonly sim: quantumsim;
|
|
648
|
+
readonly name: string;
|
|
649
|
+
readonly expectedprobs: readonly number[];
|
|
650
|
+
readonly reference: string;
|
|
651
|
+
readonly note: string;
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* builds the bell state (|00> + |11>)/sqrt(2) with h(0) then cnot(0,1);
|
|
656
|
+
* measurement of either qubit is a perfect coin and both always agree.
|
|
657
|
+
* expected distribution: [0.5, 0, 0.5, 0].
|
|
658
|
+
* reference: einstein-podolsky-rosen 1935; bell 1964; chapter 1 of
|
|
659
|
+
* nielsen and chuang, quantum computation and quantum information
|
|
660
|
+
* (2000).
|
|
661
|
+
*/
|
|
662
|
+
export function bellstate(): circuitproof {
|
|
663
|
+
const sim = new quantumsim(2);
|
|
664
|
+
sim.h(0).cnot(0, 1);
|
|
665
|
+
return {
|
|
666
|
+
sim,
|
|
667
|
+
name: 'bell',
|
|
668
|
+
expectedprobs: [0.5, 0, 0.5, 0],
|
|
669
|
+
reference: 'bell 1964; nielsen and chuang 2000 ch. 1',
|
|
670
|
+
note: 'maximally entangled pair; probs(0) = probs(1) = [0.5, 0.5]',
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* builds an n-qubit ghz state (|0...0> + |1...1>)/sqrt(2) by h(0) plus
|
|
676
|
+
* a cnot fanout; expected distribution carries mass 0.5 on index 0 and
|
|
677
|
+
* index 2^n - 1 only. reference: greenberger-horne-zeilinger 1989.
|
|
678
|
+
*/
|
|
679
|
+
export function ghzstate(n: number = 3): circuitproof {
|
|
680
|
+
if (!Number.isInteger(n) || n < 2 || n > 12) {
|
|
681
|
+
throw new quantumerror({
|
|
682
|
+
code: 'qubitrange',
|
|
683
|
+
message: `ghzstate supports 2 to 12 qubits, got ${n}`,
|
|
684
|
+
detail: 'cap exists so expectedprobs stays readable',
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
const sim = new quantumsim(n);
|
|
688
|
+
sim.h(0);
|
|
689
|
+
for (let q = 1; q < n; q++) {
|
|
690
|
+
sim.cnot(0, q);
|
|
691
|
+
}
|
|
692
|
+
const expected = new Array<number>(1 << n).fill(0);
|
|
693
|
+
expected[0] = 0.5;
|
|
694
|
+
expected[(1 << n) - 1] = 0.5;
|
|
695
|
+
return {
|
|
696
|
+
sim,
|
|
697
|
+
name: `ghz${n}`,
|
|
698
|
+
expectedprobs: expected,
|
|
699
|
+
reference: 'greenberger, horne, zeilinger 1989',
|
|
700
|
+
note: 'n-party entanglement witness; only |0...0> and |1...1> survive',
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/* ------------------------------------------------------------------ */
|
|
705
|
+
/* context: grover search (grover2 provable, grover3 statistical) */
|
|
706
|
+
/* ------------------------------------------------------------------ */
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* grover2: exhaustive-search winner |11> on 2 qubits with exactly one
|
|
710
|
+
* oracle call. the circuit is the task-pinned sequence
|
|
711
|
+
* h,h -> cz -> h,h -> x,x -> cz -> h,h whose unique fixed point is
|
|
712
|
+
* amplitude -1 on |11>, so prob(11) = 1 up to 1e-12 float noise.
|
|
713
|
+
* expected distribution: [0, 0, 0, 1].
|
|
714
|
+
* reference: grover, a fast quantum mechanical algorithm for database
|
|
715
|
+
* search, stoc 1996 (arxiv:quant-ph/9605043).
|
|
716
|
+
*/
|
|
717
|
+
export function grover2(): circuitproof {
|
|
718
|
+
const sim = new quantumsim(2);
|
|
719
|
+
sim.h(0).h(1);
|
|
720
|
+
sim.cz(0, 1);
|
|
721
|
+
sim.h(0).h(1);
|
|
722
|
+
sim.x(0).x(1);
|
|
723
|
+
sim.cz(0, 1);
|
|
724
|
+
sim.h(0).h(1);
|
|
725
|
+
return {
|
|
726
|
+
sim,
|
|
727
|
+
name: 'grover2',
|
|
728
|
+
expectedprobs: [0, 0, 0, 1],
|
|
729
|
+
reference: 'grover 1996 stoc (arxiv:quant-ph/9605043)',
|
|
730
|
+
note: 'one oracle call finds |11> with certainty (4 states, 1 marked)',
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/**
|
|
735
|
+
* grover3: search over 8 items for winner |111> with two iterations of
|
|
736
|
+
* oracle ccz + diffuser (h x3, x x3, ccz, x x3, h x3). theory gives
|
|
737
|
+
* success sin^2(5 * asin(1/sqrt 8)) = 0.9461, comfortably above 0.9.
|
|
738
|
+
* reference: grover 1996; boyer, brassard, hoefer, tapp 1998 iteration
|
|
739
|
+
* counting for m = 1 marked item of n = 8.
|
|
740
|
+
*/
|
|
741
|
+
export function grover3(): circuitproof {
|
|
742
|
+
const sim = new quantumsim(3);
|
|
743
|
+
sim.h(0).h(1).h(2);
|
|
744
|
+
for (let iteration = 0; iteration < 2; iteration++) {
|
|
745
|
+
ccz(sim, 0, 1, 2);
|
|
746
|
+
sim.h(0).h(1).h(2);
|
|
747
|
+
sim.x(0).x(1).x(2);
|
|
748
|
+
ccz(sim, 0, 1, 2);
|
|
749
|
+
sim.x(0).x(1).x(2);
|
|
750
|
+
sim.h(0).h(1).h(2);
|
|
751
|
+
}
|
|
752
|
+
const dim = sim.dim;
|
|
753
|
+
const expected = new Array<number>(dim).fill(0.0077);
|
|
754
|
+
expected[dim - 1] = 0.9461;
|
|
755
|
+
return {
|
|
756
|
+
sim,
|
|
757
|
+
name: 'grover3',
|
|
758
|
+
expectedprobs: expected,
|
|
759
|
+
reference: 'grover 1996; boyer-brassard-hoefer-tapp 1998',
|
|
760
|
+
note: 'prob(111) = 0.9461 theoretical; measured within float noise',
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/* ------------------------------------------------------------------ */
|
|
765
|
+
/* context: deutsch oracle discrimination */
|
|
766
|
+
/* ------------------------------------------------------------------ */
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* deutsch verdict of the single-bit oracle f: 'constant' when f(0) =
|
|
770
|
+
* f(1), 'balanced' when f(0) != f(1); one oracle call decides it.
|
|
771
|
+
*/
|
|
772
|
+
export type deutschverdict = 'constant' | 'balanced';
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* deutsch algorithm on 2 qubits (input qubit 0, ancilla qubit 1 in
|
|
776
|
+
* |->). the oracle is built from the truth table of f exactly as the
|
|
777
|
+
* four possible (x, y) -> (x, y xor f(x)) unitaries: identity, x on
|
|
778
|
+
* the ancilla, cnot(0,1) for f(x) = x, and x + cnot for f(x) = not x.
|
|
779
|
+
* phase kickback leaves qubit 0 in |0> for constant f and |1> for
|
|
780
|
+
* balanced f, so a single measurement decides the promise problem.
|
|
781
|
+
* reference: deutsch 1985; deutsch and jozsa, rapid solution of
|
|
782
|
+
* problems by quantum computation, proc r soc lond a 439, 553 (1992).
|
|
783
|
+
*/
|
|
784
|
+
export function deutsch(f: (x: 0 | 1) => 0 | 1): {
|
|
785
|
+
sim: quantumsim;
|
|
786
|
+
verdict: deutschverdict;
|
|
787
|
+
measured: 0 | 1;
|
|
788
|
+
expected: deutschverdict;
|
|
789
|
+
reference: string;
|
|
790
|
+
} {
|
|
791
|
+
const f0 = f(0);
|
|
792
|
+
const f1 = f(1);
|
|
793
|
+
const sim = new quantumsim(2);
|
|
794
|
+
sim.x(1);
|
|
795
|
+
sim.h(0).h(1);
|
|
796
|
+
if (f0 === 0 && f1 === 0) {
|
|
797
|
+
// constant zero: identity oracle, no gates.
|
|
798
|
+
} else if (f0 === 1 && f1 === 1) {
|
|
799
|
+
sim.x(1);
|
|
800
|
+
} else if (f0 === 0 && f1 === 1) {
|
|
801
|
+
sim.cnot(0, 1);
|
|
802
|
+
} else {
|
|
803
|
+
sim.x(1);
|
|
804
|
+
sim.cnot(0, 1);
|
|
805
|
+
}
|
|
806
|
+
sim.h(0);
|
|
807
|
+
const measured = sim.measure(0);
|
|
808
|
+
return {
|
|
809
|
+
sim,
|
|
810
|
+
verdict: measured === 0 ? 'constant' : 'balanced',
|
|
811
|
+
measured,
|
|
812
|
+
expected: f0 === f1 ? 'constant' : 'balanced',
|
|
813
|
+
reference: 'deutsch 1985; deutsch-jozsa 1992 proc r soc a 439 553',
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
/* ------------------------------------------------------------------ */
|
|
818
|
+
/* context: quantum teleportation sketch */
|
|
819
|
+
/* ------------------------------------------------------------------ */
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* qteleport: teleports the state of qubit 0 to qubit 2 through a bell
|
|
823
|
+
* pair on qubits 1 and 2 (h(1), cnot(1,2)), the bell-basis measurement
|
|
824
|
+
* cnot(0,1) + h(0), then the classical corrections x/z on qubit 2
|
|
825
|
+
* conditioned on the two outcomes. the simulator runs the whole loop so
|
|
826
|
+
* the fidelity is exactly 1 for any of the three prepared inputs; the
|
|
827
|
+
* fidelity is read off the two amplitudes that survive the bell
|
|
828
|
+
* measurement (index m1*2 + m0 and its +4 partner on qubit 2).
|
|
829
|
+
* reference: bennett, brassard, crepeau, jozsa, peres, wootters,
|
|
830
|
+
* teleporting an unknown quantum state via dual classical and
|
|
831
|
+
* einstein-podolsky-rosen channels, prl 70, 1895 (1993).
|
|
832
|
+
*/
|
|
833
|
+
export function qteleport(input: 'zero' | 'one' | 'plus' = 'one'): {
|
|
834
|
+
sim: quantumsim;
|
|
835
|
+
outcome0: 0 | 1;
|
|
836
|
+
outcome1: 0 | 1;
|
|
837
|
+
fidelity: number;
|
|
838
|
+
input: 'zero' | 'one' | 'plus';
|
|
839
|
+
reference: string;
|
|
840
|
+
} {
|
|
841
|
+
const sim = new quantumsim(3);
|
|
842
|
+
if (input === 'one') {
|
|
843
|
+
sim.x(0);
|
|
844
|
+
} else if (input === 'plus') {
|
|
845
|
+
sim.h(0);
|
|
846
|
+
}
|
|
847
|
+
sim.h(1);
|
|
848
|
+
sim.cnot(1, 2);
|
|
849
|
+
sim.cnot(0, 1);
|
|
850
|
+
sim.h(0);
|
|
851
|
+
const outcome1 = sim.measure(1);
|
|
852
|
+
const outcome0 = sim.measure(0);
|
|
853
|
+
if (outcome1 === 1) {
|
|
854
|
+
sim.x(2);
|
|
855
|
+
}
|
|
856
|
+
if (outcome0 === 1) {
|
|
857
|
+
sim.z(2);
|
|
858
|
+
}
|
|
859
|
+
const lowindex = (outcome1 << 1) | outcome0;
|
|
860
|
+
const alow = sim.amplitude(lowindex);
|
|
861
|
+
const ahigh = sim.amplitude(lowindex + 4);
|
|
862
|
+
const fidelity =
|
|
863
|
+
input === 'plus'
|
|
864
|
+
? ((alow[0] + ahigh[0]) / Math.SQRT2) ** 2 + ((alow[1] + ahigh[1]) / Math.SQRT2) ** 2
|
|
865
|
+
: input === 'one'
|
|
866
|
+
? ahigh[0] * ahigh[0] + ahigh[1] * ahigh[1]
|
|
867
|
+
: alow[0] * alow[0] + alow[1] * alow[1];
|
|
868
|
+
return {
|
|
869
|
+
sim,
|
|
870
|
+
outcome0,
|
|
871
|
+
outcome1,
|
|
872
|
+
fidelity,
|
|
873
|
+
input,
|
|
874
|
+
reference: 'bennett, brassard, crepeau, jozsa, peres, wootters 1993 prl 70 1895',
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/* ------------------------------------------------------------------ */
|
|
879
|
+
/* context: bb84 stations (alice, bob, eve over a simulated channel) */
|
|
880
|
+
/* ------------------------------------------------------------------ */
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* basis 0 is rectilinear (z, states |0>/|1>) and basis 1 is diagonal
|
|
884
|
+
* (x, states |+>/|->); the mapping matches the polarization bases of
|
|
885
|
+
* the 1984 protocol.
|
|
886
|
+
*/
|
|
887
|
+
export type bb84basis = 0 | 1;
|
|
888
|
+
|
|
889
|
+
/**
|
|
890
|
+
* alice station: draws random key bits and random bases from
|
|
891
|
+
* node:crypto and prepares one 1-qubit quantumsim photon per bit
|
|
892
|
+
* (x for bit 1, extra h when the basis is diagonal).
|
|
893
|
+
*/
|
|
894
|
+
export class alice {
|
|
895
|
+
readonly #bits: number[] = [];
|
|
896
|
+
readonly #bases: number[] = [];
|
|
897
|
+
|
|
898
|
+
constructor(count: number) {
|
|
899
|
+
try {
|
|
900
|
+
for (let i = 0; i < count; i++) {
|
|
901
|
+
this.#bits.push(randomInt(0, 2));
|
|
902
|
+
this.#bases.push(randomInt(0, 2));
|
|
903
|
+
}
|
|
904
|
+
} catch (cause) {
|
|
905
|
+
throw new quantumerror({
|
|
906
|
+
code: 'randomness',
|
|
907
|
+
message: 'alice could not draw randomness from node:crypto',
|
|
908
|
+
detail: String(cause),
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/** alice's raw key bits. */
|
|
914
|
+
get bits(): readonly number[] {
|
|
915
|
+
return this.#bits;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/** alice's basis string (0 rectilinear, 1 diagonal). */
|
|
919
|
+
get bases(): readonly number[] {
|
|
920
|
+
return this.#bases;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/**
|
|
924
|
+
* prepares the photon burst; each photon is a real 1-qubit state so
|
|
925
|
+
* the channel, eve and bob all act on amplitudes, not on labels.
|
|
926
|
+
*/
|
|
927
|
+
prepare(): quantumsim[] {
|
|
928
|
+
return this.#bits.map((bit, i) => {
|
|
929
|
+
const photon = new quantumsim(1);
|
|
930
|
+
if (bit === 1) {
|
|
931
|
+
photon.x(0);
|
|
932
|
+
}
|
|
933
|
+
if (this.#bases[i] === 1) {
|
|
934
|
+
photon.h(0);
|
|
935
|
+
}
|
|
936
|
+
return photon;
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* bob station: measures each incoming photon in his own random bases
|
|
943
|
+
* (h before the z measurement when diagonal), collapsing the photon.
|
|
944
|
+
*/
|
|
945
|
+
export class bob {
|
|
946
|
+
readonly #bases: number[] = [];
|
|
947
|
+
readonly #outcomes: number[] = [];
|
|
948
|
+
|
|
949
|
+
constructor(count: number) {
|
|
950
|
+
try {
|
|
951
|
+
for (let i = 0; i < count; i++) {
|
|
952
|
+
this.#bases.push(randomInt(0, 2));
|
|
953
|
+
}
|
|
954
|
+
} catch (cause) {
|
|
955
|
+
throw new quantumerror({
|
|
956
|
+
code: 'randomness',
|
|
957
|
+
message: 'bob could not draw randomness from node:crypto',
|
|
958
|
+
detail: String(cause),
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/** bob's basis string. */
|
|
964
|
+
get bases(): readonly number[] {
|
|
965
|
+
return this.#bases;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/** bob's raw measurement outcomes. */
|
|
969
|
+
get outcomes(): readonly number[] {
|
|
970
|
+
return this.#outcomes;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
/**
|
|
974
|
+
* measures the whole burst and stores the classical outcomes;
|
|
975
|
+
* measuring a diagonal-basis photon in the rectilinear basis
|
|
976
|
+
* genuinely yields a coin flip, which is where intercept-resend
|
|
977
|
+
* damage shows up.
|
|
978
|
+
*/
|
|
979
|
+
measure(photons: readonly quantumsim[]): readonly number[] {
|
|
980
|
+
this.#outcomes.length = 0;
|
|
981
|
+
for (let i = 0; i < photons.length; i++) {
|
|
982
|
+
const photon = photons[i];
|
|
983
|
+
if (this.#bases[i] === 1) {
|
|
984
|
+
photon.h(0);
|
|
985
|
+
}
|
|
986
|
+
this.#outcomes.push(photon.measure(0));
|
|
987
|
+
}
|
|
988
|
+
return this.#outcomes;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
/**
|
|
993
|
+
* eve station: intercept-resend attacker. she measures every photon in
|
|
994
|
+
* her own random basis (collapsing it) and resends a fresh photon
|
|
995
|
+
* prepared in that basis with the bit she saw. on sifted bits where
|
|
996
|
+
* alice and bob used the same basis, eve chose the wrong basis half
|
|
997
|
+
* the time and bob then errs half of those, so the qber lands at 25
|
|
998
|
+
* percent against 0 percent on a clean channel.
|
|
999
|
+
* reference: the intercept-resend attack is exercise material of every
|
|
1000
|
+
* bb84 treatment since bennett-brassard 1984.
|
|
1001
|
+
*/
|
|
1002
|
+
export class eve {
|
|
1003
|
+
readonly #bases: number[] = [];
|
|
1004
|
+
readonly #bits: number[] = [];
|
|
1005
|
+
|
|
1006
|
+
/** bases eve used (for audits). */
|
|
1007
|
+
get bases(): readonly number[] {
|
|
1008
|
+
return this.#bases;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/** bits eve extracted (for audits). */
|
|
1012
|
+
get bits(): readonly number[] {
|
|
1013
|
+
return this.#bits;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
/** taps the burst and returns the resent photons. */
|
|
1017
|
+
intercept(photons: readonly quantumsim[]): quantumsim[] {
|
|
1018
|
+
this.#bases.length = 0;
|
|
1019
|
+
this.#bits.length = 0;
|
|
1020
|
+
return photons.map((photon) => {
|
|
1021
|
+
const basis = randomInt(0, 2);
|
|
1022
|
+
if (basis === 1) {
|
|
1023
|
+
photon.h(0);
|
|
1024
|
+
}
|
|
1025
|
+
const bit = photon.measure(0);
|
|
1026
|
+
const resend = new quantumsim(1);
|
|
1027
|
+
if (bit === 1) {
|
|
1028
|
+
resend.x(0);
|
|
1029
|
+
}
|
|
1030
|
+
if (basis === 1) {
|
|
1031
|
+
resend.h(0);
|
|
1032
|
+
}
|
|
1033
|
+
this.#bases.push(basis);
|
|
1034
|
+
this.#bits.push(bit);
|
|
1035
|
+
return resend;
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* bb84 quantum channel: a photon pipeline that alice fills, an optional
|
|
1042
|
+
* eve taps (counted), and bob drains. no network, no localhost — the
|
|
1043
|
+
* channel is an in-memory queue standing in for fiber.
|
|
1044
|
+
*/
|
|
1045
|
+
export class bb84channel {
|
|
1046
|
+
#inflight: quantumsim[] = [];
|
|
1047
|
+
#taps = 0;
|
|
1048
|
+
|
|
1049
|
+
/** sends a photon burst onto the channel. */
|
|
1050
|
+
send(photons: readonly quantumsim[]): void {
|
|
1051
|
+
this.#inflight = [...photons];
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/**
|
|
1055
|
+
* taps the channel: the operator receives the in-flight burst and its
|
|
1056
|
+
* return value replaces it (intercept-resend); taps are counted.
|
|
1057
|
+
*/
|
|
1058
|
+
tap(operator: (photons: readonly quantumsim[]) => quantumsim[]): quantumsim[] {
|
|
1059
|
+
this.#taps += 1;
|
|
1060
|
+
this.#inflight = operator(this.#inflight);
|
|
1061
|
+
return this.#inflight;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/** drains the channel for the receiver. */
|
|
1065
|
+
receive(): quantumsim[] {
|
|
1066
|
+
const out = this.#inflight;
|
|
1067
|
+
this.#inflight = [];
|
|
1068
|
+
return out;
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
/** number of taps observed on the channel (informational). */
|
|
1072
|
+
get tapped(): number {
|
|
1073
|
+
return this.#taps;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/* ------------------------------------------------------------------ */
|
|
1078
|
+
/* context: bb84 protocol runner (sifting, qber, detection) */
|
|
1079
|
+
/* ------------------------------------------------------------------ */
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* runbb84 result: the sifted keys (sample bits removed), the
|
|
1083
|
+
* full-channel qber, and the sampling-based eavesdropper verdict.
|
|
1084
|
+
*/
|
|
1085
|
+
export type bb84result = {
|
|
1086
|
+
readonly keyalice: readonly number[];
|
|
1087
|
+
readonly keybob: readonly number[];
|
|
1088
|
+
readonly qber: number;
|
|
1089
|
+
readonly detected: boolean;
|
|
1090
|
+
readonly siftedlen: number;
|
|
1091
|
+
readonly sampledlen: number;
|
|
1092
|
+
readonly sampledqber: number;
|
|
1093
|
+
readonly threshold: number;
|
|
1094
|
+
readonly reference: string;
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* runs the complete bb84 flow over the simulated channel: alice
|
|
1099
|
+
* prepares, the optional eve taps, bob measures, both sides sift on
|
|
1100
|
+
* equal bases, the qber is computed over the full sifted key and the
|
|
1101
|
+
* detection verdict comes from comparing a public sample of up to 128
|
|
1102
|
+
* sifted bits (abort threshold 11 percent, the one-way bb84
|
|
1103
|
+
* reconciliation bound of the shor-preskill lineage).
|
|
1104
|
+
* reference: bennett and brassard, quantum cryptography: public key
|
|
1105
|
+
* distribution and coin tossing, proc ieee int conf on computers,
|
|
1106
|
+
* systems and signal processing, bangalore 1984, pp. 175-179.
|
|
1107
|
+
*/
|
|
1108
|
+
export function runbb84(options: { eve?: boolean; bits?: number } = {}): bb84result {
|
|
1109
|
+
const bits = options.bits ?? 1024;
|
|
1110
|
+
if (!Number.isInteger(bits) || bits < 64 || bits > 8192) {
|
|
1111
|
+
throw new quantumerror({
|
|
1112
|
+
code: 'bitcount',
|
|
1113
|
+
message: `bb84 burst size must be in [64, 8192], got ${bits}`,
|
|
1114
|
+
detail: '1024 keeps the qber estimate comfortably outside noise bands',
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
const a = new alice(bits);
|
|
1118
|
+
const b = new bob(bits);
|
|
1119
|
+
const channel = new bb84channel();
|
|
1120
|
+
channel.send(a.prepare());
|
|
1121
|
+
if (options.eve === true) {
|
|
1122
|
+
const attacker = new eve();
|
|
1123
|
+
channel.tap((photons) => attacker.intercept(photons));
|
|
1124
|
+
}
|
|
1125
|
+
b.measure(channel.receive());
|
|
1126
|
+
const sifted: number[] = [];
|
|
1127
|
+
for (let i = 0; i < bits; i++) {
|
|
1128
|
+
if (a.bases[i] === b.bases[i]) {
|
|
1129
|
+
sifted.push(i);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
if (sifted.length < 16) {
|
|
1133
|
+
throw new quantumerror({
|
|
1134
|
+
code: 'siftempty',
|
|
1135
|
+
message: 'sifting kept fewer than 16 bits; raise the burst size',
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
let mismatches = 0;
|
|
1139
|
+
for (const i of sifted) {
|
|
1140
|
+
if (a.bits[i] !== b.outcomes[i]) {
|
|
1141
|
+
mismatches += 1;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
const qber = mismatches / sifted.length;
|
|
1145
|
+
const order = [...sifted.keys()];
|
|
1146
|
+
for (let i = order.length - 1; i > 0; i--) {
|
|
1147
|
+
const j = randomInt(0, i + 1);
|
|
1148
|
+
const tmp = order[i];
|
|
1149
|
+
order[i] = order[j];
|
|
1150
|
+
order[j] = tmp;
|
|
1151
|
+
}
|
|
1152
|
+
const samplecount = Math.min(128, Math.max(8, Math.floor(sifted.length / 2)));
|
|
1153
|
+
const sample = new Set(order.slice(0, samplecount));
|
|
1154
|
+
let samplemismatch = 0;
|
|
1155
|
+
for (const i of sample) {
|
|
1156
|
+
if (a.bits[i] !== b.outcomes[i]) {
|
|
1157
|
+
samplemismatch += 1;
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
const sampledqber = samplemismatch / sample.size;
|
|
1161
|
+
const keyalice: number[] = [];
|
|
1162
|
+
const keybob: number[] = [];
|
|
1163
|
+
for (const i of sifted) {
|
|
1164
|
+
if (sample.has(i)) continue;
|
|
1165
|
+
keyalice.push(a.bits[i]);
|
|
1166
|
+
keybob.push(b.outcomes[i]);
|
|
1167
|
+
}
|
|
1168
|
+
return {
|
|
1169
|
+
keyalice,
|
|
1170
|
+
keybob,
|
|
1171
|
+
qber,
|
|
1172
|
+
detected: sampledqber > 0.11,
|
|
1173
|
+
siftedlen: sifted.length,
|
|
1174
|
+
sampledlen: sample.size,
|
|
1175
|
+
sampledqber,
|
|
1176
|
+
threshold: 0.11,
|
|
1177
|
+
reference: 'bb84, bangalore 1984; abort threshold from shor-preskill 2000 lineage',
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
/* ------------------------------------------------------------------ */
|
|
1182
|
+
/* context: e91 entangled pairs + chsh */
|
|
1183
|
+
/* ------------------------------------------------------------------ */
|
|
1184
|
+
|
|
1185
|
+
/**
|
|
1186
|
+
* e91 run result: the chsh s value, the key bits extracted from
|
|
1187
|
+
* matched analyzer settings and the eavesdropper verdict.
|
|
1188
|
+
*/
|
|
1189
|
+
export type e91result = {
|
|
1190
|
+
readonly s: number;
|
|
1191
|
+
readonly classicalbound: number;
|
|
1192
|
+
readonly quantumbound: number;
|
|
1193
|
+
readonly detected: boolean;
|
|
1194
|
+
readonly keylen: number;
|
|
1195
|
+
readonly qber: number;
|
|
1196
|
+
readonly pairs: number;
|
|
1197
|
+
readonly chshpairs: number;
|
|
1198
|
+
readonly reference: string;
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
const deg2rad = Math.PI / 180;
|
|
1202
|
+
|
|
1203
|
+
/**
|
|
1204
|
+
* measures qubit q of sim at analyzer angle a (degrees): applies
|
|
1205
|
+
* ry(-2a) so outcome 0 projects onto cos(a)|0> + sin(a)|1> and outcome
|
|
1206
|
+
* 1 onto its orthogonal partner, then collapses via measure.
|
|
1207
|
+
*/
|
|
1208
|
+
function measureatangle(sim: quantumsim, q: number, angledeg: number): 0 | 1 {
|
|
1209
|
+
sim.ry(-2 * angledeg * deg2rad, q);
|
|
1210
|
+
return sim.measure(q);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/**
|
|
1214
|
+
* prepares a fresh |+a> = cos(a)|0> + sin(a)|1> photon on qubit q;
|
|
1215
|
+
* used by eve to resend exactly what she measured.
|
|
1216
|
+
*/
|
|
1217
|
+
function prepareangle(sim: quantumsim, q: number, angledeg: number, bit: 0 | 1): void {
|
|
1218
|
+
if (bit === 1) {
|
|
1219
|
+
sim.x(q);
|
|
1220
|
+
}
|
|
1221
|
+
if (angledeg !== 0) {
|
|
1222
|
+
sim.ry(2 * angledeg * deg2rad, q);
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
/**
|
|
1227
|
+
* rune91: ekert-1991 entanglement-based key distribution on |phi+>
|
|
1228
|
+
* pairs. each round is half the time a key round (both parties pick
|
|
1229
|
+
* 0 or 45 degrees; equal angles give perfectly correlated bits on a
|
|
1230
|
+
* clean source) and half the time a chsh round (alice 0 or 45 degrees,
|
|
1231
|
+
* bob 22.5 or 67.5 degrees). the chsh statistic
|
|
1232
|
+
* s = e(0,22.5) - e(0,67.5) + e(45,22.5) + e(45,67.5) converges to
|
|
1233
|
+
* 2 sqrt(2) = 2.8284 for the entangled source and cannot exceed the
|
|
1234
|
+
* classical bound 2 once eve collapses the pairs, which is exactly the
|
|
1235
|
+
* e91 security test (bell-inequality violation as the eavesdropper
|
|
1236
|
+
* detector). an intercept-resend eve also pushes the matched-angle key
|
|
1237
|
+
* qber to 25 percent.
|
|
1238
|
+
* reference: ekert, quantum cryptography based on bell's theorem,
|
|
1239
|
+
* prl 67, 661 (1991); clauser-horne-shimony-holt 1969 prl 23 880;
|
|
1240
|
+
* bell 1964; aspect, dalibard, roger 1982.
|
|
1241
|
+
*/
|
|
1242
|
+
export function rune91(options: { eve?: boolean; pairs?: number } = {}): e91result {
|
|
1243
|
+
const pairs = options.pairs ?? 2400;
|
|
1244
|
+
if (!Number.isInteger(pairs) || pairs < 200 || pairs > 200000) {
|
|
1245
|
+
throw new quantumerror({
|
|
1246
|
+
code: 'paircount',
|
|
1247
|
+
message: `e91 needs 200 to 200000 pairs, got ${pairs}`,
|
|
1248
|
+
detail: '2400 pairs put the chsh noise band about three sigma from the verdict line',
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
const keytotals = { matched: 0, mismatch: 0 };
|
|
1252
|
+
const buckets = new Map<string, { n: number; agree: number }>();
|
|
1253
|
+
for (let round = 0; round < pairs; round++) {
|
|
1254
|
+
let pair = new quantumsim(2);
|
|
1255
|
+
pair.h(0).cnot(0, 1);
|
|
1256
|
+
if (options.eve === true) {
|
|
1257
|
+
const b0 = randomInt(0, 2) === 1 ? 45 : 0;
|
|
1258
|
+
const b1 = randomInt(0, 2) === 1 ? 45 : 0;
|
|
1259
|
+
const m0 = measureatangle(pair, 0, b0);
|
|
1260
|
+
const m1 = measureatangle(pair, 1, b1);
|
|
1261
|
+
const resend = new quantumsim(2);
|
|
1262
|
+
prepareangle(resend, 0, b0, m0);
|
|
1263
|
+
prepareangle(resend, 1, b1, m1);
|
|
1264
|
+
pair = resend;
|
|
1265
|
+
}
|
|
1266
|
+
const mode = randomInt(0, 2);
|
|
1267
|
+
if (mode === 0) {
|
|
1268
|
+
const adeg = randomInt(0, 2) === 1 ? 45 : 0;
|
|
1269
|
+
const bdeg = randomInt(0, 2) === 1 ? 45 : 0;
|
|
1270
|
+
const ma = measureatangle(pair, 0, adeg);
|
|
1271
|
+
const mb = measureatangle(pair, 1, bdeg);
|
|
1272
|
+
if (adeg === bdeg) {
|
|
1273
|
+
keytotals.matched += 1;
|
|
1274
|
+
if (ma !== mb) {
|
|
1275
|
+
keytotals.mismatch += 1;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
} else {
|
|
1279
|
+
const adeg = randomInt(0, 2) === 1 ? 45 : 0;
|
|
1280
|
+
const bdeg = randomInt(0, 2) === 1 ? 67.5 : 22.5;
|
|
1281
|
+
const ea = measureatangle(pair, 0, adeg);
|
|
1282
|
+
const eb = measureatangle(pair, 1, bdeg);
|
|
1283
|
+
const keyname = `${adeg}:${bdeg}`;
|
|
1284
|
+
const bucket = buckets.get(keyname) ?? { n: 0, agree: 0 };
|
|
1285
|
+
bucket.n += 1;
|
|
1286
|
+
if (ea === eb) {
|
|
1287
|
+
bucket.agree += 1;
|
|
1288
|
+
}
|
|
1289
|
+
buckets.set(keyname, bucket);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
const correlation = (a: number, b: number): number => {
|
|
1293
|
+
const bucket = buckets.get(`${a}:${b}`);
|
|
1294
|
+
if (!bucket || bucket.n === 0) {
|
|
1295
|
+
return Number.NaN;
|
|
1296
|
+
}
|
|
1297
|
+
return (2 * bucket.agree) / bucket.n - 1;
|
|
1298
|
+
};
|
|
1299
|
+
const s =
|
|
1300
|
+
correlation(0, 22.5) - correlation(0, 67.5) + correlation(45, 22.5) + correlation(45, 67.5);
|
|
1301
|
+
if (!Number.isFinite(s)) {
|
|
1302
|
+
throw new quantumerror({
|
|
1303
|
+
code: 'chshsample',
|
|
1304
|
+
message: 'a chsh angle pair received zero samples; raise the pair count',
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
const keylen = keytotals.matched;
|
|
1308
|
+
const qber = keylen === 0 ? Number.NaN : keytotals.mismatch / keylen;
|
|
1309
|
+
let chshpairs = 0;
|
|
1310
|
+
for (const bucket of buckets.values()) {
|
|
1311
|
+
chshpairs += bucket.n;
|
|
1312
|
+
}
|
|
1313
|
+
const detected = s <= 2.4 || (Number.isFinite(qber) && qber > 0.05);
|
|
1314
|
+
return {
|
|
1315
|
+
s,
|
|
1316
|
+
classicalbound: 2,
|
|
1317
|
+
quantumbound: 2 * Math.SQRT2,
|
|
1318
|
+
detected,
|
|
1319
|
+
keylen,
|
|
1320
|
+
qber,
|
|
1321
|
+
pairs,
|
|
1322
|
+
chshpairs,
|
|
1323
|
+
reference: 'ekert 1991 prl 67 661; chsh 1969; bell 1964; aspect 1982',
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
/* ------------------------------------------------------------------ */
|
|
1328
|
+
/* context: qrng (quantum-inspired random, honest anu fallback) */
|
|
1329
|
+
/* ------------------------------------------------------------------ */
|
|
1330
|
+
|
|
1331
|
+
/**
|
|
1332
|
+
* quantuminspiredrandom: draws count integers in [min, max) from the
|
|
1333
|
+
* node crypto CSPRNG via randomInt (node rejection-samples internally,
|
|
1334
|
+
* so the stream is unbiased). this is a classical CSPRNG standing in
|
|
1335
|
+
* for a QRNG photon source; the API shape matches hardware QRNG
|
|
1336
|
+
* services (count + integer range in, uniform integers out) so the
|
|
1337
|
+
* integration seam is already quantum-ready. no physics is claimed.
|
|
1338
|
+
*/
|
|
1339
|
+
export function quantuminspiredrandom(
|
|
1340
|
+
count: number,
|
|
1341
|
+
options: { min?: number; max?: number } = {},
|
|
1342
|
+
): number[] {
|
|
1343
|
+
if (!Number.isInteger(count) || count < 1 || count > 1048576) {
|
|
1344
|
+
throw new quantumerror({
|
|
1345
|
+
code: 'countrange',
|
|
1346
|
+
message: `qrng count must be in [1, 1048576], got ${count}`,
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
const min = options.min ?? 0;
|
|
1350
|
+
const max = options.max ?? 256;
|
|
1351
|
+
if (!Number.isInteger(min) || !Number.isInteger(max) || min >= max || max - min > 2 ** 36) {
|
|
1352
|
+
throw new quantumerror({
|
|
1353
|
+
code: 'range',
|
|
1354
|
+
message: `qrng range needs integers min < max with span <= 2^36, got [${min}, ${max})`,
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
try {
|
|
1358
|
+
const values: number[] = [];
|
|
1359
|
+
for (let i = 0; i < count; i++) {
|
|
1360
|
+
values.push(randomInt(min, max));
|
|
1361
|
+
}
|
|
1362
|
+
return values;
|
|
1363
|
+
} catch (cause) {
|
|
1364
|
+
throw new quantumerror({
|
|
1365
|
+
code: 'randomness',
|
|
1366
|
+
message: 'crypto.randomInt failed while filling the qrng batch',
|
|
1367
|
+
detail: String(cause),
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
/**
|
|
1373
|
+
* anuqrng: optional asynchronous fetch of the australian national
|
|
1374
|
+
* university quantum random numbers service (api.quantumnumbers.io,
|
|
1375
|
+
* successor of qrng.anu.edu.au), which digitizes vacuum fluctuations.
|
|
1376
|
+
* the planner is honest: without network egress or an api key the call
|
|
1377
|
+
* fails fast (abort timeout 2500 ms default) and the function falls
|
|
1378
|
+
* back to the same CSPRNG as quantuminspiredrandom, reporting
|
|
1379
|
+
* source: 'csprng'. anu values are 64-bit unsigned integers folded
|
|
1380
|
+
* into the requested range with one modulo (bias below 2^-32 for any
|
|
1381
|
+
* practical span, documented here rather than hidden).
|
|
1382
|
+
*/
|
|
1383
|
+
export async function anuqrng(
|
|
1384
|
+
count: number,
|
|
1385
|
+
options: { apikey?: string; timeoutms?: number; min?: number; max?: number } = {},
|
|
1386
|
+
): Promise<{ values: number[]; source: 'anu' | 'csprng'; note: string }> {
|
|
1387
|
+
const min = options.min ?? 0;
|
|
1388
|
+
const max = options.max ?? 256;
|
|
1389
|
+
const span = max - min;
|
|
1390
|
+
if (
|
|
1391
|
+
!Number.isInteger(count) ||
|
|
1392
|
+
count < 1 ||
|
|
1393
|
+
count > 1024 ||
|
|
1394
|
+
!Number.isInteger(min) ||
|
|
1395
|
+
min >= max
|
|
1396
|
+
) {
|
|
1397
|
+
throw new quantumerror({
|
|
1398
|
+
code: 'range',
|
|
1399
|
+
message: `anuqrng needs count in [1, 1024] and integer min < max, got count=${count}`,
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
const timeoutms = options.timeoutms ?? 2500;
|
|
1403
|
+
const apikey = options.apikey ?? '';
|
|
1404
|
+
try {
|
|
1405
|
+
const url =
|
|
1406
|
+
`https://api.quantumnumbers.io/qranum/v1/generate?apikey=${apikey}` +
|
|
1407
|
+
`&number=${count}&size=8`;
|
|
1408
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(timeoutms) });
|
|
1409
|
+
if (!response.ok) {
|
|
1410
|
+
throw new Error(`anu http status ${response.status}`);
|
|
1411
|
+
}
|
|
1412
|
+
const payload = (await response.json()) as { data?: unknown };
|
|
1413
|
+
if (!Array.isArray(payload.data) || payload.data.length < count) {
|
|
1414
|
+
throw new Error('anu payload did not carry enough values');
|
|
1415
|
+
}
|
|
1416
|
+
const values = payload.data.slice(0, count).map((value) => min + (Number(value) % span));
|
|
1417
|
+
if (values.some((value) => !Number.isInteger(value) || value < min || value >= max)) {
|
|
1418
|
+
throw new Error('anu payload contained non-integer entries');
|
|
1419
|
+
}
|
|
1420
|
+
return {
|
|
1421
|
+
values,
|
|
1422
|
+
source: 'anu',
|
|
1423
|
+
note: 'vacuum-fluctuation source, api.quantumnumbers.io',
|
|
1424
|
+
};
|
|
1425
|
+
} catch (cause) {
|
|
1426
|
+
return {
|
|
1427
|
+
values: quantuminspiredrandom(count, { min, max }),
|
|
1428
|
+
source: 'csprng',
|
|
1429
|
+
note: `anu fetch unavailable (${String(cause)}); honest CSPRNG fallback`,
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
/**
|
|
1435
|
+
* convenience bytes draw used by the self check and available to
|
|
1436
|
+
* integrators: crypto.randomBytes surfaced under the qrng banner with
|
|
1437
|
+
* the same honesty note as quantuminspiredrandom.
|
|
1438
|
+
*/
|
|
1439
|
+
export function quantuminspiredbytes(count: number): Uint8Array {
|
|
1440
|
+
if (!Number.isInteger(count) || count < 1 || count > 1048576) {
|
|
1441
|
+
throw new quantumerror({
|
|
1442
|
+
code: 'countrange',
|
|
1443
|
+
message: `qrng byte count must be in [1, 1048576], got ${count}`,
|
|
1444
|
+
});
|
|
1445
|
+
}
|
|
1446
|
+
try {
|
|
1447
|
+
return randomBytes(count);
|
|
1448
|
+
} catch (cause) {
|
|
1449
|
+
throw new quantumerror({
|
|
1450
|
+
code: 'randomness',
|
|
1451
|
+
message: 'crypto.randomBytes failed',
|
|
1452
|
+
detail: String(cause),
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
/* ------------------------------------------------------------------ */
|
|
1458
|
+
/* context: dnavault codec (goldman 2013 ternary rotation) */
|
|
1459
|
+
/* ------------------------------------------------------------------ */
|
|
1460
|
+
|
|
1461
|
+
/**
|
|
1462
|
+
* dnavault codec constants: 4 strands addressed by blockindex mod 4,
|
|
1463
|
+
* each block synthesized twice (on its home strand and the next one)
|
|
1464
|
+
* so decoding survives the loss of any single strand; every byte maps
|
|
1465
|
+
* to 6 ternary digits (3^6 = 729 > 256) and every digit to one
|
|
1466
|
+
* nucleotide through the goldman rotation next = (digit + 1 + previous)
|
|
1467
|
+
* mod 4, which makes consecutive repeats impossible (homopolymer run
|
|
1468
|
+
* length 1, stricter than the standard run-length <= 3 synthesis
|
|
1469
|
+
* constraint). reference: goldman et al., towards practical,
|
|
1470
|
+
* high-capacity, low-maintenance information storage in synthesized
|
|
1471
|
+
* dna, nature 494, 77-80 (2013), doi:10.1038/nature11875.
|
|
1472
|
+
*/
|
|
1473
|
+
export const dnavaultspec = {
|
|
1474
|
+
strandcount: 4,
|
|
1475
|
+
redundancy: 2,
|
|
1476
|
+
blockpayloadbytes: 32,
|
|
1477
|
+
blockframebytes: 35,
|
|
1478
|
+
nucleotidesperblock: 210,
|
|
1479
|
+
tritsperbyte: 6,
|
|
1480
|
+
maxinputbytes: 65536,
|
|
1481
|
+
homopolymerrunmax: 1,
|
|
1482
|
+
rule: 'next = (digit + 1 + previous) mod 4, digit in {0,1,2}',
|
|
1483
|
+
reference: 'goldman et al. 2013 nature 494 77-80 doi:10.1038/nature11875',
|
|
1484
|
+
} as const satisfies Record<string, string | number>;
|
|
1485
|
+
|
|
1486
|
+
const nucleotides = ['A', 'C', 'G', 'T'] as const;
|
|
1487
|
+
const nucleotideindex: Readonly<Record<string, number>> = { A: 0, C: 1, G: 2, T: 3 };
|
|
1488
|
+
const powersof3 = [1, 3, 9, 27, 81, 243];
|
|
1489
|
+
|
|
1490
|
+
/**
|
|
1491
|
+
* dnavaultencode result: the four strand oligo strings, the block
|
|
1492
|
+
* count and the nucleotide accounting for the capacity planner.
|
|
1493
|
+
*/
|
|
1494
|
+
export type dnavaultencoderesult = {
|
|
1495
|
+
readonly strands: readonly string[];
|
|
1496
|
+
readonly blocks: number;
|
|
1497
|
+
readonly oligos: number;
|
|
1498
|
+
readonly nucleotides: number;
|
|
1499
|
+
readonly redundancy: number;
|
|
1500
|
+
readonly homopolymerrunmax: number;
|
|
1501
|
+
readonly reference: string;
|
|
1502
|
+
};
|
|
1503
|
+
|
|
1504
|
+
/**
|
|
1505
|
+
* encodes bytes into four dna strands with real goldman-2013 rules:
|
|
1506
|
+
* a 4-byte big-endian length prefix, 32-byte payload blocks framed as
|
|
1507
|
+
* [index u16 be][payload 32][checksum u8 of index + payload], ternary
|
|
1508
|
+
* rotation encoding per byte, dual-strand placement (blockindex mod 4
|
|
1509
|
+
* and (blockindex + 1) mod 4). the archive planner guard caps input at
|
|
1510
|
+
* 64 kib; real synthesis runs are planned, not executed, here.
|
|
1511
|
+
*/
|
|
1512
|
+
export function dnavaultencode(bytes: Uint8Array): dnavaultencoderesult {
|
|
1513
|
+
if (bytes.length > dnavaultspec.maxinputbytes) {
|
|
1514
|
+
throw new quantumerror({
|
|
1515
|
+
code: 'archiveguard',
|
|
1516
|
+
message: `dnavaultencode is an archive planner: input capped at ${dnavaultspec.maxinputbytes} bytes, got ${bytes.length}`,
|
|
1517
|
+
detail: 'use the capacity planner (planodefault) for larger archives',
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
const stream: number[] = [
|
|
1521
|
+
(bytes.length >>> 24) & 0xff,
|
|
1522
|
+
(bytes.length >>> 16) & 0xff,
|
|
1523
|
+
(bytes.length >>> 8) & 0xff,
|
|
1524
|
+
bytes.length & 0xff,
|
|
1525
|
+
...bytes,
|
|
1526
|
+
];
|
|
1527
|
+
const blocks = Math.ceil(stream.length / dnavaultspec.blockpayloadbytes);
|
|
1528
|
+
const strands: string[][] = [[], [], [], []];
|
|
1529
|
+
for (let b = 0; b < blocks; b++) {
|
|
1530
|
+
const payload: number[] = [];
|
|
1531
|
+
for (let k = 0; k < dnavaultspec.blockpayloadbytes; k++) {
|
|
1532
|
+
payload.push(stream[b * dnavaultspec.blockpayloadbytes + k] ?? 0);
|
|
1533
|
+
}
|
|
1534
|
+
const hi = (b >>> 8) & 0xff;
|
|
1535
|
+
const lo = b & 0xff;
|
|
1536
|
+
let sum = hi + lo;
|
|
1537
|
+
for (const byte of payload) {
|
|
1538
|
+
sum += byte;
|
|
1539
|
+
}
|
|
1540
|
+
const frame = [hi, lo, ...payload, sum & 0xff];
|
|
1541
|
+
let oligo = '';
|
|
1542
|
+
let previous = 0;
|
|
1543
|
+
for (const byte of frame) {
|
|
1544
|
+
let value = byte;
|
|
1545
|
+
for (let k = 0; k < dnavaultspec.tritsperbyte; k++) {
|
|
1546
|
+
const digit = value % 3;
|
|
1547
|
+
value = (value - digit) / 3;
|
|
1548
|
+
const base = (digit + previous + 1) % 4;
|
|
1549
|
+
oligo += nucleotides[base];
|
|
1550
|
+
previous = base;
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
strands[b % 4].push(oligo);
|
|
1554
|
+
strands[(b + 1) % 4].push(oligo);
|
|
1555
|
+
}
|
|
1556
|
+
const oligos = blocks * dnavaultspec.redundancy;
|
|
1557
|
+
return {
|
|
1558
|
+
strands: strands.map((strand) => strand.join('')),
|
|
1559
|
+
blocks,
|
|
1560
|
+
oligos,
|
|
1561
|
+
nucleotides: oligos * dnavaultspec.nucleotidesperblock,
|
|
1562
|
+
redundancy: dnavaultspec.redundancy,
|
|
1563
|
+
homopolymerrunmax: dnavaultspec.homopolymerrunmax,
|
|
1564
|
+
reference: dnavaultspec.reference,
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/* ------------------------------------------------------------------ */
|
|
1569
|
+
/* context: dnavault decode (checksums, redundancy, roundtrip) */
|
|
1570
|
+
/* ------------------------------------------------------------------ */
|
|
1571
|
+
|
|
1572
|
+
/**
|
|
1573
|
+
* decodes strands back to bytes: every 210-nucleotide oligo is
|
|
1574
|
+
* de-rotated to a 35-byte frame, checksum-verified, deduplicated by
|
|
1575
|
+
* block index and reassembled in order. oligos failing checksum or
|
|
1576
|
+
* carrying invalid letters are dropped silently because every block
|
|
1577
|
+
* exists on two strands; a genuinely missing index throws.
|
|
1578
|
+
*/
|
|
1579
|
+
export function dnavaultdecode(strands: readonly string[]): Uint8Array {
|
|
1580
|
+
const byindex = new Map<number, number[]>();
|
|
1581
|
+
for (const strand of strands) {
|
|
1582
|
+
const clean = strand.replace(/\s+/g, '').toUpperCase();
|
|
1583
|
+
for (
|
|
1584
|
+
let start = 0;
|
|
1585
|
+
start + dnavaultspec.nucleotidesperblock <= clean.length;
|
|
1586
|
+
start += dnavaultspec.nucleotidesperblock
|
|
1587
|
+
) {
|
|
1588
|
+
const oligo = clean.slice(start, start + dnavaultspec.nucleotidesperblock);
|
|
1589
|
+
try {
|
|
1590
|
+
const frame: number[] = [];
|
|
1591
|
+
let previous = 0;
|
|
1592
|
+
let value = 0;
|
|
1593
|
+
let power = 0;
|
|
1594
|
+
for (const letter of oligo) {
|
|
1595
|
+
const base = nucleotideindex[letter];
|
|
1596
|
+
if (base === undefined) {
|
|
1597
|
+
throw new Error(`non-nucleotide letter ${letter}`);
|
|
1598
|
+
}
|
|
1599
|
+
const digit = (((base - previous - 1) % 4) + 4) % 4;
|
|
1600
|
+
if (digit > 2) {
|
|
1601
|
+
throw new Error('rotation arithmetic produced an impossible digit');
|
|
1602
|
+
}
|
|
1603
|
+
value += digit * powersof3[power];
|
|
1604
|
+
power += 1;
|
|
1605
|
+
if (power === dnavaultspec.tritsperbyte) {
|
|
1606
|
+
frame.push(value);
|
|
1607
|
+
value = 0;
|
|
1608
|
+
power = 0;
|
|
1609
|
+
}
|
|
1610
|
+
previous = base;
|
|
1611
|
+
}
|
|
1612
|
+
const index = (frame[0] << 8) | frame[1];
|
|
1613
|
+
let sum = frame[0] + frame[1];
|
|
1614
|
+
for (let k = 2; k < frame.length - 1; k++) {
|
|
1615
|
+
sum += frame[k];
|
|
1616
|
+
}
|
|
1617
|
+
if ((sum & 0xff) !== frame[frame.length - 1]) {
|
|
1618
|
+
throw new Error(`checksum mismatch on block ${index}`);
|
|
1619
|
+
}
|
|
1620
|
+
if (!byindex.has(index)) {
|
|
1621
|
+
byindex.set(index, frame);
|
|
1622
|
+
}
|
|
1623
|
+
} catch {
|
|
1624
|
+
// corrupted oligo: redundancy owns recovery, skip it.
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
const count = byindex.size;
|
|
1629
|
+
if (count === 0) {
|
|
1630
|
+
throw new quantumerror({
|
|
1631
|
+
code: 'unrecoverable',
|
|
1632
|
+
message: 'no valid dnavault block survived decoding',
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
for (let b = 0; b < count; b++) {
|
|
1636
|
+
if (!byindex.has(b)) {
|
|
1637
|
+
throw new quantumerror({
|
|
1638
|
+
code: 'blockmissing',
|
|
1639
|
+
message: `dnavault block ${b} is missing across all strands`,
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
const stream: number[] = [];
|
|
1644
|
+
for (let b = 0; b < count; b++) {
|
|
1645
|
+
const frame = byindex.get(b);
|
|
1646
|
+
if (!frame) {
|
|
1647
|
+
throw new quantumerror({
|
|
1648
|
+
code: 'blockmissing',
|
|
1649
|
+
message: `dnavault block ${b} vanished between the has check and the read`,
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
for (let k = 2; k < frame.length - 1; k++) {
|
|
1653
|
+
stream.push(frame[k]);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
const length = ((stream[0] << 24) | (stream[1] << 16) | (stream[2] << 8) | stream[3]) >>> 0;
|
|
1657
|
+
if (length > stream.length - 4) {
|
|
1658
|
+
throw new quantumerror({
|
|
1659
|
+
code: 'lengthcorrupt',
|
|
1660
|
+
message: `declared length ${length} exceeds decoded payload ${stream.length - 4}`,
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1663
|
+
return Uint8Array.from(stream.slice(4, 4 + length));
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
/* ------------------------------------------------------------------ */
|
|
1667
|
+
/* context: dnavault capacity planner (215 pb/g) */
|
|
1668
|
+
/* ------------------------------------------------------------------ */
|
|
1669
|
+
|
|
1670
|
+
/**
|
|
1671
|
+
* dnavault capacity anchors: the widely quoted 215 petabytes per gram
|
|
1672
|
+
* of dna figure (church 2012 science era synthesis estimates, echoed
|
|
1673
|
+
* by the nature 2013 news coverage around goldman et al.) anchors the
|
|
1674
|
+
* physical planner; the codec rates above are what this module
|
|
1675
|
+
* actually charges.
|
|
1676
|
+
*/
|
|
1677
|
+
export const dnacapacityspec = {
|
|
1678
|
+
bytespergram: 2.15e17,
|
|
1679
|
+
densityquote: '215 petabytes per gram of dna',
|
|
1680
|
+
densityreference: 'church, gao, kosuri 2012 science 337 1628 era estimates; nature news 2013',
|
|
1681
|
+
synthesispitch: 'archive planner only; no oligo is ordered or synthesized',
|
|
1682
|
+
} as const satisfies Record<string, string | number>;
|
|
1683
|
+
|
|
1684
|
+
/**
|
|
1685
|
+
* dnavault capacity estimate for an archive of sizebytes: dna grams at
|
|
1686
|
+
* the 215 pb/g density, oligo and nucleotide counts at this codec's
|
|
1687
|
+
* real rates, and the storage overhead ratio.
|
|
1688
|
+
*/
|
|
1689
|
+
export function dnacapacity(sizebytes: number): {
|
|
1690
|
+
grams: number;
|
|
1691
|
+
oligos: number;
|
|
1692
|
+
nucleotides: number;
|
|
1693
|
+
overhead: number;
|
|
1694
|
+
density: string;
|
|
1695
|
+
reference: string;
|
|
1696
|
+
} {
|
|
1697
|
+
if (!Number.isFinite(sizebytes) || sizebytes <= 0 || sizebytes > 2 ** 40) {
|
|
1698
|
+
throw new quantumerror({
|
|
1699
|
+
code: 'sizerange',
|
|
1700
|
+
message: `capacity planner accepts archives up to 1 tebibyte, got ${sizebytes}`,
|
|
1701
|
+
});
|
|
1702
|
+
}
|
|
1703
|
+
const payload = sizebytes + 4;
|
|
1704
|
+
const blocks = Math.ceil(payload / dnavaultspec.blockpayloadbytes);
|
|
1705
|
+
const oligos = blocks * dnavaultspec.redundancy;
|
|
1706
|
+
const nucleotides = oligos * dnavaultspec.nucleotidesperblock;
|
|
1707
|
+
return {
|
|
1708
|
+
grams: sizebytes / dnacapacityspec.bytespergram,
|
|
1709
|
+
oligos,
|
|
1710
|
+
nucleotides,
|
|
1711
|
+
overhead: (blocks * dnavaultspec.blockframebytes + 4) / sizebytes - 1,
|
|
1712
|
+
density: dnacapacityspec.densityquote,
|
|
1713
|
+
reference: dnacapacityspec.densityreference,
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
/**
|
|
1718
|
+
* planodefault: the default dna archive plan for a payload of
|
|
1719
|
+
* sizebytes — dna mass in grams (215 pb/g density), number of oligo
|
|
1720
|
+
* strands to synthesize, and the codec overhead fraction. this is the
|
|
1721
|
+
* planner entry point for the storage tier; nothing is written.
|
|
1722
|
+
*/
|
|
1723
|
+
export function planodefault(sizebytes: number): {
|
|
1724
|
+
grams: number;
|
|
1725
|
+
strands: number;
|
|
1726
|
+
overhead: number;
|
|
1727
|
+
storage: string;
|
|
1728
|
+
reference: string;
|
|
1729
|
+
} {
|
|
1730
|
+
const estimate = dnacapacity(sizebytes);
|
|
1731
|
+
return {
|
|
1732
|
+
grams: estimate.grams,
|
|
1733
|
+
strands: estimate.oligos,
|
|
1734
|
+
overhead: estimate.overhead,
|
|
1735
|
+
storage: 'dna (goldman 2013 ternary rotation, 4 strands, dual placement)',
|
|
1736
|
+
reference: `${dnavaultspec.reference}; ${estimate.reference}`,
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
/* ------------------------------------------------------------------ */
|
|
1741
|
+
/* context: optical 5d quartz planner (360 tb/disc) */
|
|
1742
|
+
/* ------------------------------------------------------------------ */
|
|
1743
|
+
|
|
1744
|
+
/**
|
|
1745
|
+
* opticalvault anchors: 5d femtosecond-laser nanostructuring of fused
|
|
1746
|
+
* quartz as demonstrated by the university of southampton
|
|
1747
|
+
* optoelectronics research centre (zhang, kazansky et al., 2013-2016):
|
|
1748
|
+
* 360 tb per disc headline capacity, survival to 1000 c and the quoted
|
|
1749
|
+
* 13.8-billion-year room-temperature stability. the five dimensions
|
|
1750
|
+
* are three spatial coordinates plus nanograting orientation and
|
|
1751
|
+
* retardance. the write parameters below are planning defaults for the
|
|
1752
|
+
* femtosecond pass, not device constants.
|
|
1753
|
+
*/
|
|
1754
|
+
export const opticalvaultspec = {
|
|
1755
|
+
bytesperdisc: 3.6e14,
|
|
1756
|
+
layersperdisc: 100,
|
|
1757
|
+
logicalbitsperdot: 4,
|
|
1758
|
+
redundancycopies: 3,
|
|
1759
|
+
stabilityc: 1000,
|
|
1760
|
+
lifetimeyearsroom: 1.38e10,
|
|
1761
|
+
pulsedurationfs: 280,
|
|
1762
|
+
wavelengthnm: 1030,
|
|
1763
|
+
layerspacingum: 4.5,
|
|
1764
|
+
medium: 'fused quartz',
|
|
1765
|
+
dimensions: 'x, y, z + nanograting orientation + retardance',
|
|
1766
|
+
reference:
|
|
1767
|
+
'zhang, gecavicius, beresna, kazansky 2013 cleo; southampton orc 2013-2016 (360 tb/disc, 13.8 billion years at room temperature)',
|
|
1768
|
+
} as const satisfies Record<string, string | number>;
|
|
1769
|
+
|
|
1770
|
+
/**
|
|
1771
|
+
* opticalplan: recording plan for a 5d quartz archive of sizebytes:
|
|
1772
|
+
* disc count at 360 tb per disc with three-copy redundancy, layer and
|
|
1773
|
+
* nanograting dot counts at 4 logical bits per dot, and the
|
|
1774
|
+
* femtosecond write pass description. pure planner output.
|
|
1775
|
+
*/
|
|
1776
|
+
export function opticalplan(sizebytes: number): {
|
|
1777
|
+
discs: number;
|
|
1778
|
+
layers: number;
|
|
1779
|
+
dots: number;
|
|
1780
|
+
redundancy: number;
|
|
1781
|
+
lifetimeyearsroom: number;
|
|
1782
|
+
stabilityc: number;
|
|
1783
|
+
writeplan: {
|
|
1784
|
+
pulsedurationfs: number;
|
|
1785
|
+
wavelengthnm: number;
|
|
1786
|
+
layerspacingum: number;
|
|
1787
|
+
dimensions: string;
|
|
1788
|
+
};
|
|
1789
|
+
reference: string;
|
|
1790
|
+
note: string;
|
|
1791
|
+
} {
|
|
1792
|
+
if (!Number.isFinite(sizebytes) || sizebytes <= 0 || sizebytes > 2 ** 50) {
|
|
1793
|
+
throw new quantumerror({
|
|
1794
|
+
code: 'sizerange',
|
|
1795
|
+
message: `optical planner accepts archives up to 1 pebibyte, got ${sizebytes}`,
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
const stored = sizebytes * opticalvaultspec.redundancycopies;
|
|
1799
|
+
const discs = Math.max(1, Math.ceil(stored / opticalvaultspec.bytesperdisc));
|
|
1800
|
+
const dots = Math.ceil((stored * 8) / opticalvaultspec.logicalbitsperdot);
|
|
1801
|
+
return {
|
|
1802
|
+
discs,
|
|
1803
|
+
layers: discs * opticalvaultspec.layersperdisc,
|
|
1804
|
+
dots,
|
|
1805
|
+
redundancy: opticalvaultspec.redundancycopies,
|
|
1806
|
+
lifetimeyearsroom: opticalvaultspec.lifetimeyearsroom,
|
|
1807
|
+
stabilityc: opticalvaultspec.stabilityc,
|
|
1808
|
+
writeplan: {
|
|
1809
|
+
pulsedurationfs: opticalvaultspec.pulsedurationfs,
|
|
1810
|
+
wavelengthnm: opticalvaultspec.wavelengthnm,
|
|
1811
|
+
layerspacingum: opticalvaultspec.layerspacingum,
|
|
1812
|
+
dimensions: opticalvaultspec.dimensions,
|
|
1813
|
+
},
|
|
1814
|
+
reference: opticalvaultspec.reference,
|
|
1815
|
+
note: 'planning defaults, not device constants; lifetime figure is the quoted upper bound',
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
/* ------------------------------------------------------------------ */
|
|
1820
|
+
/* context: holographic note (microsoft hsd) and storage compare */
|
|
1821
|
+
/* ------------------------------------------------------------------ */
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* holographic storage note for the storage tier compare: microsoft
|
|
1825
|
+
* research announced the holographic storage device (hsd) program in
|
|
1826
|
+
* 2024 — page-oriented holograms in a photosensitive wafer cartridge,
|
|
1827
|
+
* about 1.4 tb per 12-centimeter disc form factor with a multi-terabyte
|
|
1828
|
+
* roadmap. this module keeps it as a comparison note only; the
|
|
1829
|
+
* concrete planners shipped here are dna and 5d quartz.
|
|
1830
|
+
*/
|
|
1831
|
+
export const holographicnote = {
|
|
1832
|
+
vendor: 'microsoft research',
|
|
1833
|
+
program: 'hsd (holographic storage device), announced 2024',
|
|
1834
|
+
capacity: 'about 1.4 tb per 12-cm disc, multi-terabyte roadmap',
|
|
1835
|
+
medium: 'photosensitive holographic wafer cartridges',
|
|
1836
|
+
position: 'comparison note; no planner shipped for this tier',
|
|
1837
|
+
} as const satisfies Record<string, string>;
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* storagecompare: one-line planner comparison across the three
|
|
1841
|
+
* quantum-adjacent storage tiers this module knows about.
|
|
1842
|
+
*/
|
|
1843
|
+
export function storagecompare(sizebytes: number): readonly {
|
|
1844
|
+
tier: string;
|
|
1845
|
+
units: string;
|
|
1846
|
+
massorunits: string;
|
|
1847
|
+
lifetime: string;
|
|
1848
|
+
reference: string;
|
|
1849
|
+
}[] {
|
|
1850
|
+
const dna = planodefault(sizebytes);
|
|
1851
|
+
const optical = opticalplan(sizebytes);
|
|
1852
|
+
return [
|
|
1853
|
+
{
|
|
1854
|
+
tier: 'dna',
|
|
1855
|
+
units: `${dna.strands} oligo strands`,
|
|
1856
|
+
massorunits: `${dna.grams.toExponential(3)} g of dna`,
|
|
1857
|
+
lifetime: 'centuries dry and dark (synthesis-dependent)',
|
|
1858
|
+
reference: 'goldman 2013 nature 494 77-80',
|
|
1859
|
+
},
|
|
1860
|
+
{
|
|
1861
|
+
tier: 'optical5d',
|
|
1862
|
+
units: `${optical.discs} quartz disc${optical.discs === 1 ? '' : 's'}`,
|
|
1863
|
+
massorunits: `${optical.layers} layers, ${optical.dots.toExponential(3)} dots`,
|
|
1864
|
+
lifetime: `quoted ${optical.lifetimeyearsroom.toExponential(2)} years at room temperature, ${optical.stabilityc} c survival`,
|
|
1865
|
+
reference: 'southampton orc 2013-2016',
|
|
1866
|
+
},
|
|
1867
|
+
{
|
|
1868
|
+
tier: 'holographic',
|
|
1869
|
+
units: `${Math.max(1, Math.ceil((sizebytes * 3) / 1.4e12))} hsd discs (planner sketch)`,
|
|
1870
|
+
massorunits: 'wafer cartridges',
|
|
1871
|
+
lifetime: 'decades, drive-dependent',
|
|
1872
|
+
reference: 'microsoft hsd 2024 announcement',
|
|
1873
|
+
},
|
|
1874
|
+
];
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
/* ------------------------------------------------------------------ */
|
|
1878
|
+
/* context: qram bucket brigade (giovannetti 2008, o(log n) routing) */
|
|
1879
|
+
/* ------------------------------------------------------------------ */
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* qramcell: one bucket-brigade memory slot addressed by its index.
|
|
1883
|
+
*/
|
|
1884
|
+
export interface qramcell {
|
|
1885
|
+
readonly address: number;
|
|
1886
|
+
readonly value: number;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
/**
|
|
1890
|
+
* qramreadresult: the value plus the routing trace that proves the
|
|
1891
|
+
* o(log n) switch activity of the bucket-brigade tree.
|
|
1892
|
+
*/
|
|
1893
|
+
export interface qramreadresult {
|
|
1894
|
+
readonly address: number;
|
|
1895
|
+
readonly value: number;
|
|
1896
|
+
readonly hops: number;
|
|
1897
|
+
readonly path: readonly number[];
|
|
1898
|
+
readonly switchesactive: number;
|
|
1899
|
+
readonly classicalwritelines: number;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
/**
|
|
1903
|
+
* qrambucketbrigade: classical simulation of the giovannetti-lloyd-
|
|
1904
|
+
* maccone bucket-brigade qram. a classical ram read energizes n word
|
|
1905
|
+
* lines; the bucket brigade routes one bus down a binary switch tree so
|
|
1906
|
+
* only log2(n) switches per read must flip (and, in the quantum
|
|
1907
|
+
* version, only o(log n) qubits must be active, which is what makes
|
|
1908
|
+
* quantum-addressed lookup of a superposition affordable). the switch
|
|
1909
|
+
* tree is simulated hop by hop and the read result carries the trace.
|
|
1910
|
+
* reference: giovannetti, lloyd, maccone, quantum random access memory,
|
|
1911
|
+
* prl 100, 160501 (2008); giovannetti, lloyd, maccone, architectures
|
|
1912
|
+
* for a quantum random access memory, pra 78, 052310 (2008).
|
|
1913
|
+
*/
|
|
1914
|
+
export class qrambucketbrigade {
|
|
1915
|
+
readonly #cells: number[];
|
|
1916
|
+
readonly #addressbits: number;
|
|
1917
|
+
|
|
1918
|
+
constructor(cells: number) {
|
|
1919
|
+
if (!Number.isInteger(cells) || cells < 2 || cells > 256 || (cells & (cells - 1)) !== 0) {
|
|
1920
|
+
throw new quantumerror({
|
|
1921
|
+
code: 'cellcount',
|
|
1922
|
+
message: `qram needs a power-of-two cell count in [2, 256], got ${cells}`,
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
this.#cells = new Array<number>(cells).fill(0);
|
|
1926
|
+
this.#addressbits = Math.round(Math.log2(cells));
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
/** number of addressable cells. */
|
|
1930
|
+
get cellscount(): number {
|
|
1931
|
+
return this.#cells.length;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
/** address width in bits (the tree depth). */
|
|
1935
|
+
get addressbits(): number {
|
|
1936
|
+
return this.#addressbits;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
/** switch count of the tree (inner nodes; n - 1 for n cells). */
|
|
1940
|
+
get switchcount(): number {
|
|
1941
|
+
return this.#cells.length - 1;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
/** classical write into one cell (energizes one word line). */
|
|
1945
|
+
write(address: number, value: number): void {
|
|
1946
|
+
this.#guardaddress(address);
|
|
1947
|
+
this.#cells[address] = value;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
/** lists the cells as an address/value table. */
|
|
1951
|
+
cells(): readonly qramcell[] {
|
|
1952
|
+
return this.#cells.map((value, address) => ({ address, value }));
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
/**
|
|
1956
|
+
* routes one read down the bucket-brigade tree: at level l the switch
|
|
1957
|
+
* forwards along address bit (addressbits - 1 - l); the bus ends at
|
|
1958
|
+
* the leaf and the trace records every switch node id visited
|
|
1959
|
+
* (root id 1, children 2i and 2i + 1).
|
|
1960
|
+
*/
|
|
1961
|
+
read(address: number): qramreadresult {
|
|
1962
|
+
this.#guardaddress(address);
|
|
1963
|
+
const path: number[] = [1];
|
|
1964
|
+
let node = 1;
|
|
1965
|
+
for (let level = 0; level < this.#addressbits; level++) {
|
|
1966
|
+
const bit = (address >> (this.#addressbits - 1 - level)) & 1;
|
|
1967
|
+
node = node * 2 + bit;
|
|
1968
|
+
path.push(node);
|
|
1969
|
+
}
|
|
1970
|
+
return {
|
|
1971
|
+
address,
|
|
1972
|
+
value: this.#cells[address],
|
|
1973
|
+
hops: this.#addressbits,
|
|
1974
|
+
path,
|
|
1975
|
+
switchesactive: this.#addressbits,
|
|
1976
|
+
classicalwritelines: this.#cells.length,
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
#guardaddress(address: number): void {
|
|
1981
|
+
if (!Number.isInteger(address) || address < 0 || address >= this.#cells.length) {
|
|
1982
|
+
throw new quantumerror({
|
|
1983
|
+
code: 'addressrange',
|
|
1984
|
+
message: `qram address must be in [0, ${this.#cells.length - 1}], got ${address}`,
|
|
1985
|
+
});
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
/**
|
|
1991
|
+
* qramdemo: the n=8 showcase — 3 switch levels, 7 switches, every read
|
|
1992
|
+
* routes 3 hops against 8 classical word lines, plus a note of what
|
|
1993
|
+
* the quantum version would query in one shot (a superposed address
|
|
1994
|
+
* returning a superposed memory word).
|
|
1995
|
+
*/
|
|
1996
|
+
export function qramdemo(): {
|
|
1997
|
+
reads: readonly qramreadresult[];
|
|
1998
|
+
levels: number;
|
|
1999
|
+
switches: number;
|
|
2000
|
+
hops: number;
|
|
2001
|
+
classicalwritelines: number;
|
|
2002
|
+
reference: string;
|
|
2003
|
+
} {
|
|
2004
|
+
const qram = new qrambucketbrigade(8);
|
|
2005
|
+
for (let address = 0; address < 8; address++) {
|
|
2006
|
+
qram.write(address, address * 100 + 7);
|
|
2007
|
+
}
|
|
2008
|
+
const reads = [qram.read(0), qram.read(3), qram.read(7)];
|
|
2009
|
+
return {
|
|
2010
|
+
reads,
|
|
2011
|
+
levels: qram.addressbits,
|
|
2012
|
+
switches: qram.switchcount,
|
|
2013
|
+
hops: qram.addressbits,
|
|
2014
|
+
classicalwritelines: qram.cellscount,
|
|
2015
|
+
reference: 'giovannetti, lloyd, maccone 2008 prl 100 160501',
|
|
2016
|
+
};
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
/* ------------------------------------------------------------------ */
|
|
2020
|
+
/* context: shor plan (theoretical 7-qubit circuit, never executed) */
|
|
2021
|
+
/* ------------------------------------------------------------------ */
|
|
2022
|
+
|
|
2023
|
+
/**
|
|
2024
|
+
* shorcircuitplan: the honest factoring plan. the module ships the
|
|
2025
|
+
* theoretical 7-qubit order-finding circuit for n = 15 (3 counting
|
|
2026
|
+
* qubits + 4 work qubits, base a in {7, 11}, period 4,
|
|
2027
|
+
* gcd(7^2 - 1, 15) = 3 and gcd(7^2 + 1, 15) = 5) and refuses to run it.
|
|
2028
|
+
*/
|
|
2029
|
+
export type shorcircuitplan = {
|
|
2030
|
+
readonly n: number;
|
|
2031
|
+
readonly executable: false;
|
|
2032
|
+
readonly qubits: number;
|
|
2033
|
+
readonly countingqubits: number;
|
|
2034
|
+
readonly workqubits: number;
|
|
2035
|
+
readonly bases: readonly number[];
|
|
2036
|
+
readonly expectedperiod: number;
|
|
2037
|
+
readonly factors: readonly number[];
|
|
2038
|
+
readonly stages: readonly string[];
|
|
2039
|
+
readonly gatesestimate: number;
|
|
2040
|
+
readonly reason: string;
|
|
2041
|
+
readonly references: readonly string[];
|
|
2042
|
+
};
|
|
2043
|
+
|
|
2044
|
+
/**
|
|
2045
|
+
* shorplan(n): returns the theoretical shor circuit for the
|
|
2046
|
+
* factorization and never runs it. honesty clause: compiled
|
|
2047
|
+
* controlled-modular-exponentiation networks for any semiprime beyond
|
|
2048
|
+
* toy size grow past the 20-qubit statevector budget of this
|
|
2049
|
+
* simulator (numbers above 15 need more than 20 clean qubits plus
|
|
2050
|
+
* ancillas before scheduling even starts in js), and the 2013
|
|
2051
|
+
* "oversimplifying quantum factoring" literature shows compressed
|
|
2052
|
+
* order-finding circuits for 15 can factor without any genuine quantum
|
|
2053
|
+
* advantage — so the module plans, cites and stops.
|
|
2054
|
+
* references: shor 1994 focs / siam j comput 26 1484 (1997);
|
|
2055
|
+
* vedal, barenco, ekert 1996 pra 54 147; vandersypen et al. 2001
|
|
2056
|
+
* nature 414 883 (the 7-qubit nmr run of 15); smolin, gambetta,
|
|
2057
|
+
* smith 2013 pra 87 030302 "oversimplifying quantum factoring".
|
|
2058
|
+
*/
|
|
2059
|
+
export function shorplan(n: number): shorcircuitplan {
|
|
2060
|
+
if (!Number.isInteger(n) || n < 4) {
|
|
2061
|
+
throw new quantumerror({
|
|
2062
|
+
code: 'compositerange',
|
|
2063
|
+
message: `shorplan needs an integer >= 4, got ${n}`,
|
|
2064
|
+
});
|
|
2065
|
+
}
|
|
2066
|
+
const isfifteen = n === 15;
|
|
2067
|
+
const workqubits = isfifteen ? 4 : Math.max(4, Math.ceil(Math.log2(n)));
|
|
2068
|
+
const countingqubits = isfifteen ? 3 : Math.max(3, 2 * workqubits - 5);
|
|
2069
|
+
const stages = [
|
|
2070
|
+
'init work register to |1> (x on the low work qubit)',
|
|
2071
|
+
`hadamard over the ${countingqubits} counting qubits (uniform superposition of exponents x)`,
|
|
2072
|
+
'controlled a^x mod n per exponent bit (order-4 swap pattern for a in {7, 11} when n = 15)',
|
|
2073
|
+
`inverse qft over the ${countingqubits} counting qubits`,
|
|
2074
|
+
'measure counting register: phases concentrate on k/4 with r = 4',
|
|
2075
|
+
'continued fractions on the measured phase -> candidate order r',
|
|
2076
|
+
'gcd(a^(r/2) - 1, n), gcd(a^(r/2) + 1, n) -> classical factors',
|
|
2077
|
+
];
|
|
2078
|
+
return {
|
|
2079
|
+
n,
|
|
2080
|
+
executable: false,
|
|
2081
|
+
qubits: countingqubits + workqubits,
|
|
2082
|
+
countingqubits,
|
|
2083
|
+
workqubits,
|
|
2084
|
+
bases: isfifteen ? [7, 11] : [2],
|
|
2085
|
+
expectedperiod: isfifteen ? 4 : 0,
|
|
2086
|
+
factors: isfifteen ? [3, 5] : [],
|
|
2087
|
+
stages,
|
|
2088
|
+
gatesestimate: isfifteen ? 400 : countingqubits * workqubits * workqubits * 12,
|
|
2089
|
+
reason:
|
|
2090
|
+
'theoretical plan only: controlled modular exponentiation for semiprimes beyond 15 exceeds the 20-qubit statevector envelope of this simulator, and compressed 15-circuits are known to factor without quantum advantage (smolin-gambetta-smith 2013)',
|
|
2091
|
+
references: [
|
|
2092
|
+
'shor 1994 focs; siam j comput 26 1484 (1997)',
|
|
2093
|
+
'vedal-barenco-ekert 1996 pra 54 147',
|
|
2094
|
+
'vandersypen et al. 2001 nature 414 883',
|
|
2095
|
+
'smolin-gambetta-smith 2013 pra 87 030302',
|
|
2096
|
+
],
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
|
|
2100
|
+
/* ------------------------------------------------------------------ */
|
|
2101
|
+
/* context: registry (capability catalog) */
|
|
2102
|
+
/* ------------------------------------------------------------------ */
|
|
2103
|
+
|
|
2104
|
+
/**
|
|
2105
|
+
* freeze helper with a const type parameter so the registry literal
|
|
2106
|
+
* keeps its exact readonly member types after freezing.
|
|
2107
|
+
*/
|
|
2108
|
+
function freeze<const T extends Record<string, unknown>>(spec: T): Readonly<T> {
|
|
2109
|
+
return Object.freeze(spec);
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
/**
|
|
2113
|
+
* quantumregistry: the capability catalog of this module for the engine
|
|
2114
|
+
* integrator: simulator envelope, provable algorithms, key-exchange
|
|
2115
|
+
* protocols, storage tiers, randomness sources and primary citations.
|
|
2116
|
+
*/
|
|
2117
|
+
export const quantumregistry = freeze({
|
|
2118
|
+
simulator: 'statevector',
|
|
2119
|
+
precision: 'float64 interleaved re/im',
|
|
2120
|
+
maxqubits: 20,
|
|
2121
|
+
algorithms: [
|
|
2122
|
+
'bellstate',
|
|
2123
|
+
'ghzstate',
|
|
2124
|
+
'grover2',
|
|
2125
|
+
'grover3',
|
|
2126
|
+
'deutsch',
|
|
2127
|
+
'qteleport',
|
|
2128
|
+
'shorplan (plan only)',
|
|
2129
|
+
] as const,
|
|
2130
|
+
protocols: ['bb84', 'e91'],
|
|
2131
|
+
storage: ['dna goldman 2013', 'optical 5d quartz', 'holographic note', 'qram bucket brigade'],
|
|
2132
|
+
random: ['csprng (node:crypto)', 'anu qrng (optional, fallback)'],
|
|
2133
|
+
sources: [
|
|
2134
|
+
'goldman et al. 2013 nature 494 77-80 doi:10.1038/nature11875',
|
|
2135
|
+
'church, gao, kosuri 2012 science 337 1628 (215 pb/g era)',
|
|
2136
|
+
'grover 1996 stoc arxiv:quant-ph/9605043',
|
|
2137
|
+
'deutsch-jozsa 1992 proc r soc lond a 439 553',
|
|
2138
|
+
'bennett-brassard 1984 bangalore proceedings 175-179',
|
|
2139
|
+
'ekert 1991 prl 67 661; chsh 1969 prl 23 880; bell 1964',
|
|
2140
|
+
'bennett et al. 1993 prl 70 1895 (teleportation)',
|
|
2141
|
+
'giovannetti-lloyd-maccone 2008 prl 100 160501 (qram)',
|
|
2142
|
+
'shor 1994/1997; vandersypen 2001 nature 414 883; smolin-gambetta-smith 2013',
|
|
2143
|
+
'zhang-kazansky et al. 2013-2016 southampton orc (5d quartz)',
|
|
2144
|
+
'microsoft research hsd 2024 announcement',
|
|
2145
|
+
'anu quantum numbers, api.quantumnumbers.io',
|
|
2146
|
+
],
|
|
2147
|
+
});
|
|
2148
|
+
|
|
2149
|
+
/** registry type alias for integrators that want the frozen shape. */
|
|
2150
|
+
export type quantumregistrytype = typeof quantumregistry;
|
|
2151
|
+
|
|
2152
|
+
/* ------------------------------------------------------------------ */
|
|
2153
|
+
/* context: self check (every claim, machine-verified) */
|
|
2154
|
+
/* ------------------------------------------------------------------ */
|
|
2155
|
+
|
|
2156
|
+
/**
|
|
2157
|
+
* one machine-verified claim row: name, pass flag and a detail line
|
|
2158
|
+
* with the measured numbers.
|
|
2159
|
+
*/
|
|
2160
|
+
export type quantumcheckrow = {
|
|
2161
|
+
readonly name: string;
|
|
2162
|
+
readonly pass: boolean;
|
|
2163
|
+
readonly detail: string;
|
|
2164
|
+
};
|
|
2165
|
+
|
|
2166
|
+
/**
|
|
2167
|
+
* quantumselfcheck: runs every documented claim of this module against
|
|
2168
|
+
* the real simulator — bell/grover/deutsch/teleport statistics, bb84
|
|
2169
|
+
* with and without an eavesdropper, the e91 chsh violation, the dna
|
|
2170
|
+
* roundtrip including strand loss, the qrng range, the qram routing
|
|
2171
|
+
* depth, the shor honesty clause, the 20-qubit envelope guard and the
|
|
2172
|
+
* using/disposal guard. returns the row table plus pass counts so the
|
|
2173
|
+
* integrator can gate ci on it. randomness is live node:crypto, so the
|
|
2174
|
+
* protocol rows carry the statistical margins described in their
|
|
2175
|
+
* circuit references.
|
|
2176
|
+
*/
|
|
2177
|
+
export function quantumselfcheck(): {
|
|
2178
|
+
rows: readonly quantumcheckrow[];
|
|
2179
|
+
passed: number;
|
|
2180
|
+
failed: number;
|
|
2181
|
+
reference: string;
|
|
2182
|
+
} {
|
|
2183
|
+
const rows: quantumcheckrow[] = [];
|
|
2184
|
+
const push = (name: string, pass: boolean, detail: string): void => {
|
|
2185
|
+
rows.push({ name, pass, detail });
|
|
2186
|
+
};
|
|
2187
|
+
|
|
2188
|
+
// 1. envelope guard: 21 qubits must be refused.
|
|
2189
|
+
let refused = false;
|
|
2190
|
+
try {
|
|
2191
|
+
new quantumsim(21);
|
|
2192
|
+
} catch (cause) {
|
|
2193
|
+
refused = cause instanceof quantumerror && cause.code === 'qubitrange';
|
|
2194
|
+
}
|
|
2195
|
+
push('maxqubits guard', refused, `quantumsim(21) refused: ${refused}`);
|
|
2196
|
+
|
|
2197
|
+
// 2. norm stability under a 12-qubit hadamard ladder.
|
|
2198
|
+
{
|
|
2199
|
+
using ladder = new quantumsim(12);
|
|
2200
|
+
for (let q = 0; q < 12; q++) {
|
|
2201
|
+
ladder.h(q);
|
|
2202
|
+
}
|
|
2203
|
+
const drift = Math.abs(ladder.norm() - 1);
|
|
2204
|
+
push(
|
|
2205
|
+
'statevector norm',
|
|
2206
|
+
drift < 1e-12,
|
|
2207
|
+
`12 hadamards leave norm 1 within ${drift.toExponential(2)}`,
|
|
2208
|
+
);
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
// 3. bell state distribution.
|
|
2212
|
+
{
|
|
2213
|
+
using bell = bellstate().sim;
|
|
2214
|
+
const p = bell.probabilities();
|
|
2215
|
+
const ok =
|
|
2216
|
+
Math.abs(p[0] - 0.5) < 1e-12 && Math.abs(p[3] - 0.5) < 1e-12 && p[1] < 1e-12 && p[2] < 1e-12;
|
|
2217
|
+
push(
|
|
2218
|
+
'bellstate',
|
|
2219
|
+
ok,
|
|
2220
|
+
`probs [${p[0].toFixed(6)}, ${p[1].toFixed(6)}, ${p[2].toFixed(6)}, ${p[3].toFixed(6)}] ~ [0.5, 0, 0.5, 0]`,
|
|
2221
|
+
);
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
// 4. ghz corners.
|
|
2225
|
+
{
|
|
2226
|
+
using ghz = ghzstate(4).sim;
|
|
2227
|
+
const p = ghz.probabilities();
|
|
2228
|
+
const ok = Math.abs(p[0] - 0.5) < 1e-12 && Math.abs(p[15] - 0.5) < 1e-12;
|
|
2229
|
+
push('ghzstate(4)', ok, `p[0000]=${p[0].toFixed(6)}, p[1111]=${p[15].toFixed(6)}`);
|
|
2230
|
+
}
|
|
2231
|
+
|
|
2232
|
+
// 5. grover2 certainty.
|
|
2233
|
+
{
|
|
2234
|
+
using g2 = grover2().sim;
|
|
2235
|
+
const p11 = g2.probabilities()[3];
|
|
2236
|
+
push('grover2', p11 >= 0.99, `prob(11) = ${p11.toFixed(9)} (>= 0.99)`);
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
// 6. grover3 statistics.
|
|
2240
|
+
{
|
|
2241
|
+
using g3 = grover3().sim;
|
|
2242
|
+
const p111 = g3.probabilities()[7];
|
|
2243
|
+
push('grover3', p111 >= 0.9, `prob(111) = ${p111.toFixed(6)} (theory 0.9461, >= 0.9)`);
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
// 7-8. deutsch both promises.
|
|
2247
|
+
{
|
|
2248
|
+
const constant = deutsch(() => 0);
|
|
2249
|
+
const balanced = deutsch((x) => x);
|
|
2250
|
+
push(
|
|
2251
|
+
'deutsch constant',
|
|
2252
|
+
constant.verdict === 'constant' && constant.verdict === constant.expected,
|
|
2253
|
+
`f=0 -> ${constant.verdict} (measured ${constant.measured})`,
|
|
2254
|
+
);
|
|
2255
|
+
push(
|
|
2256
|
+
'deutsch balanced',
|
|
2257
|
+
balanced.verdict === 'balanced' && balanced.verdict === balanced.expected,
|
|
2258
|
+
`f=x -> ${balanced.verdict} (measured ${balanced.measured})`,
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// 9-10. teleportation fidelity for both nontrivial inputs.
|
|
2263
|
+
{
|
|
2264
|
+
const one = qteleport('one');
|
|
2265
|
+
const plus = qteleport('plus');
|
|
2266
|
+
push(
|
|
2267
|
+
'qteleport |1>',
|
|
2268
|
+
Math.abs(one.fidelity - 1) < 1e-12,
|
|
2269
|
+
`fidelity ${one.fidelity.toFixed(12)} (corrections m0=${one.outcome0}, m1=${one.outcome1})`,
|
|
2270
|
+
);
|
|
2271
|
+
push(
|
|
2272
|
+
'qteleport |+>',
|
|
2273
|
+
Math.abs(plus.fidelity - 1) < 1e-12,
|
|
2274
|
+
`fidelity ${plus.fidelity.toFixed(12)} (basis restored after z correction)`,
|
|
2275
|
+
);
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
// 11-12. bb84 clean channel and intercept-resend.
|
|
2279
|
+
{
|
|
2280
|
+
const clean = runbb84({ eve: false, bits: 1024 });
|
|
2281
|
+
push(
|
|
2282
|
+
'bb84 clean',
|
|
2283
|
+
clean.qber < 0.05 && !clean.detected,
|
|
2284
|
+
`qber ${clean.qber.toFixed(4)} < 0.05, detected ${clean.detected}, sifted ${clean.siftedlen}, key ${clean.keyalice.length} bits`,
|
|
2285
|
+
);
|
|
2286
|
+
const attacked = runbb84({ eve: true, bits: 1024 });
|
|
2287
|
+
push(
|
|
2288
|
+
'bb84 intercept-resend',
|
|
2289
|
+
attacked.detected && attacked.qber > 0.2,
|
|
2290
|
+
`qber ${attacked.qber.toFixed(4)} > 0.2, sampled ${attacked.sampledqber.toFixed(4)} > ${attacked.threshold}, detected ${attacked.detected}`,
|
|
2291
|
+
);
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
// 13-14. e91 chsh violation and collapse under eve.
|
|
2295
|
+
{
|
|
2296
|
+
const clean = rune91({ eve: false, pairs: 2400 });
|
|
2297
|
+
push(
|
|
2298
|
+
'e91 clean chsh',
|
|
2299
|
+
clean.s > 2.4 && !clean.detected,
|
|
2300
|
+
`s = ${clean.s.toFixed(4)} > 2.4 (tsirelson 2.8284), detected ${clean.detected}, keylen ${clean.keylen}`,
|
|
2301
|
+
);
|
|
2302
|
+
const attacked = rune91({ eve: true, pairs: 2400 });
|
|
2303
|
+
push(
|
|
2304
|
+
'e91 intercept-resend',
|
|
2305
|
+
attacked.detected,
|
|
2306
|
+
`s = ${attacked.s.toFixed(4)} (classical bound 2), qber ${Number.isFinite(attacked.qber) ? attacked.qber.toFixed(4) : 'n/a'}, detected ${attacked.detected}`,
|
|
2307
|
+
);
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// 15. dna roundtrip with a strand lost.
|
|
2311
|
+
{
|
|
2312
|
+
const payload = randomBytes(1000);
|
|
2313
|
+
const encoded = dnavaultencode(payload);
|
|
2314
|
+
const direct = dnavaultdecode(encoded.strands);
|
|
2315
|
+
const degraded = dnavaultdecode([encoded.strands[0], encoded.strands[2], encoded.strands[3]]);
|
|
2316
|
+
let equal = direct.length === payload.length && degraded.length === payload.length;
|
|
2317
|
+
for (let i = 0; equal && i < payload.length; i++) {
|
|
2318
|
+
equal = direct[i] === payload[i] && degraded[i] === payload[i];
|
|
2319
|
+
}
|
|
2320
|
+
let runmax = 1;
|
|
2321
|
+
for (const strand of encoded.strands) {
|
|
2322
|
+
let current = 1;
|
|
2323
|
+
for (let i = 1; i < strand.length; i++) {
|
|
2324
|
+
current = strand[i] === strand[i - 1] ? current + 1 : 1;
|
|
2325
|
+
if (current > runmax) {
|
|
2326
|
+
runmax = current;
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
push(
|
|
2331
|
+
'dnavault roundtrip',
|
|
2332
|
+
equal && runmax <= 3,
|
|
2333
|
+
`1000 bytes roundtrip byte-perfect with strand 1 lost; longest homopolymer run ${runmax} (<= 3)`,
|
|
2334
|
+
);
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
// 16. qrng range.
|
|
2338
|
+
{
|
|
2339
|
+
const values = quantuminspiredrandom(4096, { min: -7, max: 19 });
|
|
2340
|
+
const distinct = new Set(values).size;
|
|
2341
|
+
const inrange = values.every((value) => Number.isInteger(value) && value >= -7 && value < 19);
|
|
2342
|
+
push(
|
|
2343
|
+
'qrng range',
|
|
2344
|
+
inrange && distinct > 16,
|
|
2345
|
+
`4096 draws in [-7, 19): all in range ${inrange}, ${distinct} distinct values`,
|
|
2346
|
+
);
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
// 17. qram routing depth.
|
|
2350
|
+
{
|
|
2351
|
+
const demo = qramdemo();
|
|
2352
|
+
push(
|
|
2353
|
+
'qram bucket brigade',
|
|
2354
|
+
demo.hops === 3 && demo.switches === 7 && demo.reads.every((read) => read.hops === 3),
|
|
2355
|
+
`n=8: ${demo.switches} switches, every read routes ${demo.hops} hops vs ${demo.classicalwritelines} word lines`,
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
// 18. shor plan honesty.
|
|
2360
|
+
{
|
|
2361
|
+
const plan = shorplan(15);
|
|
2362
|
+
push(
|
|
2363
|
+
'shorplan honest',
|
|
2364
|
+
plan.executable === false && plan.qubits === 7 && plan.expectedperiod === 4,
|
|
2365
|
+
`n=15 plan: ${plan.qubits} qubits, period ${plan.expectedperiod}, factors [${plan.factors.join(', ')}], execution refused`,
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
// 19. using/disposal guard.
|
|
2370
|
+
{
|
|
2371
|
+
let survivor: quantumsim | undefined;
|
|
2372
|
+
{
|
|
2373
|
+
using disposable = new quantumsim(2);
|
|
2374
|
+
disposable.h(0);
|
|
2375
|
+
survivor = disposable;
|
|
2376
|
+
}
|
|
2377
|
+
let disposed = false;
|
|
2378
|
+
try {
|
|
2379
|
+
survivor.x(0);
|
|
2380
|
+
} catch (cause) {
|
|
2381
|
+
disposed = cause instanceof quantumerror && cause.code === 'disposed';
|
|
2382
|
+
}
|
|
2383
|
+
push('using disposal', disposed, 'post-using gate call threw quantumerror(disposed)');
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
const passed = rows.filter((row) => row.pass).length;
|
|
2387
|
+
return {
|
|
2388
|
+
rows,
|
|
2389
|
+
passed,
|
|
2390
|
+
failed: rows.length - passed,
|
|
2391
|
+
reference: 'task v18-quantum validation matrix; live node:crypto randomness',
|
|
2392
|
+
};
|
|
2393
|
+
}
|