MophongLT / static /js /quantum_visualizations.js
gaialive's picture
Upload 170 files
227c43a verified
// 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
};*/