PageSourceSearch

https://bloqqer.onrender.com/js/quantum/quantumState.js

js bloqqer.onrender.com collected 2026-10-03 11:39:19 UTC 20,550 bytes, 582 lines download raw bytes

1/**
2 * Represents and manipulates a quantum state on the Bloch sphere
3 * Upgraded to seamlessly support multi-qubit (N-qubit) simulations,
4 * exact density matrix math, and partial trace computations for entanglement.
5 * Fully optimized for performance and absolute mathematical integrity.
6 *
7 * Set QuantumState.DEBUG = true to enable verbose physical integrity logging.
8 */
9class QuantumState {
10    constructor(numQubits = 1) {
11        this.qubits = numQubits;
12        this.resetToZero();
13    }
14
15    /** @type {boolean} Enable verbose physical integrity console logging */
16    static DEBUG = false;
17
18    /**
19     * Resets the quantum state to |00...0⟩
20     */
21    resetToZero() {
22        const size = Math.pow(2, this.qubits);
23        this.stateVector = Array(size).fill(0).map((_, i) => 
24            i === 0 ? math.complex(1, 0) : math.complex(0, 0)
25        );
26        this.normalize();
27        this.validateStatePhysicalIntegrity();
28        this.updateAlphaBeta();
29        this.updateBlochCoordinates();
30    }
31
32    /**
33     * Enforces strict physical normalization <ψ|ψ> = 1 to prevent numerical precision drift
34     */
35    normalize() {
36        let normSq = 0;
37        const size = this.stateVector.length;
38        for (let i = 0; i < size; i++) {
39            const c = this.stateVector[i];
40            normSq += math.re(c) * math.re(c) + math.im(c) * math.im(c);
41        }
42        
43        const norm = Math.sqrt(normSq);
44        if (norm > 0) {
45            for (let i = 0; i < size; i++) {
46                this.stateVector[i] = math.divide(this.stateVector[i], norm);
47            }
48        }
49    }
50
51    /**
52     * Diagnostic self-validation suite.
53     * Asserts that state vector normalization <ψ|ψ> = 1 and the trace of each 
54     * reduced density matrix Tr(ρ_k) = 1 are completely accurate to within 1e-6.
55     * @returns {Object} Physical integrity report
56     */
57    validateStatePhysicalIntegrity() {
58        let normSq = 0;
59        const size = this.stateVector.length;
60        for (let i = 0; i < size; i++) {
61            const c = this.stateVector[i];
62            normSq += math.re(c) * math.re(c) + math.im(c) * math.im(c);
63        }
64        
65        const normIsValid = Math.abs(normSq - 1.0) < 1e-6;
66        const qubitIntegrity = [];
67        let rdmTraceIsValid = true;
68        
69        for (let q = 0; q < this.qubits; q++) {
70            const rho = this.getReducedDensityMatrix(q);
71            const trace = math.re(math.add(rho[0][0], rho[1][1]));
72            const traceIsValid = Math.abs(trace - 1.0) < 1e-6;
73            if (!traceIsValid) rdmTraceIsValid = false;
74            
75            qubitIntegrity.push({
76                qubit: q,
77                trace: trace,
78                isValid: traceIsValid
79            });
80        }
81        
82        const integrity = {
83            norm: normSq,
84            normIsValid,
85            rdmTraceIsValid,
86            qubitIntegrity,
87            overallValid: normIsValid && rdmTraceIsValid
88        };
89
90        // Only log in DEBUG mode to prevent console flooding in production
91        if (QuantumState.DEBUG) {
92            console.log("🧬 Quantum State Physical Integrity Check:",
93                `\n- Norm <ψ|ψ>: ${normSq.toFixed(8)} [${normIsValid ? "✓ VALID" : "❌ INVALID"}]`,
94                `\n- RDM Traces Valid: ${rdmTraceIsValid ? "✓ VALID" : "❌ INVALID"}`
95            );
96        }
97
98        // Auto-correct if drift detected
99        if (!normIsValid) {
100            if (QuantumState.DEBUG) {
101                console.warn("⚠️ Rounding drift detected. Applying normalization corrections.");
102            }
103            this.normalize();
104        }
105
106        return integrity;
107    }
108
109    /**
110     * Updates alpha and beta for 1-qubit backward compatibility
111     */
112    updateAlphaBeta() {
113        this.alpha = this.stateVector[0] || math.complex(1, 0);
114        this.beta = this.stateVector[1] || math.complex(0, 0);
115    }
116
117    /**
118     * Sets the quantum state from Ket notation string (primarily for 1-qubit)
119     * @param {string} ketString - Input in format like "0.7|0⟩ + 0.7i|1⟩"
120     * @returns {boolean} True if successfully parsed, false otherwise
121     */
122    setFromKetString(ketString) {
123        try {
124            // Security: Limit input string length to 100 characters to prevent ReDoS / CPU exhaustion
125            if (!ketString || typeof ketString !== 'string' || ketString.length > 100) {
126                throw new Error("Invalid input: expression is empty or exceeds length limit of 100 characters");
127            }
128
129            const cleaned = ketString.replace(/\s+/g, '');
130            // Pattern to match coefficients and ket symbols, like:
131            // (0.5+0.5i)|0> or -0.707i|1> or |0⟩
132            const regex = /([^|01>⟩]*)\s*(?:\|(0|1)⟩|\|(0|1)>)/g;
133            let match;
134            let alpha = math.complex(0, 0);
135            let beta = math.complex(0, 0);
136            let matchFound = false;
137
138            while ((match = regex.exec(cleaned)) !== null) {
139                matchFound = true;
140                let coefStr = match[1] || '';
141                const ketVal = match[2] || match[3]; // '0' or '1'
142                
143                // Strip outer parentheses if present
144                coefStr = coefStr.replace(/^\s*\(\s*/, '').replace(/\s*\)\s*$/, '');
145                if (coefStr.endsWith('*')) coefStr = coefStr.slice(0, -1);
146                
147                let coef = math.complex(1, 0);
148                if (coefStr === '' || coefStr === '+') {
149                    coef = math.complex(1, 0);
150                } else if (coefStr === '-') {
151                    coef = math.complex(-1, 0);
152                } else {
153                    // Security: Restrict allowed characters and mathematical terms
154                    if (!/^[0-9.+\-*/()ijepncsotqrx\s]*$/i.test(coefStr)) {
155                        throw new Error("Forbidden characters in coefficient expression");
156                    }
157                    const words = coefStr.match(/[a-z]+/gi) || [];
158                    const allowedWords = new Set(['i', 'j', 'e', 'pi', 'sin', 'cos', 'tan', 'sqrt', 'exp']);
159                    for (const word of words) {
160                        if (!allowedWords.has(word.toLowerCase())) {
161                            throw new Error("Forbidden function or term in coefficient expression: " + word);
162                        }
163                    }
164                    // Evaluate cleanly using Math.js
165                    coef = math.evaluate(coefStr);
166                }
167                
168                if (ketVal === '0') {
169                    alpha = math.add(alpha, coef);
170                } else if (ketVal === '1') {
171                    beta = math.add(beta, coef);
172                }
173            }
174            
175            if (!matchFound) {
176                throw new Error("No valid state terms found");
177            }
178            
179            const norm = Math.sqrt(
180                math.add(
181                    math.pow(math.abs(alpha), 2),
182                    math.pow(math.abs(beta), 2)
183                )
184            );
185            
186            if (norm === 0) {
187                throw new Error("State cannot be zero norm");
188            }
189            
190            this.qubits = 1;
191            this.stateVector = [
192                math.divide(alpha, norm),
193                math.divide(beta, norm)
194            ];
195            
196            this.normalize();
197            this.validateStatePhysicalIntegrity();
198            this.updateAlphaBeta();
199            this.updateBlochCoordinates();
200            return true;
201        } catch (error) {
202            console.error("Error parsing ket notation:", error);
203            return false;
204        }
205    }
206    
207    /**
208     * Calculate reduced density matrix for qubit k by tracing out all other qubits.
209     * @param {number} k - Index of the qubit to inspect (0 to N-1)
210     * @returns {Array<Array<math.Complex>>} A 2x2 complex density matrix
211     */
212    getReducedDensityMatrix(k) {
213        const N = this.qubits;
214        const size = Math.pow(2, N);
215        const shift = N - 1 - k;
216        const mask = 1 << shift;
217        
218        let r00 = math.complex(0, 0);
219        let r11 = math.complex(0, 0);
220        let r01 = math.complex(0, 0);
221        let r10 = math.complex(0, 0);
222        
223        for (let i = 0; i < size; i++) {
224            const bitK = (i & mask) >> shift;
225            
226            if (bitK === 0) {
227                const i0 = i;
228                const i1 = i | mask;
229                
230                const psi0 = this.stateVector[i0];
231                const psi1 = this.stateVector[i1];
232                
233                r00 = math.add(r00, math.multiply(psi0, math.conj(psi0)));
234                r11 = math.add(r11, math.multiply(psi1, math.conj(psi1)));
235                r01 = math.add(r01, math.multiply(psi0, math.conj(psi1)));
236                r10 = math.add(r10, math.multiply(psi1, math.conj(psi0)));
237            }
238        }
239        
240        return [[r00, r01], [r10, r11]];
241    }
242
243    /**
244     * Calculate Bloch sphere coordinates from quantum state for all qubits
245     */
246    updateBlochCoordinates() {
247        this.qubitCoordinates = [];
248        
249        for (let q = 0; q < this.qubits; q++) {
250            const rho = this.getReducedDensityMatrix(q);
251            
252            const x = 2 * math.re(rho[0][1]);
253            const y = 2 * math.im(rho[0][1]);
254            const z = math.re(math.subtract(rho[0][0], rho[1][1]));
255            
256            this.qubitCoordinates.push({ x, y, z });
257        }
258        
259        this.blochCoordinates = this.qubitCoordinates[0] || { x: 0, y: 0, z: 1 };
260    }
261    
262    /**
263     * Get a nice formatted string representation of the current state
264     */
265    getKetString() {
266        const formatComplex = (c) => {
267            const re = math.round(math.re(c), 3);
268            const im = math.round(math.im(c), 3);
269            
270            if (im === 0) {
271                if (re === 0) return '0';
272                return re.toString();
273            }
274            if (re === 0) {
275                if (im === 1) return 'i';
276                if (im === -1) return '-i';
277                return `${im}i`;
278            }
279            return `${re}${im >= 0 ? '+' : ''}${im === 1 ? 'i' : im === -1 ? '-i' : im + 'i'}`;
280        };
281        
282        let result = '';
283        const size = Math.pow(2, this.qubits);
284        for (let i = 0; i < size; i++) {
285            const coef = this.stateVector[i];
286            if (math.abs(coef) > 0.001) {
287                if (result.length > 0) result += ' + ';
288                const coefStr = formatComplex(coef);
289                const binaryStr = i.toString(2).padStart(this.qubits, '0');
290                
291                if (coefStr === '1') {
292                    result += `|${binaryStr}⟩`;
293                } else if (coefStr === '-1') {
294                    result += `-|${binaryStr}⟩`;
295                } else {
296                    const needsParens = coefStr.includes('+') || 
297                                        (coefStr.includes('-') && coefStr.indexOf('-') !== 0) || 
298                                        coefStr.includes('i');
299                    if (needsParens) {
300                        result += `(${coefStr})|${binaryStr}⟩`;
301                    } else {
302                        result += `${coefStr}|${binaryStr}⟩`;
303                    }
304                }
305            }
306        }
307        
308        return result || '0';
309    }
310    
311    /**
312     * Apply a single qubit gate on qubit index k using its 2x2 matrix
313     * Pair-wise shift logic reduces complexity to O(2^N) from Kronecker matrix multiplication's O(2^2N).
314     */
315    applySingleQubitGate(k, U) {
316        const N = this.qubits;
317        const size = Math.pow(2, N);
318        const shift = N - 1 - k;
319        const mask = 1 << shift;
320        const nextState = new Array(size);
321        
322        for (let i = 0; i < size; i++) {
323            const bitK = (i & mask) >> shift;
324            if (bitK === 0) {
325                const i0 = i;
326                const i1 = i | mask;
327                
328                const psi0 = this.stateVector[i0];
329                const psi1 = this.stateVector[i1];
330                
331                nextState[i0] = math.add(
332                    math.multiply(U[0][0], psi0),
333                    math.multiply(U[0][1], psi1)
334                );
335                nextState[i1] = math.add(
336                    math.multiply(U[1][0], psi0),
337                    math.multiply(U[1][1], psi1)
338                );
339            }
340        }
341        
342        this.stateVector = nextState;
343        this.normalize();
344        this.validateStatePhysicalIntegrity();
345        this.updateAlphaBeta();
346        this.updateBlochCoordinates();
347    }
348
349    /**
350     * Apply a CNOT gate (controlled-NOT)
351     */
352    applyCNOT(c, t) {
353        const N = this.qubits;
354        const size = Math.pow(2, N);
355        const maskC = 1 << (N - 1 - c);
356        const maskT = 1 << (N - 1 - t);
357        const nextState = [...this.stateVector];
358        
359        for (let i = 0; i < size; i++) {
360            if ((i & maskC) !== 0) {
361                if ((i & maskT) === 0) {
362                    const i0 = i;
363                    const i1 = i | maskT;
364                    
365                    const temp = nextState[i0];
366                    nextState[i0] = nextState[i1];
367                    nextState[i1] = temp;
368                }
369            }
370        }
371        
372        this.stateVector = nextState;
373        this.normalize();
374        this.validateStatePhysicalIntegrity();
375        this.updateAlphaBeta();
376        this.updateBlochCoordinates();
377    }
378
379    /**
380     * Apply a CZ gate (controlled-Z phase flip)
381     */
382    applyCZ(c, t) {
383        const N = this.qubits;
384        const size = Math.pow(2, N);
385        const maskC = 1 << (N - 1 - c);
386        const maskT = 1 << (N - 1 - t);
387        
388        for (let i = 0; i < size; i++) {
389            if ((i & maskC) !== 0 && (i & maskT) !== 0) {
390                this.stateVector[i] = math.multiply(-1, this.stateVector[i]);
391            }
392        }
393        
394        this.normalize();
395        this.validateStatePhysicalIntegrity();
396        this.updateAlphaBeta();
397        this.updateBlochCoordinates();
398    }
399
400    /**
401     * Apply a SWAP gate between qubit 1 and qubit 2
402     */
403    applySWAP(q1, q2) {
404        const N = this.qubits;
405        const size = Math.pow(2, N);
406        const mask1 = 1 << (N - 1 - q1);
407        const mask2 = 1 << (N - 1 - q2);
408        const nextState = [...this.stateVector];
409        
410        for (let i = 0; i < size; i++) {
411            const bit1 = (i & mask1) !== 0 ? 1 : 0;
412            const bit2 = (i & mask2) !== 0 ? 1 : 0;
413            
414            if (bit1 !== bit2) {
415                if (bit1 === 0) {
416                    const i0 = i;
417                    const i1 = (i | mask1) & ~mask2;
418                    
419                    const temp = nextState[i0];
420                    nextState[i0] = nextState[i1];
421                    nextState[i1] = temp;
422                }
423            }
424        }
425        
426        this.stateVector = nextState;
427        this.normalize();
428        this.validateStatePhysicalIntegrity();
429        this.updateAlphaBeta();
430        this.updateBlochCoordinates();
431    }
432
433    /**
434     * Apply a Toffoli gate (controlled-controlled-NOT)
435     */
436    applyToffoli(c1, c2, t) {
437        const N = this.qubits;
438        const size = Math.pow(2, N);
439        const maskC1 = 1 << (N - 1 - c1);
440        const maskC2 = 1 << (N - 1 - c2);
441        const maskT = 1 << (N - 1 - t);
442        const nextState = [...this.stateVector];
443        
444        for (let i = 0; i < size; i++) {
445            if ((i & maskC1) !== 0 && (i & maskC2) !== 0) {
446                if ((i & maskT) === 0) {
447                    const i0 = i;
448                    const i1 = i | maskT;
449                    
450                    const temp = nextState[i0];
451                    nextState[i0] = nextState[i1];
452                    nextState[i1] = temp;
453                }
454            }
455        }
456        
457        this.stateVector = nextState;
458        this.normalize();
459        this.validateStatePhysicalIntegrity();
460        this.updateAlphaBeta();
461        this.updateBlochCoordinates();
462    }
463    
464    /**
465     * Apply a quantum gate to the current state targeting a specific qubit
466     * @param {string} gate - The gate to apply ('h', 'x', 'y', 'z', 's', 't', 'sdg', 'tdg', etc.)
467     * @param {number} targetQubit - Index of the target qubit (0 or 1)
468     */
469    applyGate(gate, targetQubit = 0) {
470        const H = [
471            [math.divide(1, math.sqrt(2)), math.divide(1, math.sqrt(2))],
472            [math.divide(1, math.sqrt(2)), math.divide(-1, math.sqrt(2))]
473        ];
474        const X = [[0, 1], [1, 0]];
475        const Y = [[0, math.complex(0, -1)], [math.complex(0, 1), 0]];
476        const Z = [[1, 0], [0, -1]];
477        const S = [[1, 0], [0, math.complex(0, 1)]];
478        const T = [[1, 0], [0, math.exp(math.complex(0, Math.PI / 4))]];
479        const I = [[1, 0], [0, 1]];
480        const Sdg = [[1, 0], [0, math.complex(0, -1)]];
481        const Tdg = [[1, 0], [0, math.exp(math.complex(0, -Math.PI / 4))]];
482        
483        switch (gate.toLowerCase()) {
484            case 'i':
485                this.applySingleQubitGate(targetQubit, I);
486                break;
487            case 'h':
488                this.applySingleQubitGate(targetQubit, H);
489                break;
490            case 'x':
491                this.applySingleQubitGate(targetQubit, X);
492                break;
493            case 'y':
494                this.applySingleQubitGate(targetQubit, Y);
495                break;
496            case 'z':
497                this.applySingleQubitGate(targetQubit, Z);
498                break;
499            case 's':
500                this.applySingleQubitGate(targetQubit, S);
501                break;
502            case 'sdg':
503            case 's†':
504                this.applySingleQubitGate(targetQubit, Sdg);
505                break;
506            case 't':
507                this.applySingleQubitGate(targetQubit, T);
508                break;
509            case 'tdg':
510            case 't†':
511                this.applySingleQubitGate(targetQubit, Tdg);
512                break;
513            default:
514                console.error(`Unknown gate: ${gate}`);
515        }
516    }
517    
518    /**
519     * Apply a custom rotation to a specific qubit in the quantum state
520     * @param {number} theta - Theta angle in radians
521     * @param {number} phi - Phi angle in radians
522     * @param {number} targetQubit - Index of the target qubit
523     */
524    applyRotation(theta, phi, targetQubit = 0) {
525        const e_neg_i_phi = math.exp(math.complex(0, -phi));
526        const e_i_phi = math.exp(math.complex(0, phi));
527        const U = [
528            [math.complex(Math.cos(theta / 2), 0), math.multiply(-Math.sin(theta / 2), e_neg_i_phi)],
529            [math.multiply(Math.sin(theta / 2), e_i_phi), math.complex(Math.cos(theta / 2), 0)]
530        ];
531        this.applySingleQubitGate(targetQubit, U);
532    }
533
534    /**
535     * Apply a parameterized phase shift gate (Rphi) to a specific qubit
536     * @param {number} phi - Phase shift angle in radians
537     * @param {number} targetQubit - Index of the target qubit
538     */
539    applyPhaseShift(phi, targetQubit = 0) {
540        const U = [
541            [math.complex(1, 0), math.complex(0, 0)],
542            [math.complex(0, 0), math.exp(math.complex(0, phi))]
543        ];
544        this.applySingleQubitGate(targetQubit, U);
545    }
546
547
548    /**
549     * Collapse a specific qubit based on Born's rule probabilities.
550     * @param {number} k - Index of the qubit to collapse
551     * @param {number|null} forcedOutcome - If specified, forces the collapse outcome (0 or 1)
552     * @returns {number} The outcome of the collapse (0 or 1)
553     */
554    collapseQubit(k, forcedOutcome = null) {
555        const rho = this.getReducedDensityMatrix(k);
556        const p0 = Math.max(0, Math.min(1.0, math.re(rho[0][0])));
557        
558        let outcome = forcedOutcome;
559        if (outcome === null) {
560            outcome = Math.random() < p0 ? 0 : 1;
561        }
562        
563        const N = this.qubits;
564        const size = Math.pow(2, N);
565        const shift = N - 1 - k;
566        const mask = 1 << shift;
567        
568        for (let i = 0; i < size; i++) {
569            const bitK = (i & mask) >> shift;
570            if (bitK !== outcome) {
571                this.stateVector[i] = math.complex(0, 0);
572            }
573        }
574        
575        this.normalize();
576        this.validateStatePhysicalIntegrity();
577        this.updateAlphaBeta();
578        this.updateBlochCoordinates();
579        
580        return outcome;
581    }
582}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.