PageSourceSearch

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

js bloqqer.onrender.com collected 2026-10-03 11:39:20 UTC 18,685 bytes, 540 lines download raw bytes

1/**
2 * Circuit Reflector - Animates gate effects on the Bloch sphere step-by-step
3 */
4class CircuitReflector {
5    constructor(composer) {
6        this.composer = composer;
7        this.quantumState = composer.quantumState;
8        this.isReflecting = false;
9        this.currentStep = 0;
10        this.stateHistory = [];
11        this.originalState = null;
12        this.animationSpeed = 1000; // ms per step
13        this.animationTimer = null;
14    }
15    
16    /**
17     * Start circuit reflection
18     */
19    startReflection() {
20        if (this.isReflecting) return;
21        
22        this.isReflecting = true;
23        this.currentStep = 0;
24        
25        // Save original state
26        this.originalState = {
27            qubits: this.quantumState.qubits,
28            stateVector: this.quantumState.stateVector.map(c => math.complex(math.re(c), math.im(c))),
29            qubitCoordinates: JSON.parse(JSON.stringify(this.quantumState.qubitCoordinates)),
30            alpha: this.quantumState.alpha,
31            beta: this.quantumState.beta,
32            blochCoordinates: { ...this.quantumState.blochCoordinates }
33        };
34        
35        // Initialize state history
36        this.stateHistory = [{
37            step: 0,
38            state: { ...this.originalState },
39            description: 'Initial state |00>'
40        }];
41        
42        // Enable path tracing with bright color on both spheres
43        this.composer.blochSphere0.setPathEnabled(true);
44        this.composer.blochSphere1.setPathEnabled(true);
45        this.setReflectionPathColor();
46        
47        // Ensure path tracing is visible
48        this.composer.blochSphere0.clearPath();
49        this.composer.blochSphere1.clearPath();
50        
51        // Apply first step if circuit has gates
52        const circuit = this.composer.getCircuit();
53        if (circuit.length > 0) {
54            this.applyStep(0);
55        }
56    }
57    
58    /**
59     * Apply a specific step
60     */
61    applyStep(stepIndex) {
62        const circuit = this.composer.getCircuit();
63        if (stepIndex >= circuit.length) return;
64        
65        const gate = circuit[stepIndex];
66        
67        // Apply gate to quantum state
68        this.applyGateToState(gate);
69        
70        // Determine animation flags for each qubit
71        const targetQubit = gate.qubit;
72        let animateQ0 = false;
73        let animateQ1 = false;
74        
75        if (gate.type === 'CNOT' || gate.type === 'CZ') {
76            animateQ0 = (gate.control === 0 || gate.target === 0);
77            animateQ1 = (gate.control === 1 || gate.target === 1);
78        } else if (gate.type === 'SWAP') {
79            animateQ0 = (gate.qubit1 === 0 || gate.qubit2 === 0);
80            animateQ1 = (gate.qubit1 === 1 || gate.qubit2 === 1);
81        } else {
82            animateQ0 = (targetQubit === 0);
83            animateQ1 = (targetQubit === 1);
84        }
85        
86        // Update both spheres: target animates (true), others update instantly (false)
87        const coords0 = this.quantumState.qubitCoordinates[0] || { x: 0, y: 0, z: 1 };
88        const coords1 = this.quantumState.qubitCoordinates[1] || { x: 0, y: 0, z: 1 };
89        
90        this.composer.blochSphere0.updateStateVector(coords0, animateQ0, gate);
91        this.composer.blochSphere1.updateStateVector(coords1, animateQ1, gate);
92        
93        // Set distinct color for this step
94        const stepColor = this.getStateColor(stepIndex);
95        this.composer.blochSphere0.setPathColor(stepColor);
96        this.composer.blochSphere1.setPathColor(stepColor);
97        
98        // Add to state history
99        this.stateHistory.push({
100            step: stepIndex + 1,
101            state: {
102                qubits: this.quantumState.qubits,
103                stateVector: this.quantumState.stateVector.map(c => math.complex(math.re(c), math.im(c))),
104                qubitCoordinates: JSON.parse(JSON.stringify(this.quantumState.qubitCoordinates)),
105                alpha: this.quantumState.alpha,
106                beta: this.quantumState.beta,
107                blochCoordinates: { ...this.quantumState.blochCoordinates }
108            },
109            description: this.getGateDescription(gate)
110        });
111        
112        // Highlight current gate in circuit
113        this.highlightGate(stepIndex);
114        
115        // Update step counter
116        this.currentStep = stepIndex + 1;
117    }
118    
119    /**
120     * Go to a specific step (without applying subsequent gates)
121     */
122    goToStep(stepIndex) {
123        if (stepIndex < 0 || stepIndex > this.stateHistory.length - 1) return;
124        
125        // Restore state from history
126        const historyEntry = this.stateHistory[stepIndex];
127        this.quantumState.qubits = historyEntry.state.qubits;
128        if (historyEntry.state.stateVector) {
129            this.quantumState.stateVector = historyEntry.state.stateVector.map(c => math.complex(math.re(c), math.im(c)));
130        }
131        if (historyEntry.state.qubitCoordinates) {
132            this.quantumState.qubitCoordinates = JSON.parse(JSON.stringify(historyEntry.state.qubitCoordinates));
133        }
134        this.quantumState.alpha = historyEntry.state.alpha;
135        this.quantumState.beta = historyEntry.state.beta;
136        this.quantumState.blochCoordinates = { ...historyEntry.state.blochCoordinates };
137        
138        // Update Bloch spheres with proper arrow movement
139        const coords0 = this.quantumState.qubitCoordinates[0] || { x: 0, y: 0, z: 1 };
140        const coords1 = this.quantumState.qubitCoordinates[1] || { x: 0, y: 0, z: 1 };
141        
142        this.composer.blochSphere0.updateStateVector(coords0, true);
143        this.composer.blochSphere1.updateStateVector(coords1, true);
144        
145        // Update step counter
146        this.currentStep = stepIndex;
147        
148        // Highlight current gate
149        this.highlightGate(stepIndex - 1);
150    }
151    
152    /**
153     * Apply gate to quantum state
154     */
155    applyGateToState(gate) {
156        const { type, qubit, params } = gate;
157        
158        // Define matrices for standard single-qubit gates
159        const H = [
160            [math.divide(1, math.sqrt(2)), math.divide(1, math.sqrt(2))],
161            [math.divide(1, math.sqrt(2)), math.divide(-1, math.sqrt(2))]
162        ];
163        const X = [[0, 1], [1, 0]];
164        const Y = [[0, math.complex(0, -1)], [math.complex(0, 1), 0]];
165        const Z = [[1, 0], [0, -1]];
166        const S = [[1, 0], [0, math.complex(0, 1)]];
167        const T = [[1, 0], [0, math.exp(math.complex(0, Math.PI / 4))]];
168        
169        switch (type) {
170            case 'H':
171                this.quantumState.applySingleQubitGate(qubit, H);
172                break;
173            case 'X':
174                this.quantumState.applySingleQubitGate(qubit, X);
175                break;
176            case 'Y':
177                this.quantumState.applySingleQubitGate(qubit, Y);
178                break;
179            case 'Z':
180                this.quantumState.applySingleQubitGate(qubit, Z);
181                break;
182            case 'S':
183                this.quantumState.applySingleQubitGate(qubit, S);
184                break;
185            case 'T':
186                this.quantumState.applySingleQubitGate(qubit, T);
187                break;
188            case 'RX': {
189                const rxAngle = (params.angle * Math.PI) / 180;
190                const rx = [
191                    [Math.cos(rxAngle / 2), math.complex(0, -Math.sin(rxA
191ngle / 2))],
192                    [math.complex(0, -Math.sin(rxAngle / 2)), Math.cos(rxAngle / 2)]
193                ];
194                this.quantumState.applySingleQubitGate(qubit, rx);
195                break;
196            }
197            case 'RY': {
198                const ryAngle = (params.angle * Math.PI) / 180;
199                const ry = [
200                    [Math.cos(ryAngle / 2), -Math.sin(ryAngle / 2)],
201                    [Math.sin(ryAngle / 2), Math.cos(ryAngle / 2)]
202                ];
203                this.quantumState.applySingleQubitGate(qubit, ry);
204                break;
205            }
206            case 'RZ': {
207                const rzAngle = (params.angle * Math.PI) / 180;
208                const rz = [
209                    [math.exp(math.complex(0, -rzAngle / 2)), 0],
210                    [0, math.exp(math.complex(0, rzAngle / 2))]
211                ];
212                this.quantumState.applySingleQubitGate(qubit, rz);
213                break;
214            }
215            case 'CNOT': {
216                const control = gate.control !== undefined ? gate.control : qubit;
217                const target = gate.target !== undefined ? gate.target : (qubit + 1) % this.quantumState.qubits;
218                this.quantumState.applyCNOT(control, target);
219                break;
220            }
221            case 'CZ': {
222                const control = gate.control !== undefined ? gate.control : qubit;
223                const target = gate.target !== undefined ? gate.target : (qubit + 1) % this.quantumState.qubits;
224                this.quantumState.applyCZ(control, target);
225                break;
226            }
227            case 'SWAP': {
228                const qubit1 = gate.qubit1 !== undefined ? gate.qubit1 : qubit;
229                const qubit2 = gate.qubit2 !== undefined ? gate.qubit2 : (qubit + 1) % this.quantumState.qubits;
230                this.quantumState.applySWAP(qubit1, qubit2);
231                break;
232            }
233            case 'TOFFOLI': {
234                const control1 = gate.control1 !== undefined ? gate.control1 : qubit;
235                const control2 = gate.control2 !== undefined ? gate.control2 : (qubit + 1) % this.quantumState.qubits;
236                const target = gate.target !== undefined ? gate.target : (qubit + 2) % this.quantumState.qubits;
237                this.quantumState.applyToffoli(control1, control2, target);
238                break;
239            }
240            case 'M':
241                // Measurement is a no-op during animation reflection
242                break;
243            default:
244                console.warn(`Unknown gate type: ${type}`);
245        }
246        
247        // Update Bloch coordinates
248        this.quantumState.updateBlochCoordinates();
249    }
250    
251    /**
252     * Get description for a gate
253     */
254    getGateDescription(gate) {
255        const { type, qubit, params } = gate;
256        
257        let description = `${type} on Q${qubit}`;
258        
259        if (params.angle !== undefined) {
260            description += ` (${params.angle}°)`;
261        }
262        
263        if (gate.type === 'CNOT') {
264            description = `CNOT: Q${gate.control} → Q${gate.target}`;
265        } else if (gate.type === 'SWAP') {
266            description = `SWAP: Q${gate.qubit1} ↔ Q${gate.qubit2}`;
267        } else if (gate.type === 'TOFFOLI') {
268            description = `Toffoli: Q${gate.control1},Q${gate.control2} → Q${gate.target}`;
269        }
270        
271        return description;
272    }
273    
274    /**
275     * Highlight current gate in circuit
276     */
277    highlightGate(stepIndex) {
278        // Remove previous highlights
279        document.querySelectorAll('.gate-element.highlighted').forEach(el => {
280            el.classList.remove('highlighted');
281        });
282        
283        // Add highlight to current gate
284        const circuit = this.composer.getCircuit();
285        if (stepIndex >= 0 && stepIndex < circuit.length) {
286            const gate = circuit[stepIndex];
287            const gateElement = document.querySelector(
288                `.gate-element[data-qubit="${gate.qubit}"][data-time="${gate.time}"]`
289            );
290            if (gateElement) {
291                gateElement.classList.add('highlighted');
292            }
293        }
294    }
295    
296    /**
297     * Set reflection path color
298     */
299    setReflectionPathColor() {
300        // Use signature vibrant green color for reflection traces
301        this.composer.blochSphere0.setPathColor('#34C759');
302        this.composer.blochSphere1.setPathColor('#34C759');
303    }
304    
305    /**
306     * Get color for qubit state based on step
307     */
308    getStateColor(stepIndex) {
309        const colors = [
310            '#34C759', // Vibrant Green (--accent-green)
311            '#FF9F0A', // Vibrant Orange (--accent-orange)
312            '#64D2FF', // Sleek Sky Blue
313            '#BF5AF2', // Premium Violet
314            '#FF6482', // Elegant Rose
315            '#E4E4E7', // Crisp Zinc White (--accent-zinc)
316            '#FFD60A', // Warm Yellow
317            '#30D158', // Minty Green
318            '#FF453A', // Modern Soft Red
319            '#98989D'  // Medium Zinc Grey
320        ];
321        return colors[stepIndex % colors.length];
322    }
323    
324    /**
325     * Reset to original state
326     */
327    reset() {
328        if (!this.originalState) return;
329        
330        // Restore original state
331        this.quantumState.qubits = this.originalState.qubits;
332        if (this.originalState.stateVector) {
333            this.quantumState.stateVector = this.originalState.stateVector.map(c => math.complex(math.re(c), math.im(c)));
334        }
335        if (this.originalState.qubitCoordinates) {
336            this.quantumState.qubitCoordinates = JSON.parse(JSON.stringify(this.originalState.qubitCoordinates));
337        }
338        this.quantumState.alpha = this.originalState.alpha;
339        this.quantumState.beta = this.originalState.beta;
340        this.quantumState.blochCoordinates = { ...this.originalState.blochCoordinates };
341        
342        // Update Bloch sphere with proper arrow movement
343        const coords0 = this.quantumState.qubitCoordinates[0] || { x: 0, y: 0, z: 1 };
344        const coords1 = this.quantumState.qubitCoordinates[1] || { x: 0, y: 0, z: 1 };
345        this.composer.blochSphere0.updateStateVector(coords0, true);
346        this.composer.blochSphere1.updateStateVector(coords1, true);
347        
348        // Clear state history
349        this.stateHistory = [{
350            step: 0,
351            state: { ...this.originalState },
352            description: 'Initial state |00>'
353        }];
354        
355        // Reset step counter
356        this.currentStep = 0;
357        
358        // Clear the visual path trail so the next reflection starts fresh
359        this.composer.blochSphere0.clearPath();
360        this.composer.blochSphere1.clearPath();
361        
362        // Remove highlights
363        document.querySelectorAll('.gate-element.highlighted').forEach(el => {
364            el.classList.remove('highlighted');
365        });
366    }
367    
368    /**
369     * Stop reflection
370     */
371    stop() {
372        this.isReflecting = false;
373        
374        // Clear animation timer
375        if (this.animationTimer) {
376            clearTimeout(this.animationTimer);
377            this.animationTimer = null;
378        }
379        
380        // Remove highlights
381        document.querySelectorAll('.gate-element.highlighted').forEach(el => {
382            el.classList.remove('highlighted');
383        });
384        
385        // Reset path color to brand orange
386        this.composer.blochSphere0.setPathColor('#FF9F0A');
387        this.composer.blochSphere1.setPathColor('#FF9F0A');
388    }
389    
390    /**
391     * Animate through all steps automatically
392     */
393    animateAllSteps() {
394        // If already reflecting, stop the current session before restarting
395        if (this.isReflecting) {
396            this.stop();
397        }
398        
399        this.startReflection();
400        this.animateNextStep();
401    }
402    
403    /**
404     * Animate to next step
405     */
406    animateNextStep() {
407        const circuit = this.composer.getCircuit();
408        
409        if (this.currentStep < circuit.length) {
410            this.applyStep(this.currentStep);
411            
412            // Schedule next step
413            this.animationTimer = setTimeout(() => {
414                this.animateNextStep();
415            }, this.animationSpeed);
416        } else {
417            // Animation complete
418            this.isReflecting = false;
419            this.animationTimer = null;
420        }
421    }
422    
423    /**
424     * Pause animation
425     */
426    pauseAnimation() {
427        if (this.animationTimer) {
428            clearTimeout(this.animationTimer);
429            this.animationTimer = null;
430        }
431    }
432    
433    /**
434     * Resume animation
435     */
436    resumeAnimation() {
437        if (this.isReflecting && !this.animationTimer) {
438            this.animateNextStep();
439        }
440    }
441    
442    /**
443     * Set animation speed
444     */
445    setAnimationSpeed(speedMs) {
446        this.animationSpeed = Math.max(100, Math.min(5000, speedMs));
447    }
448    
449    /**
450     * Get current state description
451     */
452    getCurrentStateDescription() {
453        if (this.stateHistory.length === 0) return 'No state';
454        
455        const currentEntry = this.stateHistory[this.currentStep];
456        return currentEntry ? currentEntry.description : 'Unknown state';
457    }
458    
459    /**
460     * Get state history
461     */
462    getStateHistory() {
463        return this.stateHistory;
464    }
465    
466    /**
467     * Get current step
468     */
469    getCurrentStep() {
470        return this.currentStep;
471    }
472    
473    /**
474     * Check if reflection is active
475     */
476    isActive() {
477        return this.isReflecting;
478    }
479    
480    /**
481     * Get circuit statistics for reflection
482     */
483    getReflectionStats() {
484        const circuit = this.composer.getCircuit();
485        const totalSteps = circuit.length;
486        const completedSteps = this.currentStep;
487        const progress = totalSteps > 0 ? (completedSteps / totalSteps) * 100 : 0;
488        
489        return {
490            totalSteps,
491            completedSteps,
492            progress,
493            isComplete: completedSteps >= totalSteps,
494            currentState: this.getCurrentStateDescription()
495        };
496    }
497    
498    /**
499     * Export reflection data
500     */
501    exportReflectionData() {
502        return {
503            circuit: this.composer.getCircuit(),
504            stateHistory: this.stateHistory,
505            originalState: this.originalState,
506            stats: this.getReflectionStats(),
507            exportDate: new Date().toISOString()
508        };
509    }
510    
511    /**
512     * Import reflection data
513     */
514    importReflectionData(data) {
515        try {
516            // Import circuit
517            if (data.circuit) {
518                this.composer.clearCircuit();
519                data.circuit.forEach(gate => {
520                    this.composer.addGate(gate.type, gate.qubit, gate.time, gate.params);
521                });
522            }
523            
524            // Import state history
525            if (data.stateHistory) {
526                this.stateHistory = data.stateHistory;
527            }
528            
529            // Import original state
530            if (data.originalState) {
531                this.originalState = data.originalState;
532            }
533            
534            return true;
535        } catch (error) {
536            console.error('Error importing reflection data:', error);
537            return false;
538        }
539    }
540} 

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.