MophongLT / static /js /visualization_adapter.js
gaialive's picture
Upload 170 files
227c43a verified
/**
* GXS - QuantumNexus - Visualization Adapter
* Fixed version that ensures Bloch sphere and circuit visualizations render properly
*/
document.addEventListener('DOMContentLoaded', function() {
console.log('Enhanced visualization adapter loaded');
// Initialize a Bloch sphere visualization
initBlochSphere();
// Initialize circuit visualizer if needed
initCircuitVisualizer();
// Setup state change buttons
setupStateButtons();
});
function initBlochSphere() {
// Look for Bloch sphere containers in various locations
const containers = [
document.getElementById('bloch-sphere-container'),
document.getElementById('quantum-state-viz')
];
const container = containers.find(c => c !== null);
if (!container) {
console.log('No Bloch sphere container found on page');
return;
}
console.log('Initializing Bloch sphere in:', container.id);
try {
// Ensure THREE.js is available
if (typeof THREE === 'undefined') {
// Load THREE.js dynamically if not available
loadScript('https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js', function() {
createBlochSphere(container);
});
} else {
createBlochSphere(container);
}
} catch (e) {
console.error('Error initializing Bloch sphere:', e);
container.innerHTML = `
<div class="alert alert-warning p-3 text-center">
<i class="fas fa-exclamation-triangle me-2"></i>
Error initializing visualization: ${e.message}
</div>`;
}
}
function createBlochSphere(container) {
// Check if we should use the QuantumViz namespace implementation
if (window.QuantumViz && window.QuantumViz.BlochSphere) {
try {
// Use the namespaced version
console.log('Using QuantumViz.BlochSphere implementation');
const blochSphere = new QuantumViz.BlochSphere(container.id);
// Store reference to the created instance
window.blochSphere = {
setStateByAngles: function(theta, phi) {
blochSphere.setStateByAngles(theta, phi);
},
setToState0: function() { this.setStateByAngles(0, 0); },
setToState1: function() { this.setStateByAngles(Math.PI, 0); },
setToStatePlus: function() { this.setStateByAngles(Math.PI/2, 0); },
setToStateMinus: function() { this.setStateByAngles(Math.PI/2, Math.PI); },
setToPlusI: function() { this.setStateByAngles(Math.PI/2, Math.PI/2); },
setToMinusI: function() { this.setStateByAngles(Math.PI/2, -Math.PI/2); }
};
// Set initial state to |+⟩
window.blochSphere.setStateByAngles(Math.PI/2, 0);
return;
} catch (e) {
console.error('Error using QuantumViz implementation:', e);
// Fall back to custom implementation
}
}
// Clean container first
container.innerHTML = '';
// Set up scene
const scene = new THREE.Scene();
scene.background = new THREE.Color('#141424');
// Set up camera
const width = container.clientWidth || 400;
const height = container.clientHeight || 400;
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
camera.position.set(0, 0, 2.5);
camera.lookAt(0, 0, 0);
// Set up renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
container.appendChild(renderer.domElement);
// Add lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 5, 5);
scene.add(directionalLight);
// Add sphere
const sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
const sphereMaterial = new THREE.MeshPhongMaterial({
color: '#2a2a4a',
transparent: true,
opacity: 0.3,
wireframe: false
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
// Add wireframe
const wireGeometry = new THREE.SphereGeometry(1.001, 16, 16);
const wireMaterial = new THREE.MeshBasicMaterial({
color: '#4a4a8a',
wireframe: true,
transparent: true,
opacity: 0.5
});
const wireframe = new THREE.Mesh(wireGeometry, wireMaterial);
scene.add(wireframe);
// Add axes
const axesGroup = new THREE.Group();
// Z-axis (|0⟩ to |1⟩)
const zMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00 });
const zPoints = [
new THREE.Vector3(0, 0, -1.2),
new THREE.Vector3(0, 0, 1.2)
];
const zGeometry = new THREE.BufferGeometry().setFromPoints(zPoints);
const zLine = new THREE.Line(zGeometry, zMaterial);
axesGroup.add(zLine);
// X-axis (|+⟩ to |-⟩)
const xMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
const xPoints = [
new THREE.Vector3(-1.2, 0, 0),
new THREE.Vector3(1.2, 0, 0)
];
const xGeometry = new THREE.BufferGeometry().setFromPoints(xPoints);
const xLine = new THREE.Line(xGeometry, xMaterial);
axesGroup.add(xLine);
// Y-axis (|i+⟩ to |i-⟩)
const yMaterial = new THREE.LineBasicMaterial({ color: 0x0000ff });
const yPoints = [
new THREE.Vector3(0, -1.2, 0),
new THREE.Vector3(0, 1.2, 0)
];
const yGeometry = new THREE.BufferGeometry().setFromPoints(yPoints);
const yLine = new THREE.Line(yGeometry, yMaterial);
axesGroup.add(yLine);
scene.add(axesGroup);
// Add state vector
const arrowDirection = new THREE.Vector3(0, 0, 1);
const arrowOrigin = new THREE.Vector3(0, 0, 0);
const arrowLength = 1;
const arrowColor = 0xff3366;
const headLength = 0.2;
const headWidth = 0.1;
const stateVector = new THREE.ArrowHelper(
arrowDirection,
arrowOrigin,
arrowLength,
arrowColor,
headLength,
headWidth
);
scene.add(stateVector);
// Add equator circle
// Create points for circle manually to avoid using deprecated vertices property
const points = [];
const segments = 64;
for (let i = 0; i < segments; i++) {
const theta = (i / segments) * Math.PI * 2;
points.push(new THREE.Vector3(Math.cos(theta), Math.sin(theta), 0));
}
const equatorGeometry = new THREE.BufferGeometry().setFromPoints(points);
const equatorMaterial = new THREE.MeshBasicMaterial({
color: '#4a4a8a',
transparent: true,
opacity: 0.3,
side: THREE.DoubleSide
});
const equator = new THREE.Mesh(equatorGeometry, equatorMaterial);
equator.rotation.x = Math.PI / 2;
scene.add(equator);
// Add simple orbit controls if available
let controls = null;
if (typeof THREE.OrbitControls !== 'undefined') {
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.rotateSpeed = 0.5;
} else {
console.warn('OrbitControls not available, using auto-rotation instead');
}
// Add labels for quantum states
addStateLabel(scene, '|0⟩', 0, 0, 1.3);
addStateLabel(scene, '|1⟩', 0, 0, -1.3);
addStateLabel(scene, '|+⟩', 1.3, 0, 0);
addStateLabel(scene, '|-⟩', -1.3, 0, 0);
addStateLabel(scene, '|i+⟩', 0, 1.3, 0);
addStateLabel(scene, '|i-⟩', 0, -1.3, 0);
// Animation loop
function animate() {
requestAnimationFrame(animate);
if (controls) {
controls.update();
} else {
// Auto-rotate if no controls
sphere.rotation.y += 0.005;
wireframe.rotation.y += 0.005;
axesGroup.rotation.y += 0.005;
equator.rotation.y += 0.005;
stateVector.rotation.y += 0.005;
}
renderer.render(scene, camera);
}
animate();
// Add window resize handler
window.addEventListener('resize', () => {
const width = container.clientWidth;
const height = container.clientHeight || 400;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height);
});
// Create the interface for state changes
window.blochSphere = {
setStateByAngles: 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 arrow direction
const newDirection = new THREE.Vector3(x, y, z);
stateVector.setDirection(newDirection);
},
setToState0: function() { this.setStateByAngles(0, 0); },
setToState1: function() { this.setStateByAngles(Math.PI, 0); },
setToStatePlus: function() { this.setStateByAngles(Math.PI/2, 0); },
setToStateMinus: function() { this.setStateByAngles(Math.PI/2, Math.PI); },
setToPlusI: function() { this.setStateByAngles(Math.PI/2, Math.PI/2); },
setToMinusI: function() { this.setStateByAngles(Math.PI/2, -Math.PI/2); }
};
// Set initial state to |+⟩
window.blochSphere.setStateByAngles(Math.PI/2, 0);
console.log('Bloch sphere created successfully');
}
function addStateLabel(scene, text, x, y, z) {
// Create a canvas for the label
const canvas = document.createElement('canvas');
canvas.width = 128;
canvas.height = 64;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 32px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(text, 64, 32);
// Create texture from canvas
const texture = new THREE.CanvasTexture(canvas);
texture.needsUpdate = true;
// Create a sprite material with the texture
const spriteMaterial = new THREE.SpriteMaterial({
map: texture,
transparent: true
});
// Create the sprite
const sprite = new THREE.Sprite(spriteMaterial);
sprite.position.set(x, y, z);
sprite.scale.set(0.5, 0.25, 1);
scene.add(sprite);
return sprite;
}
function initCircuitVisualizer() {
const container = document.getElementById('circuit-demo-container');
if (!container) {
return;
}
console.log('Initializing circuit visualizer');
// Check if we should use the QuantumViz namespace implementation
if (window.QuantumViz && window.QuantumViz.QuantumCircuitRenderer) {
try {
console.log('Using QuantumViz.QuantumCircuitRenderer implementation');
const circuit = {
qubits: ['q0', 'q1', 'q2'],
gates: [
{ 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: 'CNOT', control: 1, target: 2, time: 2 },
{ type: 'M', qubit: 0, time: 3 },
{ type: 'M', qubit: 1, time: 3 }
]
};
const circuitVisualizer = new QuantumViz.QuantumCircuitRenderer(container.id);
circuitVisualizer.render(circuit);
return;
} catch (e) {
console.error('Error using QuantumViz circuit implementation:', e);
// Fall back to custom implementation
}
}
// SVG-based circuit visualizer (fallback)
createCircuitVisualizer(container);
}
function createCircuitVisualizer(container) {
// Parameters
const padding = 20;
const qubitSpacing = 50;
const gateSpacing = 60;
const labelWidth = 50;
// Sample circuit data (can be replaced with real data)
const circuit = {
qubits: ['q0', 'q1', 'q2'],
gates: [
{ 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: 'CNOT', control: 1, target: 2, time: 2 },
{ type: 'M', qubit: 0, time: 3 },
{ type: 'M', qubit: 1, time: 3 },
{ type: 'M', qubit: 2, time: 4 }
]
};
// Calculate dimensions
const width = labelWidth + (getMaxTime(circuit.gates) * gateSpacing) + (2 * padding);
const height = (circuit.qubits.length * qubitSpacing) + (2 * padding);
// Create SVG
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%');
svg.setAttribute('viewBox', `0 0 ${width} ${height}`);
svg.style.backgroundColor = '#ffffff';
container.appendChild(svg);
// Draw qubit lines
drawQubitLines(svg, circuit.qubits, labelWidth, padding, qubitSpacing, width);
// Draw gates
drawGates(svg, circuit.gates, labelWidth, padding, qubitSpacing, gateSpacing);
console.log('Circuit visualizer created successfully');
}
function getMaxTime(gates) {
let maxTime = 0;
for (const gate of gates) {
if (gate.time > maxTime) {
maxTime = gate.time;
}
}
return maxTime + 2; // Add space for the end
}
function drawQubitLines(svg, qubits, labelWidth, padding, qubitSpacing, width) {
qubits.forEach((qubit, index) => {
const y = padding + (index * qubitSpacing);
// Draw qubit label
const label = document.createElementNS('http://www.w3.org/2000/svg', 'text');
label.setAttribute('x', padding);
label.setAttribute('y', y + 5); // Slight adjustment for text centering
label.setAttribute('fill', '#000000');
label.setAttribute('text-anchor', 'start');
label.setAttribute('dominant-baseline', 'middle');
label.textContent = qubit;
svg.appendChild(label);
// Draw qubit line
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', labelWidth);
line.setAttribute('y1', y);
line.setAttribute('x2', width - padding);
line.setAttribute('y2', y);
line.setAttribute('stroke', '#888888');
line.setAttribute('stroke-width', 2);
svg.appendChild(line);
});
}
function drawGates(svg, gates, labelWidth, padding, qubitSpacing, gateSpacing) {
gates.forEach(gate => {
const x = labelWidth + (gate.time * gateSpacing) + padding;
if (gate.type === 'CNOT') {
drawCNOTGate(svg, x, gate.control, gate.target, padding, qubitSpacing);
} else if (gate.type === 'M') {
drawMeasurementGate(svg, x, gate.qubit, padding, qubitSpacing);
} else {
// Single-qubit gate
drawSingleQubitGate(svg, x, gate.qubit, gate.type, padding, qubitSpacing);
}
});
}
function drawSingleQubitGate(svg, x, qubitIndex, gateType, padding, qubitSpacing) {
const y = padding + (qubitIndex * qubitSpacing);
const size = 30;
// Use different colors for different gate types
let color;
switch (gateType) {
case 'H': color = '#3498db'; break; // Blue
case 'X': color = '#e74c3c'; break; // Red
case 'Y': color = '#2ecc71'; break; // Green
case 'Z': color = '#f39c12'; break; // Orange
default: color = '#9b59b6'; // Purple
}
// 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', color);
rect.setAttribute('stroke', '#444444');
rect.setAttribute('stroke-width', 2);
rect.setAttribute('rx', 4);
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', '#ffffff');
text.setAttribute('text-anchor', 'middle');
text.setAttribute('dominant-baseline', 'middle');
text.textContent = gateType;
svg.appendChild(text);
}
function drawCNOTGate(svg, x, controlIndex, targetIndex, padding, qubitSpacing) {
const controlY = padding + (controlIndex * qubitSpacing);
const targetY = padding + (targetIndex * 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', '#e74c3c');
line.setAttribute('stroke-width', 2);
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', '#e74c3c');
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', 'white');
targetCircle.setAttribute('stroke', '#e74c3c');
targetCircle.setAttribute('stroke-width', 2);
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', '#e74c3c');
vLine.setAttribute('stroke-width', 2);
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', '#e74c3c');
hLine.setAttribute('stroke-width', 2);
svg.appendChild(hLine);
}
function drawMeasurementGate(svg, x, qubitIndex, padding, qubitSpacing) {
const y = padding + (qubitIndex * 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', '#2ecc71');
rect.setAttribute('stroke', '#444444');
rect.setAttribute('stroke-width', 2);
rect.setAttribute('rx', 4);
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', '#ffffff');
text.setAttribute('text-anchor', 'middle');
text.setAttribute('dominant-baseline', 'middle');
text.textContent = 'M';
svg.appendChild(text);
}
function setupStateButtons() {
// Define button mappings
const buttonMappings = [
{ selector: '[id="setState-plus"], button:contains("|+⟩")', method: 'setToStatePlus' },
{ selector: '[id="setState-minus"], button:contains("|-⟩")', method: 'setToStateMinus' },
{ selector: '[id="setState-zero"], button:contains("|0⟩")', method: 'setToState0' },
{ selector: '[id="setState-one"], button:contains("|1⟩")', method: 'setToState1' },
{ selector: '[id="setState-plus-i"], button:contains("|+i⟩")', method: 'setToPlusI' },
{ selector: '[id="setState-minus-i"], button:contains("|-i⟩")', method: 'setToMinusI' }
];
buttonMappings.forEach(mapping => {
try {
// Find buttons by ID
const idSelector = mapping.selector.split(',')[0].trim();
const buttons = document.querySelectorAll(idSelector);
// Add click handlers
buttons.forEach(button => {
if (button) {
button.addEventListener('click', function() {
if (window.blochSphere && typeof window.blochSphere[mapping.method] === 'function') {
window.blochSphere[mapping.method]();
console.log(`Set state using ${mapping.method}`);
} else {
console.warn(`blochSphere.${mapping.method} not available`);
}
});
}
});
} catch (e) {
console.warn(`Error setting up button for ${mapping.method}:`, e);
}
});
}
function loadScript(url, callback) {
const script = document.createElement('script');
script.src = url;
script.onload = callback;
script.onerror = function() {
console.error(`Failed to load script: ${url}`);
};
document.head.appendChild(script);
}