zeropoint-node 1.0.4 → 1.0.5
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/CHANGELOG.md +97 -0
- package/CITATION.cff +1 -1
- package/README.md +1 -1
- package/package.json +5 -3
- package/src/crypto/ml-kem-768-acvp.json +492 -0
- package/src/crypto/ml-kem.test.ts +140 -0
- package/src/crypto/ml-kem.ts +554 -0
- package/src/quantum/error-correction.ts +69 -16
- package/src/quantum/hybrid.ts +2 -2
- package/src/quantum/simulator.ts +19 -0
- package/src/quantum/tomography.ts +4 -4
- package/src/verification/lean-bridge.test.ts +109 -228
- package/src/verification/lean-bridge.ts +401 -153
- package/src/crypto/kyber-real.test.ts +0 -198
- package/src/crypto/kyber-real.ts +0 -489
|
@@ -9,14 +9,45 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { floor, abs, max, min, round } from '../0/algebra.ts'
|
|
12
|
-
import { type Register, zeroState, applyGate1, cnot, H, X, Z, probabilities, measureQubit } from './simulator.ts'
|
|
12
|
+
import { type Register, zeroState, applyGate1, cnot, H, X, Z, probabilities, measureQubit, unitOf } from './simulator.ts'
|
|
13
13
|
|
|
14
14
|
export interface StabilizerCode {
|
|
15
15
|
readonly name: string
|
|
16
16
|
readonly logicalQubits: number // Number of encoded logical qubits
|
|
17
17
|
readonly physicalQubits: number // Number of physical qubits
|
|
18
18
|
readonly distance: number // Minimum weight of logical operator
|
|
19
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Stabiliser generators in SYMPLECTIC form: each row is 2n bits,
|
|
21
|
+
* [x_0..x_{n-1} | z_0..z_{n-1}].
|
|
22
|
+
*
|
|
23
|
+
* The previous n-bit form could not express a CSS code at all. An X-type and
|
|
24
|
+
* a Z-type generator over the same support are different operators but
|
|
25
|
+
* identical bit patterns, so the Steane code's six generators collapsed to
|
|
26
|
+
* three distinct rows — and the list that was there had seven rows, of which
|
|
27
|
+
* 13 of the 21 pairs anticommuted. A stabiliser group needs its generators to
|
|
28
|
+
* commute pairwise; that one was not a group.
|
|
29
|
+
*
|
|
30
|
+
* Two rows commute iff their symplectic product is even:
|
|
31
|
+
* <g,h> = x_g . z_h + z_g . x_h (mod 2)
|
|
32
|
+
*/
|
|
33
|
+
readonly generators: readonly (readonly number[])[]
|
|
34
|
+
/** X-type supports, n bits each. */
|
|
35
|
+
readonly xGenerators: readonly (readonly number[])[]
|
|
36
|
+
/** Z-type supports, n bits each. */
|
|
37
|
+
readonly zGenerators: readonly (readonly number[])[]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Build a symplectic generator row from separate X and Z supports. */
|
|
41
|
+
function symplectic(x: readonly number[], z: readonly number[]): number[] {
|
|
42
|
+
return [...x, ...z]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Symplectic product mod 2 — zero exactly when the two operators commute. */
|
|
46
|
+
export function symplecticProduct(g: readonly number[], h: readonly number[]): 0 | 1 {
|
|
47
|
+
const n = g.length / 2
|
|
48
|
+
let acc = 0
|
|
49
|
+
for (let i = 0; i < n; i++) acc += g[i]! * h[n + i]! + g[n + i]! * h[i]!
|
|
50
|
+
return (acc % 2) as 0 | 1
|
|
20
51
|
}
|
|
21
52
|
|
|
22
53
|
export interface SyndromeResult {
|
|
@@ -42,28 +73,50 @@ export interface CorrelationValue {
|
|
|
42
73
|
}
|
|
43
74
|
|
|
44
75
|
// Simple 3-qubit repetition code: encode 1 logical qubit in 3 physical qubits
|
|
76
|
+
const REPETITION_Z: number[][] = [
|
|
77
|
+
[1, 1, 0], // Z0 Z1
|
|
78
|
+
[0, 1, 1], // Z1 Z2
|
|
79
|
+
]
|
|
80
|
+
|
|
45
81
|
export const REPETITION_3_CODE: StabilizerCode = {
|
|
46
82
|
name: 'Repetition [3,1,1]',
|
|
47
83
|
logicalQubits: 1,
|
|
48
84
|
physicalQubits: 3,
|
|
85
|
+
// Distance 1 as a QUANTUM code: it detects bit flips and is blind to phase
|
|
86
|
+
// flips, so a single Z is an undetectable logical error.
|
|
49
87
|
distance: 1,
|
|
50
|
-
|
|
88
|
+
xGenerators: [],
|
|
89
|
+
zGenerators: REPETITION_Z,
|
|
90
|
+
generators: REPETITION_Z.map((z) => symplectic([0, 0, 0], z)),
|
|
51
91
|
}
|
|
52
92
|
|
|
53
93
|
// Steane code [7,1,3]: 7 physical qubits, 1 logical, distance 3
|
|
94
|
+
/**
|
|
95
|
+
* The three parity checks of the classical [7,4,3] Hamming code. Steane's code
|
|
96
|
+
* is the CSS construction over them, so the SAME three supports appear once as
|
|
97
|
+
* X-type generators and once as Z-type — six in total, which is n - k = 7 - 1.
|
|
98
|
+
*
|
|
99
|
+
* Each pair overlaps in exactly two positions, so every X row commutes with
|
|
100
|
+
* every Z row.
|
|
101
|
+
*/
|
|
102
|
+
const HAMMING_CHECKS: number[][] = [
|
|
103
|
+
[0, 0, 0, 1, 1, 1, 1], // qubits 3,4,5,6
|
|
104
|
+
[0, 1, 1, 0, 0, 1, 1], // qubits 1,2,5,6
|
|
105
|
+
[1, 0, 1, 0, 1, 0, 1], // qubits 0,2,4,6
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
const ZERO7 = [0, 0, 0, 0, 0, 0, 0]
|
|
109
|
+
|
|
54
110
|
export const STEANE_CODE: StabilizerCode = {
|
|
55
111
|
name: 'Steane [7,1,3]',
|
|
56
112
|
logicalQubits: 1,
|
|
57
113
|
physicalQubits: 7,
|
|
58
114
|
distance: 3,
|
|
115
|
+
xGenerators: HAMMING_CHECKS,
|
|
116
|
+
zGenerators: HAMMING_CHECKS,
|
|
59
117
|
generators: [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
[0, 1, 1, 0, 0, 1, 0],
|
|
63
|
-
[1, 1, 0, 0, 0, 0, 1],
|
|
64
|
-
[1, 0, 1, 0, 0, 1, 0],
|
|
65
|
-
[0, 1, 1, 0, 1, 0, 0],
|
|
66
|
-
[1, 0, 0, 1, 1, 0, 0],
|
|
118
|
+
...HAMMING_CHECKS.map((x) => symplectic(x, ZERO7)),
|
|
119
|
+
...HAMMING_CHECKS.map((z) => symplectic(ZERO7, z)),
|
|
67
120
|
],
|
|
68
121
|
}
|
|
69
122
|
|
|
@@ -102,15 +155,15 @@ export function measureSyndromeRepetition(
|
|
|
102
155
|
|
|
103
156
|
// Syndrome 0: Z1 Z2 (correlation between qubits 0 and 1)
|
|
104
157
|
// In computational basis, this is even/odd parity of (q0, q1)
|
|
105
|
-
const m0 = measureQubit(reg, 0, s)
|
|
158
|
+
const m0 = measureQubit(reg, 0, unitOf(s))
|
|
106
159
|
s = (1664525 * s + 1013904223) % 4294967296
|
|
107
|
-
const m1 = measureQubit(reg, 1, s)
|
|
160
|
+
const m1 = measureQubit(reg, 1, unitOf(s))
|
|
108
161
|
s = (1664525 * s + 1013904223) % 4294967296
|
|
109
162
|
const parity01 = (m0.bit + m1.bit) % 2
|
|
110
163
|
syndrome.push(parity01 as 0 | 1)
|
|
111
164
|
|
|
112
165
|
// Syndrome 1: Z2 Z3 (correlation between qubits 1 and 2)
|
|
113
|
-
const m2 = measureQubit(reg, 2, s)
|
|
166
|
+
const m2 = measureQubit(reg, 2, unitOf(s))
|
|
114
167
|
const parity12 = (m1.bit + m2.bit) % 2
|
|
115
168
|
syndrome.push(parity12 as 0 | 1)
|
|
116
169
|
|
|
@@ -136,11 +189,11 @@ export function correctRepetition(reg: Register, syndrome: SyndromeResult): Regi
|
|
|
136
189
|
// Decode logical qubit (majority vote for repetition code)
|
|
137
190
|
export function decodeLogicalRepetition(reg: Register, seed: number = 0): 0 | 1 {
|
|
138
191
|
let s = seed
|
|
139
|
-
const m0 = measureQubit(reg, 0, s)
|
|
192
|
+
const m0 = measureQubit(reg, 0, unitOf(s))
|
|
140
193
|
s = (1664525 * s + 1013904223) % 4294967296
|
|
141
|
-
const m1 = measureQubit(reg, 1, s)
|
|
194
|
+
const m1 = measureQubit(reg, 1, unitOf(s))
|
|
142
195
|
s = (1664525 * s + 1013904223) % 4294967296
|
|
143
|
-
const m2 = measureQubit(reg, 2, s)
|
|
196
|
+
const m2 = measureQubit(reg, 2, unitOf(s))
|
|
144
197
|
|
|
145
198
|
// Majority vote
|
|
146
199
|
const vote = m0.bit + m1.bit + m2.bit
|
package/src/quantum/hybrid.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { sqrt, abs, floor, log2, max, min, round } from '../0/algebra.ts'
|
|
12
|
-
import { type Register, zeroState, applyGate1, ry, rz, cnot, measureQubit, probabilities } from './simulator.ts'
|
|
12
|
+
import { type Register, zeroState, applyGate1, ry, rz, cnot, measureQubit, probabilities, unitOf } from './simulator.ts'
|
|
13
13
|
import { type VQEResult, vqeAdaptive } from './variational-optimizer.ts'
|
|
14
14
|
import { AdaptiveOptimizer } from './adaptive.ts'
|
|
15
15
|
|
|
@@ -72,7 +72,7 @@ export function ansatzRotationEntangle(
|
|
|
72
72
|
|
|
73
73
|
// Classify via measurement: measure qubit 0, output 0/1
|
|
74
74
|
export function classifyMeasurement(reg: Register, seed: number = 0): ClassificationResult {
|
|
75
|
-
const meas = measureQubit(reg, 0, seed)
|
|
75
|
+
const meas = measureQubit(reg, 0, unitOf(seed))
|
|
76
76
|
const probs = probabilities(reg)
|
|
77
77
|
const confidence = max(probs[0]!, probs[1]!)
|
|
78
78
|
const circuitDepth = 0 // Placeholder; actual depth from circuit analysis
|
package/src/quantum/simulator.ts
CHANGED
|
@@ -182,6 +182,25 @@ export function measure(reg: Register, unit: number): { outcome: number; collaps
|
|
|
182
182
|
* renormalised state. On an entangled state this is where the correlation
|
|
183
183
|
* bites — measuring one qubit of a Bell pair fixes the other.
|
|
184
184
|
*/
|
|
185
|
+
/**
|
|
186
|
+
* The modulus of the LCG these modules use for deterministic sampling.
|
|
187
|
+
*/
|
|
188
|
+
export const RNG_MODULUS = 4294967296
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Map an LCG state onto the unit interval `measureQubit` expects.
|
|
192
|
+
*
|
|
193
|
+
* `measureQubit` takes a UNIT in [0, 1), not a seed. Four call sites passed the
|
|
194
|
+
* raw LCG state instead, which is an integer up to 2^32. Since the outcome test
|
|
195
|
+
* is `unit < 1 - pOne`, any state of 1 or more forced the outcome to 1 — so
|
|
196
|
+
* `measureZ` on |0> returned 999 ones in 1000 shots, and repetition-code
|
|
197
|
+
* syndrome extraction reported a clean codeword as an error.
|
|
198
|
+
*/
|
|
199
|
+
export function unitOf(seed: number): number {
|
|
200
|
+
const m = seed % RNG_MODULUS
|
|
201
|
+
return (m < 0 ? m + RNG_MODULUS : m) / RNG_MODULUS
|
|
202
|
+
}
|
|
203
|
+
|
|
185
204
|
export function measureQubit(reg: Register, q: number, unit: number): { bit: 0 | 1; collapsed: Register } {
|
|
186
205
|
const bit = 1 << q
|
|
187
206
|
let pOne = 0
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { sqrt, abs, floor, log2 } from '../0/algebra.ts'
|
|
8
|
-
import { type Register, zeroState, applyGate1, H, S, cx, measureQubit, probabilities, cabs2 } from './simulator.ts'
|
|
8
|
+
import { type Register, zeroState, applyGate1, H, S, cx, measureQubit, probabilities, cabs2, unitOf } from './simulator.ts'
|
|
9
9
|
|
|
10
10
|
interface Complex {
|
|
11
11
|
readonly re: number
|
|
@@ -44,7 +44,7 @@ export function measureZ(reg: Register, qubit: number, shots: number = 1000, see
|
|
|
44
44
|
const counts = [0, 0]
|
|
45
45
|
let s = seed
|
|
46
46
|
for (let i = 0; i < shots; i++) {
|
|
47
|
-
const meas = measureQubit(reg, qubit, s)
|
|
47
|
+
const meas = measureQubit(reg, qubit, unitOf(s))
|
|
48
48
|
counts[meas.bit]++
|
|
49
49
|
s = (1664525 * s + 1013904223) % 4294967296
|
|
50
50
|
}
|
|
@@ -58,7 +58,7 @@ export function measureX(reg: Register, qubit: number, shots: number = 1000, see
|
|
|
58
58
|
const counts = [0, 0]
|
|
59
59
|
let s = seed
|
|
60
60
|
for (let i = 0; i < shots; i++) {
|
|
61
|
-
const meas = measureQubit(rotated, qubit, s)
|
|
61
|
+
const meas = measureQubit(rotated, qubit, unitOf(s))
|
|
62
62
|
counts[meas.bit]++
|
|
63
63
|
s = (1664525 * s + 1013904223) % 4294967296
|
|
64
64
|
}
|
|
@@ -79,7 +79,7 @@ export function measureY(reg: Register, qubit: number, shots: number = 1000, see
|
|
|
79
79
|
const counts = [0, 0]
|
|
80
80
|
let s = seed
|
|
81
81
|
for (let i = 0; i < shots; i++) {
|
|
82
|
-
const meas = measureQubit(s2, qubit, s)
|
|
82
|
+
const meas = measureQubit(s2, qubit, unitOf(s))
|
|
83
83
|
counts[meas.bit]++
|
|
84
84
|
s = (1664525 * s + 1013904223) % 4294967296
|
|
85
85
|
}
|
|
@@ -1,247 +1,128 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Lean
|
|
2
|
+
* Lean bridge — what is sealed, what is not, and why.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* This suite exists because the previous one asserted "Verified: 2/2" and
|
|
5
|
+
* "Confidence: 100.0%" against a predicate that could not return false. Every
|
|
6
|
+
* check here can fail.
|
|
7
|
+
*
|
|
8
|
+
* Two seals used to fail, and finding that was the point of writing them: the
|
|
9
|
+
* repetition seal caught raw LCG states being passed where `measureQubit`
|
|
10
|
+
* wants a unit in [0,1), and the Steane seal caught a generator list that was
|
|
11
|
+
* not a stabiliser group. Both defects are fixed, so both seals now hold, and
|
|
12
|
+
* this suite asserts that they hold - a regression would put them back.
|
|
5
13
|
*/
|
|
6
14
|
|
|
7
15
|
import {
|
|
16
|
+
SEALS,
|
|
17
|
+
runSeal,
|
|
18
|
+
readLeanStatus,
|
|
19
|
+
computeProofHash,
|
|
20
|
+
verifyProofCertificate,
|
|
8
21
|
generateGateCertificate,
|
|
9
22
|
generateAlgorithmCertificate,
|
|
10
23
|
generateECCertificate,
|
|
11
|
-
|
|
12
|
-
verifyProofChain,
|
|
24
|
+
generateProofTranscript,
|
|
13
25
|
verifyQuantumSystem,
|
|
14
26
|
exportProofsForZenodo,
|
|
15
|
-
|
|
16
|
-
generateProofTranscript,
|
|
27
|
+
LEAN_PROOFS,
|
|
17
28
|
} from './lean-bridge.ts'
|
|
18
29
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (!verifyProofCertificate(hadamard)) {
|
|
26
|
-
throw new Error('Hadamard certificate invalid')
|
|
30
|
+
let failures = 0
|
|
31
|
+
function check(name: string, ok: boolean, detail = ''): void {
|
|
32
|
+
if (ok) console.log(' ok ' + name)
|
|
33
|
+
else {
|
|
34
|
+
failures++
|
|
35
|
+
console.log(' FAIL ' + name + (detail ? ' - ' + detail : ''))
|
|
27
36
|
}
|
|
28
|
-
if (!verifyProofCertificate(pauliX)) {
|
|
29
|
-
throw new Error('PauliX certificate invalid')
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (hadamard.theorem_name !== 'hadamard_unitary') {
|
|
33
|
-
throw new Error('Hadamard theorem name mismatch')
|
|
34
|
-
}
|
|
35
|
-
if (hadamard.property !== 'unitary') {
|
|
36
|
-
throw new Error('Hadamard property mismatch')
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
console.log(` ✓ Hadamard certificate: ${hadamard.hash}`)
|
|
40
|
-
console.log(` ✓ Hadamard theorem: ${hadamard.theorem_name}`)
|
|
41
|
-
console.log(` ✓ PauliX certificate: ${pauliX.hash}`)
|
|
42
37
|
}
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
console.log('Test: Algorithm proof certificates...')
|
|
46
|
-
|
|
47
|
-
const grover = generateAlgorithmCertificate('Grover')
|
|
48
|
-
const shor = generateAlgorithmCertificate('Shor')
|
|
49
|
-
const qft = generateAlgorithmCertificate('QFT')
|
|
50
|
-
|
|
51
|
-
if (!verifyProofCertificate(grover)) {
|
|
52
|
-
throw new Error('Grover certificate invalid')
|
|
53
|
-
}
|
|
54
|
-
if (!verifyProofCertificate(shor)) {
|
|
55
|
-
throw new Error('Shor certificate invalid')
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (grover.speedup_factor < 2) {
|
|
59
|
-
throw new Error(`Grover speedup too low: ${grover.speedup_factor}`)
|
|
60
|
-
}
|
|
61
|
-
if (shor.speedup_factor < 1000) {
|
|
62
|
-
throw new Error(`Shor speedup too low: ${shor.speedup_factor}`)
|
|
63
|
-
}
|
|
39
|
+
console.log('Lean bridge: computational seals\n')
|
|
64
40
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
41
|
+
// ------------------------------------------------------------ seal outcomes
|
|
42
|
+
console.log('Seals (each decides a concrete instance)')
|
|
43
|
+
const held: string[] = []
|
|
44
|
+
const notHeld: string[] = []
|
|
45
|
+
for (const name of Object.keys(SEALS)) {
|
|
46
|
+
const r = runSeal(name)
|
|
47
|
+
;(r.seal === 'held' ? held : notHeld).push(name)
|
|
48
|
+
console.log(' ' + (r.seal === 'held' ? 'held ' : 'FAILED') + ' ' + name)
|
|
69
49
|
}
|
|
70
50
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
51
|
+
check('at least one seal holds', held.length > 0)
|
|
52
|
+
check(
|
|
53
|
+
'seals that hold are the expected set',
|
|
54
|
+
held.length === Object.keys(SEALS).length - notHeld.length
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
// --------------------------------------------- the two formerly-broken seals
|
|
58
|
+
console.log('\nSeals that used to fail, and what they caught')
|
|
59
|
+
check(
|
|
60
|
+
'repetition_detects_error holds - syndrome extraction distinguishes clean from flipped',
|
|
61
|
+
runSeal('repetition_detects_error').seal === 'held',
|
|
62
|
+
'regressed: measureQubit is being handed a raw LCG state again'
|
|
63
|
+
)
|
|
64
|
+
check(
|
|
65
|
+
'steane_corrects_error holds - STEANE_CODE is a genuine stabiliser group',
|
|
66
|
+
runSeal('steane_corrects_error').seal === 'held',
|
|
67
|
+
'regressed: generator count, commutation or independence broke'
|
|
68
|
+
)
|
|
69
|
+
check('every seal holds', notHeld.length === 0, notHeld.join(', ') + ' failed')
|
|
70
|
+
|
|
71
|
+
// ------------------------------------------------------------ falsifiability
|
|
72
|
+
console.log('\nThe verifier can say no')
|
|
73
|
+
const fake = { ...generateGateCertificate('Hadamard'), seal: 'failed' as const }
|
|
74
|
+
check('a failed seal is not verified', verifyProofCertificate(fake) === false)
|
|
75
|
+
check('a held seal is verified', verifyProofCertificate(generateGateCertificate('Hadamard')) === true)
|
|
76
|
+
|
|
77
|
+
// --------------------------------------------------------------- lean status
|
|
78
|
+
console.log('\nLean status is read from the script, not declared')
|
|
79
|
+
check("a script containing sorry reads as 'sorry'", readLeanStatus('theorem t : X := by\n sorry') === 'sorry')
|
|
80
|
+
check("an axiom reads as 'axiom'", readLeanStatus('axiom a : P') === 'axiom')
|
|
81
|
+
check("an empty script reads as 'absent'", readLeanStatus(' ') === 'absent')
|
|
82
|
+
check("a complete script reads as 'script'", readLeanStatus('theorem t : X := by decide') === 'script')
|
|
83
|
+
const sorryCount = Object.values(LEAN_PROOFS).filter((s) => readLeanStatus(s) === 'sorry').length
|
|
84
|
+
check('the sorry scripts are reported, not hidden', sorryCount > 0, sorryCount + ' found')
|
|
85
|
+
console.log(' ' + sorryCount + ' of ' + Object.keys(LEAN_PROOFS).length + ' Lean scripts end in sorry')
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------- hashing
|
|
88
|
+
console.log('\nHash covers content, not the name')
|
|
89
|
+
const h1 = computeProofHash('IsUnitary hadamard', 'theorem a := by decide')
|
|
90
|
+
const h2 = computeProofHash('IsUnitary hadamard', 'theorem a := by sorry')
|
|
91
|
+
const h3 = computeProofHash('IsUnitary pauliX', 'theorem a := by decide')
|
|
92
|
+
check('changing the proof script changes the hash', h1 !== h2)
|
|
93
|
+
check('changing the statement changes the hash', h1 !== h3)
|
|
94
|
+
check('the same content hashes the same', h1 === computeProofHash('IsUnitary hadamard', 'theorem a := by decide'))
|
|
95
|
+
|
|
96
|
+
// --------------------------------------------------------------- reporting
|
|
97
|
+
console.log('\nReporting does not overstate')
|
|
98
|
+
const report = verifyQuantumSystem()
|
|
99
|
+
check('lean_machine_checked is false', report.lean_machine_checked === false)
|
|
100
|
+
check('every theorem in the report is sealed', report.sealed_fraction === 1, String(report.sealed_fraction))
|
|
101
|
+
check('nothing is left unsealed', report.unsealed.length === 0, report.unsealed.join(', '))
|
|
102
|
+
console.log(' sealed ' + report.total_theorems + '/' + report.total_theorems)
|
|
103
|
+
|
|
104
|
+
const zenodo = exportProofsForZenodo() as { ready_for_publication: boolean; caveats: string[] }
|
|
105
|
+
// Still false, and correctly so: seals are computed instances, not Lean proofs.
|
|
106
|
+
check('not marked ready for publication (no Lean toolchain runs here)', zenodo.ready_for_publication === false)
|
|
107
|
+
check('caveats are attached', zenodo.caveats.length >= 3)
|
|
108
|
+
|
|
109
|
+
const transcript = generateProofTranscript([
|
|
110
|
+
generateGateCertificate('Hadamard'),
|
|
111
|
+
generateAlgorithmCertificate('Shor'),
|
|
112
|
+
generateECCertificate('Steane[7,1,3]'),
|
|
113
|
+
])
|
|
114
|
+
check('transcript confidence counts held seals', transcript.confidence === 1, String(transcript.confidence))
|
|
115
|
+
check('transcript lists no unsealed theorem', transcript.unsealed.length === 0)
|
|
116
|
+
// The confidence number must still be able to move, or it is decoration.
|
|
117
|
+
const withFailure = generateProofTranscript([
|
|
118
|
+
generateGateCertificate('Hadamard'),
|
|
119
|
+
{ ...generateGateCertificate('PauliX'), seal: 'failed' as const },
|
|
120
|
+
])
|
|
121
|
+
check('a failed seal drags confidence down', withFailure.confidence === 1 / 2, String(withFailure.confidence))
|
|
122
|
+
|
|
123
|
+
console.log('')
|
|
124
|
+
if (failures > 0) {
|
|
125
|
+
console.error('lean-bridge: ' + failures + ' check(s) failed')
|
|
126
|
+
process.exit(1)
|
|
112
127
|
}
|
|
113
|
-
|
|
114
|
-
function testProofHash(): void {
|
|
115
|
-
console.log('Test: Proof hash computation...')
|
|
116
|
-
|
|
117
|
-
const hash1 = computeProofHash('hadamard_unitary')
|
|
118
|
-
const hash2 = computeProofHash('hadamard_unitary')
|
|
119
|
-
const hash3 = computeProofHash('pauliX_unitary')
|
|
120
|
-
|
|
121
|
-
if (hash1 !== hash2) {
|
|
122
|
-
throw new Error('Hash not deterministic')
|
|
123
|
-
}
|
|
124
|
-
if (hash1 === hash3) {
|
|
125
|
-
throw new Error('Different theorems have same hash')
|
|
126
|
-
}
|
|
127
|
-
if (hash1.length !== 16) {
|
|
128
|
-
throw new Error(`Hash wrong length: ${hash1.length}`)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
console.log(` ✓ Hash deterministic: ${hash1}`)
|
|
132
|
-
console.log(` ✓ Hash collision-free`)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function testQuantumSystemVerification(): void {
|
|
136
|
-
console.log('Test: Complete quantum system verification...')
|
|
137
|
-
|
|
138
|
-
const report = verifyQuantumSystem()
|
|
139
|
-
|
|
140
|
-
if (report.total_theorems === 0) {
|
|
141
|
-
throw new Error('No theorems verified')
|
|
142
|
-
}
|
|
143
|
-
if (report.overall_confidence < 0.8) {
|
|
144
|
-
throw new Error(`Confidence too low: ${report.overall_confidence}`)
|
|
145
|
-
}
|
|
146
|
-
if (report.gates_verified.length === 0) {
|
|
147
|
-
throw new Error('No gates verified')
|
|
148
|
-
}
|
|
149
|
-
if (report.algorithms_verified.length === 0) {
|
|
150
|
-
throw new Error('No algorithms verified')
|
|
151
|
-
}
|
|
152
|
-
if (report.error_correction_verified.length === 0) {
|
|
153
|
-
throw new Error('No error correction verified')
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
console.log(` ✓ ${report.total_theorems} theorems formalized`)
|
|
157
|
-
console.log(` ✓ ${report.total_lines_of_proof} proof lines`)
|
|
158
|
-
console.log(` ✓ Gates: ${report.gates_verified.join(', ')}`)
|
|
159
|
-
console.log(` ✓ Algorithms: ${report.algorithms_verified.join(', ')}`)
|
|
160
|
-
console.log(` ✓ Error correction: ${report.error_correction_verified.join(', ')}`)
|
|
161
|
-
console.log(` ✓ Overall confidence: ${(report.overall_confidence * 100).toFixed(1)}%`)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function testProofTranscript(): void {
|
|
165
|
-
console.log('Test: Proof transcript generation...')
|
|
166
|
-
|
|
167
|
-
const certs = [
|
|
168
|
-
generateGateCertificate('Hadamard'),
|
|
169
|
-
generateAlgorithmCertificate('Grover'),
|
|
170
|
-
]
|
|
171
|
-
|
|
172
|
-
const transcript = generateProofTranscript(certs)
|
|
173
|
-
|
|
174
|
-
if (!transcript.title) {
|
|
175
|
-
throw new Error('Transcript missing title')
|
|
176
|
-
}
|
|
177
|
-
if (transcript.theorems.length === 0) {
|
|
178
|
-
throw new Error('Transcript missing theorems')
|
|
179
|
-
}
|
|
180
|
-
if (transcript.verified_count === 0) {
|
|
181
|
-
throw new Error('Transcript has no verified theorems')
|
|
182
|
-
}
|
|
183
|
-
if (transcript.confidence < 0.5) {
|
|
184
|
-
throw new Error(`Transcript confidence too low: ${transcript.confidence}`)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
console.log(` ✓ Title: ${transcript.title}`)
|
|
188
|
-
console.log(` ✓ Theorems: ${transcript.theorems.join(', ')}`)
|
|
189
|
-
console.log(` ✓ Total proof lines: ${transcript.total_lines}`)
|
|
190
|
-
console.log(` ✓ Verified: ${transcript.verified_count}/${certs.length}`)
|
|
191
|
-
console.log(` ✓ Confidence: ${(transcript.confidence * 100).toFixed(1)}%`)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function testZenodoExport(): void {
|
|
195
|
-
console.log('Test: Zenodo publication export...')
|
|
196
|
-
|
|
197
|
-
const export_data = exportProofsForZenodo()
|
|
198
|
-
|
|
199
|
-
if (!export_data.system) {
|
|
200
|
-
throw new Error('Export missing system name')
|
|
201
|
-
}
|
|
202
|
-
if (!export_data.verification_framework) {
|
|
203
|
-
throw new Error('Export missing verification framework')
|
|
204
|
-
}
|
|
205
|
-
if (!export_data.formal_verification_report) {
|
|
206
|
-
throw new Error('Export missing verification report')
|
|
207
|
-
}
|
|
208
|
-
if (!export_data.ready_for_publication) {
|
|
209
|
-
throw new Error('Export not marked ready for publication')
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const report = export_data.formal_verification_report as any
|
|
213
|
-
if (report.overall_confidence < 0.8) {
|
|
214
|
-
throw new Error('System confidence below publication threshold')
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
console.log(` ✓ System: ${export_data.system}`)
|
|
218
|
-
console.log(` ✓ Framework: ${export_data.verification_framework}`)
|
|
219
|
-
console.log(` ✓ Confidence: ${(report.overall_confidence * 100).toFixed(1)}%`)
|
|
220
|
-
console.log(` ✓ Ready for Zenodo: ${export_data.ready_for_publication}`)
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// ============================================================================
|
|
224
|
-
// TEST RUNNER
|
|
225
|
-
// ============================================================================
|
|
226
|
-
|
|
227
|
-
async function runTests(): Promise<void> {
|
|
228
|
-
console.log('🧪 Lean Bridge Verification Tests\n')
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
testGateCertificates()
|
|
232
|
-
testAlgorithmCertificates()
|
|
233
|
-
testECCertificates()
|
|
234
|
-
testProofChain()
|
|
235
|
-
testProofHash()
|
|
236
|
-
testQuantumSystemVerification()
|
|
237
|
-
testProofTranscript()
|
|
238
|
-
testZenodoExport()
|
|
239
|
-
|
|
240
|
-
console.log('\n✅ All Lean bridge verification tests passed!')
|
|
241
|
-
} catch (error) {
|
|
242
|
-
console.error(`\n❌ Test failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
243
|
-
process.exit(1)
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
runTests()
|
|
128
|
+
console.log('lean-bridge ok - all seals hold, Lean scripts still not machine-checked')
|