// Quantum Visualization Components for Web UI // Provides interactive visualizations for quantum states and circuits // Create a single global namespace for all quantum visualization components window.QuantumViz = window.QuantumViz || {}; // Add BlochSphere to the namespace window.QuantumViz.BlochSphere = function(containerId, options = {}) { this.container = document.getElementById(containerId); this.width = this.container.clientWidth || 400; this.height = this.container.clientHeight || 400; this.options = Object.assign({ backgroundColor: '#121212', sphereColor: '#444444', vectorColor: '#ff3366', axisColor: '#ffffff', labelColor: '#ffffff' }, options); this.initScene(); this.initSphere(); this.initAxes(); this.initStateVector(); this.initLabels(); this.animate(); // Add event listener for window resize window.addEventListener('resize', this.onResize.bind(this)); }; window.QuantumViz.BlochSphere.prototype.initScene = function() { // Scene setup this.scene = new THREE.Scene(); this.scene.background = new THREE.Color(this.options.backgroundColor); // Camera setup this.camera = new THREE.PerspectiveCamera(75, this.width / this.height, 0.1, 1000); this.camera.position.z = 2.5; // Renderer setup this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.renderer.setSize(this.width, this.height); this.container.appendChild(this.renderer.domElement); // Controls for orbit/rotation this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement); this.controls.enableDamping = true; this.controls.dampingFactor = 0.25; }; window.QuantumViz.BlochSphere.prototype.initSphere = function() { // Create the Bloch sphere const geometry = new THREE.SphereGeometry(1, 32, 32); const material = new THREE.MeshBasicMaterial({ color: this.options.sphereColor, wireframe: true, transparent: true, opacity: 0.5 }); this.sphere = new THREE.Mesh(geometry, material); this.scene.add(this.sphere); // Add equator circle // Fix for newer versions of Three.js const equatorGeometry = new THREE.CircleGeometry(1, 64); const points = []; // Create points for the circle manually for (let i = 0; i < 64; i++) { const angle = (i / 64) * Math.PI * 2; const x = Math.cos(angle); const y = Math.sin(angle); points.push(new THREE.Vector3(x, y, 0)); } const equatorBufferGeometry = new THREE.BufferGeometry().setFromPoints(points); const equatorMaterial = new THREE.LineBasicMaterial({ color: this.options.sphereColor }); this.equator = new THREE.LineLoop(equatorBufferGeometry, equatorMaterial); this.equator.rotation.x = Math.PI/2; this.scene.add(this.equator); }; window.QuantumViz.BlochSphere.prototype.initAxes = function() { // Create axes const axes = new THREE.Group(); // Z-axis (|0⟩ to |1⟩) const zGeo = new THREE.BufferGeometry().setFromPoints([ new THREE.Vector3(0, 0, -1.2), new THREE.Vector3(0, 0, 1.2) ]); const zLine = new THREE.Line(zGeo, new THREE.LineBasicMaterial({ color: this.options.axisColor })); axes.add(zLine); // X-axis (|+⟩ to |-⟩) const xGeo = new THREE.BufferGeometry().setFromPoints([ new THREE.Vector3(-1.2, 0, 0), new THREE.Vector3(1.2, 0, 0) ]); const xLine = new THREE.Line(xGeo, new THREE.LineBasicMaterial({ color: this.options.axisColor })); axes.add(xLine); // Y-axis (|i+⟩ to |i-⟩) const yGeo = new THREE.BufferGeometry().setFromPoints([ new THREE.Vector3(0, -1.2, 0), new THREE.Vector3(0, 1.2, 0) ]); const yLine = new THREE.Line(yGeo, new THREE.LineBasicMaterial({ color: this.options.axisColor })); axes.add(yLine); this.scene.add(axes); this.axes = axes; }; window.QuantumViz.BlochSphere.prototype.initStateVector = function() { // Arrow representing the current state const dir = new THREE.Vector3(0, 0, 1); // Default to |0⟩ state const origin = new THREE.Vector3(0, 0, 0); const length = 1; const headLength = 0.1; const headWidth = 0.05; this.stateVector = new THREE.ArrowHelper( dir, origin, length, this.options.vectorColor, headLength, headWidth ); this.scene.add(this.stateVector); }; window.QuantumViz.BlochSphere.prototype.initLabels = function() { // Add state labels using sprites this.addLabel('|0⟩', 0, 0, 1.3); this.addLabel('|1⟩', 0, 0, -1.3); this.addLabel('|+⟩', 1.3, 0, 0); this.addLabel('|-⟩', -1.3, 0, 0); this.addLabel('|i+⟩', 0, 1.3, 0); this.addLabel('|i-⟩', 0, -1.3, 0); }; window.QuantumViz.BlochSphere.prototype.addLabel = function(text, x, y, z) { // Create canvas for text rendering const canvas = document.createElement('canvas'); canvas.width = 128; canvas.height = 64; const ctx = canvas.getContext('2d'); ctx.fillStyle = this.options.labelColor; ctx.font = 'bold 40px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(text, 64, 32); // Convert canvas to texture const texture = new THREE.CanvasTexture(canvas); const material = new THREE.SpriteMaterial({ map: texture }); const sprite = new THREE.Sprite(material); sprite.position.set(x, y, z); sprite.scale.set(0.5, 0.25, 1); this.scene.add(sprite); }; window.QuantumViz.BlochSphere.prototype.setState = function(theta, phi) { // Convert spherical coordinates to cartesian const x = Math.sin(theta) * Math.cos(phi); const y = Math.sin(theta) * Math.sin(phi); const z = Math.cos(theta); // Update state vector direction const dir = new THREE.Vector3(x, y, z); this.stateVector.setDirection(dir); // Trigger onUpdate callback if defined if (typeof this.options.onUpdate === 'function') { this.options.onUpdate({ theta, phi, x, y, z }); } }; // Set state from Bloch sphere coordinates window.QuantumViz.BlochSphere.prototype.setStateByAngles = function(theta, phi) { this.setState(theta, phi); }; // Set state from quantum state vector [alpha, beta] window.QuantumViz.BlochSphere.prototype.setStateByVector = function(alpha, beta) { // Convert quantum state to Bloch sphere coordinates // |ψ⟩ = α|0⟩ + β|1⟩ → (θ, φ) on Bloch sphere // Handle complex numbers const alphaAbs = typeof alpha === 'object' ? Math.sqrt(alpha.real**2 + alpha.imag**2) : Math.abs(alpha); const theta = 2 * Math.acos(alphaAbs); let phi = 0; if (alphaAbs < 0.9999 && Math.abs(beta) > 0.0001) { // Calculate phase difference if (typeof beta === 'object' && typeof alpha === 'object') { // Complex numbers phi = Math.atan2(beta.imag, beta.real) - Math.atan2(alpha.imag, alpha.real); } else { // Real numbers phi = beta >= 0 ? 0 : Math.PI; } } this.setState(theta, phi); }; window.QuantumViz.BlochSphere.prototype.onResize = function() { this.width = this.container.clientWidth; this.height = this.container.clientHeight; this.camera.aspect = this.width / this.height; this.camera.updateProjectionMatrix(); this.renderer.setSize(this.width, this.height); }; window.QuantumViz.BlochSphere.prototype.animate = function() { requestAnimationFrame(this.animate.bind(this)); this.controls.update(); this.renderer.render(this.scene, this.camera); }; // Add QuantumCircuitRenderer to the namespace window.QuantumViz.QuantumCircuitRenderer = function(containerId, options = {}) { this.container = document.getElementById(containerId); this.options = Object.assign({ padding: 20, qubitSpacing: 50, gateSpacing: 60, qubitLabelWidth: 50, qubitLineColor: '#888888', gateStrokeColor: '#444444', gateFillColor: '#3498db', textColor: '#ffffff', controlColor: '#e74c3c', measurementColor: '#2ecc71' }, options); this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); this.container.appendChild(this.svg); this.circuit = null; this.qubits = []; this.width = 0; this.height = 0; // Add event listener for window resize window.addEventListener('resize', this.onResize.bind(this)); }; window.QuantumViz.QuantumCircuitRenderer.prototype.onResize = function() { if (this.circuit) { this.render(this.circuit); } }; window.QuantumViz.QuantumCircuitRenderer.prototype.clear = function() { while (this.svg.firstChild) { this.svg.removeChild(this.svg.firstChild); } }; window.QuantumViz.QuantumCircuitRenderer.prototype.render = function(circuit) { this.clear(); this.circuit = circuit; // Extract qubit and gate information this.qubits = this.extractQubits(circuit); const gates = this.extractGates(circuit); // Calculate dimensions const width = this.options.qubitLabelWidth + (gates.length * this.options.gateSpacing) + (2 * this.options.padding); const height = (this.qubits.length * this.options.qubitSpacing) + (2 * this.options.padding); // Set SVG dimensions this.svg.setAttribute('width', width); this.svg.setAttribute('height', height); this.svg.setAttribute('viewBox', `0 0 ${width} ${height}`); // Draw qubit lines this.drawQubitLines(); // Draw gates this.drawGates(gates); }; window.QuantumViz.QuantumCircuitRenderer.prototype.extractQubits = function(circuit) { // This would parse the Cirq circuit to extract qubit information // For demonstration, we'll simulate a circuit with 3 qubits return ['q0', 'q1', 'q2']; }; window.QuantumViz.QuantumCircuitRenderer.prototype.extractGates = function(circuit) { // This would parse the Cirq circuit to extract gate information // For demonstration, we'll create some sample gates return [ { type: 'H', qubit: 0, time: 0 }, { type: 'X', qubit: 1, time: 0 }, { type: 'CNOT', control: 0, target: 1, time: 1 }, { type: 'H', qubit: 0, time: 2 }, { type: 'M', qubit: 0, time: 3 }, { type: 'M', qubit: 1, time: 3 } ]; }; window.QuantumViz.QuantumCircuitRenderer.prototype.drawQubitLines = function() { const startX = this.options.qubitLabelWidth; const endX = this.svg.width.baseVal.value - this.options.padding; this.qubits.forEach((qubit, index) => { const y = this.options.padding + (index * this.options.qubitSpacing); // Draw qubit label const label = document.createElementNS('http://www.w3.org/2000/svg', 'text'); label.setAttribute('x', this.options.padding); label.setAttribute('y', y + 5); // Slight adjustment for text centering label.setAttribute('fill', this.options.textColor); label.setAttribute('text-anchor', 'start'); label.setAttribute('dominant-baseline', 'middle'); label.textContent = qubit; this.svg.appendChild(label); // Draw qubit line const line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', startX); line.setAttribute('y1', y); line.setAttribute('x2', endX); line.setAttribute('y2', y); line.setAttribute('stroke', this.options.qubitLineColor); line.setAttribute('stroke-width', 2); this.svg.appendChild(line); }); }; window.QuantumViz.QuantumCircuitRenderer.prototype.drawGates = function(gates) { gates.forEach(gate => { const x = this.options.qubitLabelWidth + (gate.time * this.options.gateSpacing) + this.options.padding; if (gate.type === 'CNOT') { this.drawCNOT(x, gate.control, gate.target); } else if (gate.type === 'M') { this.drawMeasurement(x, gate.qubit); } else { this.drawSingleQubitGate(x, gate.qubit, gate.type); } }); }; window.QuantumViz.QuantumCircuitRenderer.prototype.drawSingleQubitGate = function(x, qubitIndex, gateType) { const y = this.options.padding + (qubitIndex * this.options.qubitSpacing); const size = 30; // Draw gate box const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); rect.setAttribute('x', x - size/2); rect.setAttribute('y', y - size/2); rect.setAttribute('width', size); rect.setAttribute('height', size); rect.setAttribute('fill', this.options.gateFillColor); rect.setAttribute('stroke', this.options.gateStrokeColor); rect.setAttribute('stroke-width', 2); rect.setAttribute('rx', 4); this.svg.appendChild(rect); // Draw gate label const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text.setAttribute('x', x); text.setAttribute('y', y); text.setAttribute('fill', this.options.textColor); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = gateType; this.svg.appendChild(text); }; window.QuantumViz.QuantumCircuitRenderer.prototype.drawCNOT = function(x, controlIndex, targetIndex) { const controlY = this.options.padding + (controlIndex * this.options.qubitSpacing); const targetY = this.options.padding + (targetIndex * this.options.qubitSpacing); const radius = 15; // Draw vertical line connecting control and target const line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); line.setAttribute('x1', x); line.setAttribute('y1', controlY); line.setAttribute('x2', x); line.setAttribute('y2', targetY); line.setAttribute('stroke', this.options.controlColor); line.setAttribute('stroke-width', 2); this.svg.appendChild(line); // Draw control point const controlPoint = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); controlPoint.setAttribute('cx', x); controlPoint.setAttribute('cy', controlY); controlPoint.setAttribute('r', 5); controlPoint.setAttribute('fill', this.options.controlColor); this.svg.appendChild(controlPoint); // Draw target (⊕ symbol) const targetCircle = document.createElementNS('http://www.w3.org/2000/svg', 'circle'); targetCircle.setAttribute('cx', x); targetCircle.setAttribute('cy', targetY); targetCircle.setAttribute('r', radius); targetCircle.setAttribute('fill', 'none'); targetCircle.setAttribute('stroke', this.options.controlColor); targetCircle.setAttribute('stroke-width', 2); this.svg.appendChild(targetCircle); // Draw the "+" in the target const vLine = document.createElementNS('http://www.w3.org/2000/svg', 'line'); vLine.setAttribute('x1', x); vLine.setAttribute('y1', targetY - radius); vLine.setAttribute('x2', x); vLine.setAttribute('y2', targetY + radius); vLine.setAttribute('stroke', this.options.controlColor); vLine.setAttribute('stroke-width', 2); this.svg.appendChild(vLine); const hLine = document.createElementNS('http://www.w3.org/2000/svg', 'line'); hLine.setAttribute('x1', x - radius); hLine.setAttribute('y1', targetY); hLine.setAttribute('x2', x + radius); hLine.setAttribute('y2', targetY); hLine.setAttribute('stroke', this.options.controlColor); hLine.setAttribute('stroke-width', 2); this.svg.appendChild(hLine); }; window.QuantumViz.QuantumCircuitRenderer.prototype.drawMeasurement = function(x, qubitIndex) { const y = this.options.padding + (qubitIndex * this.options.qubitSpacing); const size = 30; // Draw measurement box const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); rect.setAttribute('x', x - size/2); rect.setAttribute('y', y - size/2); rect.setAttribute('width', size); rect.setAttribute('height', size); rect.setAttribute('fill', this.options.measurementColor); rect.setAttribute('stroke', this.options.gateStrokeColor); rect.setAttribute('stroke-width', 2); rect.setAttribute('rx', 4); this.svg.appendChild(rect); // Draw measurement symbol (M) const text = document.createElementNS('http://www.w3.org/2000/svg', 'text'); text.setAttribute('x', x); text.setAttribute('y', y); text.setAttribute('fill', this.options.textColor); text.setAttribute('text-anchor', 'middle'); text.setAttribute('dominant-baseline', 'middle'); text.textContent = 'M'; this.svg.appendChild(text); }; // Add QuantumStateProbability to the namespace window.QuantumViz.QuantumStateProbability = function(containerId, options = {}) { this.container = document.getElementById(containerId); this.options = Object.assign({ width: this.container.clientWidth || 500, height: 300, barColor: '#3498db', textColor: '#ffffff', axisColor: '#888888', gridColor: '#333333', padding: 40 }, options); this.canvas = document.createElement('canvas'); this.canvas.width = this.options.width; this.canvas.height = this.options.height; this.container.appendChild(this.canvas); this.ctx = this.canvas.getContext('2d'); // Add event listener for window resize window.addEventListener('resize', this.onResize.bind(this)); }; window.QuantumViz.QuantumStateProbability.prototype.onResize = function() { this.options.width = this.container.clientWidth; this.canvas.width = this.options.width; if (this.data) { this.render(this.data); } }; window.QuantumViz.QuantumStateProbability.prototype.render = function(stateVector) { this.data = stateVector; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // Calculate probabilities const probabilities = this.calculateProbabilities(stateVector); // Draw axes and labels this.drawAxes(probabilities.length); // Draw probability bars this.drawBars(probabilities); }; window.QuantumViz.QuantumStateProbability.prototype.calculateProbabilities = function(stateVector) { // Calculate probability for each basis state return stateVector.map(amplitude => { if (typeof amplitude === 'object') { // Complex number return amplitude.real**2 + amplitude.imag**2; } else { // Real number return amplitude**2; } }); }; window.QuantumViz.QuantumStateProbability.prototype.drawAxes = function(numStates) { const { ctx, options } = this; const { width, height, padding, axisColor, gridColor, textColor } = options; // Draw axes ctx.strokeStyle = axisColor; ctx.lineWidth = 2; ctx.beginPath(); // X-axis ctx.moveTo(padding, height - padding); ctx.lineTo(width - padding, height - padding); // Y-axis ctx.moveTo(padding, height - padding); ctx.lineTo(padding, padding); ctx.stroke(); // Draw grid lines and labels ctx.strokeStyle = gridColor; ctx.lineWidth = 1; ctx.fillStyle = textColor; ctx.font = '12px Arial'; ctx.textAlign = 'center'; // Y-axis labels and grid (probabilities) for (let i = 0; i <= 10; i++) { const y = height - padding - ((height - 2 * padding) * i / 10); const prob = i / 10; // Grid line ctx.beginPath(); ctx.moveTo(padding, y); ctx.lineTo(width - padding, y); ctx.stroke(); // Label ctx.fillText(prob.toFixed(1), padding - 15, y + 4); } // X-axis labels (basis states) const barWidth = (width - 2 * padding) / numStates; for (let i = 0; i < numStates; i++) { const x = padding + (i * barWidth) + (barWidth / 2); const label = i.toString(2).padStart(Math.log2(numStates), '0'); // Label ctx.fillText(`|${label}⟩`, x, height - padding + 20); } // Axis titles ctx.font = '14px Arial'; ctx.fillText('Basis States', width / 2, height - 10); ctx.save(); ctx.translate(15, height / 2); ctx.rotate(-Math.PI / 2); ctx.fillText('Probability', 0, 0); ctx.restore(); }; window.QuantumViz.QuantumStateProbability.prototype.drawBars = function(probabilities) { const { ctx, options } = this; const { width, height, padding, barColor } = options; const numBars = probabilities.length; const barWidth = (width - 2 * padding) / numBars; const maxBarHeight = height - 2 * padding; ctx.fillStyle = barColor; // Draw bars probabilities.forEach((prob, i) => { const barHeight = prob * maxBarHeight; const x = padding + (i * barWidth); const y = height - padding - barHeight; ctx.fillRect(x, y, barWidth * 0.8, barHeight); // Draw probability value on top of bar if it's significant if (prob > 0.05) { ctx.fillStyle = options.textColor; ctx.fillText(prob.toFixed(2), x + barWidth * 0.4, y - 5); ctx.fillStyle = barColor; } }); }; // Export visualization components /* window.quantumViz = { BlochSphere, QuantumCircuitRenderer, QuantumStateProbability };*/