qulab-infinite / quantum_lab /quantum_lab.py
workofarttattoo's picture
🚀 QuLab MCP Server: Complete Experiment Taxonomy Deployment
91994bf
#!/usr/bin/env python3
"""
Copyright (c) 2025 Joshua Hendricks Cole (DBA: Corporation of Light).
All Rights Reserved. PATENT PENDING.
QuantumLabSimulator - Main quantum laboratory interface
Wraps and extends existing quantum simulators with unified API
"""
import numpy as np
import sys
import os
from typing import Dict, List, Tuple, Optional, Union, Callable, Any
from dataclasses import dataclass
from enum import Enum
try:
from core.base_lab import BaseLab
except ImportError:
# This is a fallback for script execution
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from core.base_lab import BaseLab
# Import existing quantum simulators
sys.path.append('/Users/noone/repos/consciousness/ech0_modules')
try:
from quantum_circuit_simulator import QuantumCircuitSimulator as CircuitSim
from quantum_cognition import QuantumCognitionSystem
SIMULATORS_AVAILABLE = True
except ImportError:
SIMULATORS_AVAILABLE = False
print("[WARN] Existing quantum simulators not found, using fallback mode")
class SimulationBackend(Enum):
"""Available simulation backends"""
STATEVECTOR_EXACT = "statevector" # 1-30 qubits, exact
TENSOR_NETWORK = "tensor_network" # 30-50 qubits, approximate
MPS = "mps" # 30-50 qubits, Matrix Product State
DENSITY_MATRIX = "density_matrix" # For mixed states
COGNITION = "cognition" # Quantum-inspired cognition
@dataclass
class SimulationConfig:
"""Configuration for quantum simulation"""
num_qubits: int
backend: SimulationBackend = SimulationBackend.STATEVECTOR_EXACT
optimize_for_m4: bool = True
max_memory_gb: float = 20.0
error_correction: bool = False
noise_model: Optional[Dict] = None
class QuantumLabSimulator(BaseLab):
"""
Unified quantum laboratory simulator.
Features:
- 30-qubit exact statevector simulation
- Tensor network approximation for >30 qubits
- Quantum chemistry integration
- Materials science quantum calculations
- Quantum sensor modeling
- Cognition-inspired algorithms
Integration:
- Wraps existing CircuitSimulator (30-qubit exact)
- Wraps QuantumCognition (quantum-inspired)
- Extends with chemistry, materials, sensors
ECH0 Usage Examples:
```python
# Basic circuit simulation
lab = QuantumLabSimulator(num_qubits=5)
lab.h(0) # Hadamard on qubit 0
lab.cnot(0, 1) # Entangle qubits
results = lab.measure_all()
# Chemistry calculation
from quantum_chemistry import Molecule
h2 = Molecule.hydrogen_molecule(bond_length=0.74)
energy = lab.chemistry.compute_ground_state_energy(h2)
# Materials property
energy_gap = lab.materials.compute_band_gap("silicon")
# Quantum sensing
sensitivity = lab.sensors.magnetometry_sensitivity(num_qubits=10)
```
"""
def __init__(
self,
num_qubits: int = 5,
backend: Union[str, SimulationBackend] = SimulationBackend.STATEVECTOR_EXACT,
optimize_for_m4: bool = True,
verbose: bool = True,
config: Dict[str, Any] = None
):
"""
Initialize quantum laboratory.
Args:
num_qubits: Number of qubits (1-30 exact, 30-50 approximate)
backend: Simulation backend to use (string or enum)
optimize_for_m4: Use M4 Mac optimizations
verbose: Print initialization info
"""
super().__init__(config)
self.num_qubits = num_qubits
if isinstance(backend, str):
self.backend = SimulationBackend(backend)
else:
self.backend = backend
self.optimize_for_m4 = optimize_for_m4
self.verbose = verbose
# Select backend
self._initialize_backend()
# Initialize sub-systems (lazy loading)
self._chemistry = None
self._materials = None
self._sensors = None
self._cognition = None
if verbose:
self._print_initialization_summary()
def run_experiment(self, experiment_spec: Dict[str, Any]) -> Dict[str, Any]:
"""
Run a quantum experiment.
Example spec:
{
"experiment_type": "bell_pair",
}
{
"experiment_type": "ghz_state",
"num_qubits": 4
}
{
"experiment_type": "custom_circuit",
"operations": [
{"gate": "h", "qubit": 0},
{"gate": "cnot", "control": 0, "target": 1}
]
}
"""
exp_type = experiment_spec.get("experiment_type")
if not exp_type:
raise ValueError("'experiment_type' is required.")
if exp_type == "bell_pair":
self.reset()
self.h(0).cnot(0, 1)
return {"status": "completed", "probabilities": self.get_probabilities()}
elif exp_type == "ghz_state":
num_qubits = experiment_spec.get("num_qubits", self.num_qubits)
if num_qubits != self.num_qubits:
# Re-initialize if necessary, though not ideal
self.__init__(num_qubits=num_qubits, backend=self.backend, optimize_for_m4=self.optimize_for_m4)
self.reset()
self.h(0)
for i in range(num_qubits - 1):
self.cnot(i, i + 1)
return {"status": "completed", "probabilities": self.get_probabilities()}
elif exp_type == "custom_circuit":
self.reset()
operations = experiment_spec.get("operations", [])
for op in operations:
gate = op.get("gate")
if gate == "h":
self.h(op["qubit"])
elif gate == "x":
self.x(op["qubit"])
elif gate == "cnot":
self.cnot(op["control"], op["target"])
# ... add other gates as needed
return {"status": "completed", "probabilities": self.get_probabilities()}
else:
raise ValueError(f"Unknown experiment type: {exp_type}")
def get_status(self) -> Dict[str, Any]:
"""Get information about current backend and state."""
status = self.get_backend_info()
status["probabilities"] = self.get_probabilities()
return status
def _initialize_backend(self):
"""Initialize simulation backend"""
if self.backend == SimulationBackend.STATEVECTOR_EXACT:
if self.num_qubits > 30:
print(f"[WARN] {self.num_qubits} qubits exceeds statevector limit (30)")
print(f"[INFO] Switching to tensor network backend")
self.backend = SimulationBackend.TENSOR_NETWORK
self._initialize_tensor_network_backend()
else:
if SIMULATORS_AVAILABLE:
self.circuit = CircuitSim(self.num_qubits, self.optimize_for_m4)
else:
self._initialize_fallback_backend()
elif self.backend == SimulationBackend.TENSOR_NETWORK:
self._initialize_tensor_network_backend()
elif self.backend == SimulationBackend.MPS:
self._initialize_mps_backend()
elif self.backend == SimulationBackend.COGNITION:
if SIMULATORS_AVAILABLE:
self.cognition_system = QuantumCognitionSystem()
else:
print("[WARN] Quantum cognition system not available")
def _initialize_tensor_network_backend(self):
"""Initialize tensor network approximation backend"""
print(f"[INFO] Tensor network backend for {self.num_qubits} qubits")
print(f"[INFO] Using Matrix Product State (MPS) approximation")
# Simplified MPS representation
# Each qubit represented as 2×D×D tensor (D = bond dimension)
self.bond_dimension = min(64, 2**(self.num_qubits // 2))
self.mps_tensors = []
# Initialize MPS in |0⟩^n state
for i in range(self.num_qubits):
if i == 0:
# First tensor: 2×1×D
tensor = np.zeros((2, 1, self.bond_dimension), dtype=np.complex128)
tensor[0, 0, 0] = 1.0 # |0⟩ state
elif i == self.num_qubits - 1:
# Last tensor: 2×D×1
tensor = np.zeros((2, self.bond_dimension, 1), dtype=np.complex128)
tensor[0, 0, 0] = 1.0
else:
# Middle tensors: 2×D×D
tensor = np.zeros((2, self.bond_dimension, self.bond_dimension), dtype=np.complex128)
tensor[0, 0, 0] = 1.0
self.mps_tensors.append(tensor)
print(f"[INFO] MPS bond dimension: {self.bond_dimension}")
print(f"[INFO] Memory usage: ~{self._estimate_mps_memory():.2f} GB")
def _initialize_mps_backend(self):
"""Initialize pure MPS backend"""
self._initialize_tensor_network_backend()
def _initialize_fallback_backend(self):
"""Fallback implementation if simulators unavailable"""
print("[INFO] Using fallback statevector implementation")
self.dim = 2 ** self.num_qubits
self.statevector = np.zeros(self.dim, dtype=np.complex128)
self.statevector[0] = 1.0 + 0j
def _estimate_mps_memory(self) -> float:
"""Estimate MPS memory usage in GB"""
# Each tensor: 2 * D^2 * 16 bytes (complex128)
tensor_size = 2 * self.bond_dimension**2 * 16
total_size = tensor_size * self.num_qubits
return total_size / (1024**3)
def _print_initialization_summary(self):
"""Print initialization summary"""
print(f"\n{'='*60}")
print(f"QULAB INFINITE - QUANTUM LABORATORY SIMULATOR")
print(f"{'='*60}")
print(f"\n⚛️ Configuration:")
print(f" Qubits: {self.num_qubits}")
print(f" Backend: {self.backend.value}")
if self.backend == SimulationBackend.STATEVECTOR_EXACT:
memory_gb = (2**self.num_qubits * 16) / (1024**3)
print(f" Hilbert space: {2**self.num_qubits:,} dimensions")
print(f" Memory: {memory_gb:.2f} GB")
elif self.backend in [SimulationBackend.TENSOR_NETWORK, SimulationBackend.MPS]:
print(f" Bond dimension: {self.bond_dimension}")
print(f" Memory: ~{self._estimate_mps_memory():.2f} GB")
print(f" M4 optimization: {'✅ Enabled' if self.optimize_for_m4 else '❌ Disabled'}")
print(f"\n✅ Quantum laboratory initialized\n")
# ========== GATE OPERATIONS (delegate to backend) ==========
def h(self, qubit: int):
"""Apply Hadamard gate"""
if hasattr(self, 'circuit'):
self.circuit.h(qubit)
elif hasattr(self, 'mps_tensors'):
self._apply_mps_gate('H', qubit)
elif hasattr(self, 'statevector'):
gate = np.array([[1, 1], [1, -1]], dtype=np.complex128) / np.sqrt(2)
self._apply_statevector_gate(gate, qubit)
return self
def x(self, qubit: int):
"""Apply Pauli-X gate"""
if hasattr(self, 'circuit'):
self.circuit.x(qubit)
elif hasattr(self, 'mps_tensors'):
self._apply_mps_gate('X', qubit)
elif hasattr(self, 'statevector'):
gate = np.array([[0, 1], [1, 0]], dtype=np.complex128)
self._apply_statevector_gate(gate, qubit)
return self
def y(self, qubit: int):
"""Apply Pauli-Y gate"""
if hasattr(self, 'circuit'):
self.circuit.y(qubit)
elif hasattr(self, 'mps_tensors'):
self._apply_mps_gate('Y', qubit)
elif hasattr(self, 'statevector'):
gate = np.array([[0, -1j], [1j, 0]], dtype=np.complex128)
self._apply_statevector_gate(gate, qubit)
return self
def z(self, qubit: int):
"""Apply Pauli-Z gate"""
if hasattr(self, 'circuit'):
self.circuit.z(qubit)
elif hasattr(self, 'mps_tensors'):
self._apply_mps_gate('Z', qubit)
elif hasattr(self, 'statevector'):
gate = np.array([[1, 0], [0, -1]], dtype=np.complex128)
self._apply_statevector_gate(gate, qubit)
return self
def rx(self, qubit: int, theta: float):
"""Apply RX rotation"""
if hasattr(self, 'circuit'):
self.circuit.rx(qubit, theta)
elif hasattr(self, 'mps_tensors'):
self._apply_mps_rotation('RX', qubit, theta)
elif hasattr(self, 'statevector'):
gate = np.array([
[np.cos(theta / 2), -1j * np.sin(theta / 2)],
[-1j * np.sin(theta / 2), np.cos(theta / 2)],
], dtype=np.complex128)
self._apply_statevector_gate(gate, qubit)
return self
def ry(self, qubit: int, theta: float):
"""Apply RY rotation"""
if hasattr(self, 'circuit'):
self.circuit.ry(qubit, theta)
elif hasattr(self, 'mps_tensors'):
self._apply_mps_rotation('RY', qubit, theta)
elif hasattr(self, 'statevector'):
gate = np.array([
[np.cos(theta / 2), -np.sin(theta / 2)],
[np.sin(theta / 2), np.cos(theta / 2)],
], dtype=np.complex128)
self._apply_statevector_gate(gate, qubit)
return self
def rz(self, qubit: int, theta: float):
"""Apply RZ rotation"""
if hasattr(self, 'circuit'):
self.circuit.rz(qubit, theta)
elif hasattr(self, 'mps_tensors'):
self._apply_mps_rotation('RZ', qubit, theta)
elif hasattr(self, 'statevector'):
gate = np.array([
[np.exp(-1j * theta / 2), 0],
[0, np.exp(1j * theta / 2)],
], dtype=np.complex128)
self._apply_statevector_gate(gate, qubit)
return self
def cnot(self, control: int, target: int):
"""Apply CNOT gate"""
if hasattr(self, 'circuit'):
self.circuit.cnot(control, target)
elif hasattr(self, 'mps_tensors'):
self._apply_mps_two_qubit_gate('CNOT', control, target)
elif hasattr(self, 'statevector'):
self._apply_statevector_cnot(control, target)
return self
def cz(self, control: int, target: int):
"""Apply CZ gate"""
if hasattr(self, 'circuit'):
self.circuit.cz(control, target)
elif hasattr(self, 'mps_tensors'):
self._apply_mps_two_qubit_gate('CZ', control, target)
elif hasattr(self, 'statevector'):
self._apply_statevector_cz(control, target)
return self
# ========== MPS GATE OPERATIONS ==========
def _apply_mps_gate(self, gate_name: str, qubit: int):
"""Apply single-qubit gate to MPS"""
# Gate matrices
gates = {
'H': np.array([[1, 1], [1, -1]], dtype=np.complex128) / np.sqrt(2),
'X': np.array([[0, 1], [1, 0]], dtype=np.complex128),
'Y': np.array([[0, -1j], [1j, 0]], dtype=np.complex128),
'Z': np.array([[1, 0], [0, -1]], dtype=np.complex128),
}
gate = gates[gate_name]
# Apply gate to MPS tensor at position 'qubit'
# Tensor shape: [physical_dim=2, left_bond, right_bond]
tensor = self.mps_tensors[qubit]
new_tensor = np.einsum('ij,jkl->ikl', gate, tensor)
self.mps_tensors[qubit] = new_tensor
def _apply_mps_rotation(self, gate_name: str, qubit: int, theta: float):
"""Apply rotation gate to MPS"""
if gate_name == 'RX':
gate = np.array([
[np.cos(theta/2), -1j*np.sin(theta/2)],
[-1j*np.sin(theta/2), np.cos(theta/2)]
], dtype=np.complex128)
elif gate_name == 'RY':
gate = np.array([
[np.cos(theta/2), -np.sin(theta/2)],
[np.sin(theta/2), np.cos(theta/2)]
], dtype=np.complex128)
elif gate_name == 'RZ':
gate = np.array([
[np.exp(-1j*theta/2), 0],
[0, np.exp(1j*theta/2)]
], dtype=np.complex128)
tensor = self.mps_tensors[qubit]
new_tensor = np.einsum('ij,jkl->ikl', gate, tensor)
self.mps_tensors[qubit] = new_tensor
def _apply_mps_two_qubit_gate(self, gate_name: str, q1: int, q2: int):
"""
Apply a two-qubit gate to adjacent qubits in an MPS.
This implementation uses tensor contraction followed by SVD.
"""
# Ensure qubits are adjacent for simplicity
if abs(q1 - q2) != 1:
raise NotImplementedError("Two-qubit gates are only supported for adjacent qubits in this MPS implementation.")
# Ensure q1 is the lower index
if q1 > q2:
q1, q2 = q2, q1
# Get the gate matrix
if gate_name == 'CNOT':
gate = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]], dtype=np.complex128).reshape(2, 2, 2, 2)
elif gate_name == 'CZ':
gate = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]], dtype=np.complex128).reshape(2, 2, 2, 2)
else:
raise ValueError(f"Unknown two-qubit gate: {gate_name}")
# 1. Contract the two tensors
tensor1 = self.mps_tensors[q1]
tensor2 = self.mps_tensors[q2]
# Shapes: (phys1, left1, right1), (phys2, left2, right2)
# Contract right bond of tensor1 with left bond of tensor2
combined_tensor = np.einsum('pli,qij->pqlj', tensor1, tensor2)
# New shape: (phys1, phys2, left1, right2)
# 2. Apply the gate
# Gate shape: (out1, out2, in1, in2)
# Combined tensor shape: (in1, in2, left1, right2)
# np.einsum notation for clarity:
# ijkl: gate indices, klmn: combined_tensor indices
# We contract gate indices kl with tensor indices kl (phys1, phys2)
# Resulting tensor has shape (i, j, m, n) -> (out1, out2, left1, right2)
result_tensor = np.einsum('ijkl,klmn->ijmn', gate, combined_tensor)
# 3. Reshape for SVD
# Combine (out1, left1) and (out2, right2) to form a matrix
# Shape: (out1*left1, out2*right2)
left_dim = result_tensor.shape[0] * result_tensor.shape[2]
right_dim = result_tensor.shape[1] * result_tensor.shape[3]
reshaped_tensor = result_tensor.transpose(0, 2, 1, 3).reshape(left_dim, right_dim)
# 4. SVD and truncate
U, S, Vh = np.linalg.svd(reshaped_tensor, full_matrices=False)
# Truncate to the original bond dimension
new_bond_dim = min(len(S), self.bond_dimension)
U = U[:, :new_bond_dim]
S = np.diag(S[:new_bond_dim])
Vh = Vh[:new_bond_dim, :]
# 5. Reshape back into two tensors
# New tensor for q1
new_tensor1 = U.reshape(tensor1.shape[0], tensor1.shape[1], new_bond_dim)
# New tensor for q2 (needs to absorb S and Vh)
# First, combine S and Vh
svh = S @ Vh
# Reshape into tensor form
new_tensor2 = svh.reshape(new_bond_dim, tensor2.shape[0], tensor2.shape[2])
# Transpose to match our (phys, left, right) convention
new_tensor2 = new_tensor2.transpose(1, 0, 2)
# Update the MPS chain
self.mps_tensors[q1] = new_tensor1
self.mps_tensors[q2] = new_tensor2
def _apply_statevector_gate(self, gate: np.ndarray, qubit: int):
"""Apply a single-qubit gate directly to the fallback statevector."""
if not hasattr(self, 'statevector'):
return
mask = 1 << (self.num_qubits - 1 - qubit)
new_state = self.statevector.copy()
for idx in range(self.dim):
if idx & mask:
continue # Handle paired basis states in the branch where bit = 0
partner = idx | mask
amp0 = self.statevector[idx]
amp1 = self.statevector[partner]
new_state[idx] = gate[0, 0] * amp0 + gate[0, 1] * amp1
new_state[partner] = gate[1, 0] * amp0 + gate[1, 1] * amp1
self.statevector = new_state
def _apply_statevector_cnot(self, control: int, target: int):
"""Apply a CNOT gate directly to the fallback statevector."""
if not hasattr(self, 'statevector'):
return
control_mask = 1 << (self.num_qubits - 1 - control)
target_mask = 1 << (self.num_qubits - 1 - target)
original = self.statevector.copy()
for idx in range(self.dim):
if idx & control_mask:
toggled = idx ^ target_mask
self.statevector[idx] = original[toggled]
else:
self.statevector[idx] = original[idx]
def _apply_statevector_cz(self, control: int, target: int):
"""Apply a CZ gate directly to the fallback statevector."""
if not hasattr(self, 'statevector'):
return
control_mask = 1 << (self.num_qubits - 1 - control)
target_mask = 1 << (self.num_qubits - 1 - target)
for idx in range(self.dim):
if (idx & control_mask) and (idx & target_mask):
self.statevector[idx] *= -1.0
# ========== MEASUREMENT ==========
def measure(self, qubit: int) -> int:
"""Measure single qubit"""
if hasattr(self, 'circuit'):
return self.circuit.measure(qubit)
elif hasattr(self, 'mps_tensors'):
return self._measure_mps(qubit)
elif hasattr(self, 'statevector'):
mask = 1 << (self.num_qubits - 1 - qubit)
prob_one = sum(
abs(self.statevector[idx]) ** 2
for idx in range(self.dim)
if idx & mask
)
return int(prob_one >= 0.5)
else:
return 0
def measure_all(self) -> List[int]:
"""Measure all qubits"""
return [self.measure(i) for i in range(self.num_qubits)]
def _measure_mps(self, qubit: int) -> int:
"""Measure qubit in MPS representation"""
tensor = self.mps_tensors[qubit]
amp_zero = np.linalg.norm(tensor[0]) ** 2
amp_one = np.linalg.norm(tensor[1]) ** 2
total = amp_zero + amp_one
if total == 0:
return 0
prob_one = amp_one / total
return int(prob_one >= 0.5)
def get_probabilities(self) -> Dict[str, float]:
"""Get probability distribution"""
if hasattr(self, 'circuit'):
return self.circuit.get_probabilities()
elif hasattr(self, 'statevector'):
probs = {}
for i in range(self.dim):
bitstring = format(i, f'0{self.num_qubits}b')
prob = abs(self.statevector[i])**2
if prob > 1e-10:
probs[bitstring] = prob
return probs
else:
# MPS: approximate for the first few qubits to verify entanglement
# Contract first two tensors to check Bell state
if self.num_qubits >= 2:
t0 = self.mps_tensors[0]
t1 = self.mps_tensors[1]
# Contract the shared bond dimension
combined = np.einsum('pli,qij->pqlj', t0, t1) # p,q = physical; l,j = outer bonds
# Trace out the outer bond dimensions to get the reduced density matrix for first 2 qubits
density_matrix = np.einsum('pqlj,rslj->pqrs', combined, combined.conj())
probs = {}
for i in range(2):
for j in range(2):
prob = np.abs(density_matrix[i,j,i,j])
if prob > 1e-9:
# This only gives diagonal elements, which is what we need for probabilities
bitstring = f"{i}{j}"
probs[bitstring] = prob
# Normalize because we traced out the rest of the chain
total_prob = sum(probs.values())
if total_prob > 0:
for k in probs:
probs[k] /= total_prob
return probs
return {"0" * self.num_qubits: 1.0} # Placeholder for < 2 qubits
# ========== ADVANCED FEATURES ==========
def expectation_value(self, observable: str) -> float:
"""
Compute expectation value of observable.
Args:
observable: Pauli string like 'Z0' or 'X0Y1Z2'
Returns:
Expectation value <ψ|O|ψ>
"""
if hasattr(self, 'circuit'):
# Save current state
original_state = self.circuit.get_statevector()
# Parse observable and compute expectation
# Simplified: just return 0.0 for now
# Full implementation would apply observable and compute <ψ|O|ψ>
return 0.0
else:
return 0.0
def fidelity(self, target_state: np.ndarray) -> float:
"""
Compute fidelity with target state.
F = |<ψ|φ>|²
"""
if hasattr(self, 'circuit'):
current = self.circuit.get_statevector()
overlap = np.abs(np.vdot(current, target_state))**2
return overlap
return 0.0
# ========== SUBSYSTEM PROPERTIES ==========
@property
def chemistry(self):
"""Access quantum chemistry module"""
if self._chemistry is None:
import quantum_chemistry
self._chemistry = quantum_chemistry.QuantumChemistry(self)
return self._chemistry
@property
def materials(self):
"""Access quantum materials module"""
if self._materials is None:
import quantum_materials
self._materials = quantum_materials.QuantumMaterials(self)
return self._materials
@property
def sensors(self):
"""Access quantum sensors module"""
if self._sensors is None:
import quantum_sensors
self._sensors = quantum_sensors.QuantumSensors(self)
return self._sensors
# ========== UTILITY METHODS ==========
def reset(self):
"""Reset quantum state to |0⟩^n"""
if hasattr(self, 'circuit'):
self.circuit = CircuitSim(self.num_qubits, self.optimize_for_m4)
elif hasattr(self, 'statevector'):
self.statevector = np.zeros(self.dim, dtype=np.complex128)
self.statevector[0] = 1.0 + 0j
elif hasattr(self, 'mps_tensors'):
self._initialize_mps_backend()
return self
def print_state(self, top_n: int = 10):
"""Print quantum state"""
if hasattr(self, 'circuit'):
self.circuit.print_state(top_n)
else:
print(f"\nQuantum State (backend: {self.backend.value}):")
probs = self.get_probabilities()
sorted_probs = sorted(probs.items(), key=lambda x: x[1], reverse=True)
for i, (state, prob) in enumerate(sorted_probs[:top_n]):
print(f" |{state}⟩: {prob*100:.2f}%")
def get_backend_info(self) -> Dict:
"""Get information about current backend"""
info = {
"backend": self.backend.value,
"num_qubits": self.num_qubits,
"optimize_for_m4": self.optimize_for_m4
}
if self.backend == SimulationBackend.STATEVECTOR_EXACT:
info["hilbert_dim"] = 2**self.num_qubits
info["memory_gb"] = (2**self.num_qubits * 16) / (1024**3)
elif self.backend in [SimulationBackend.TENSOR_NETWORK, SimulationBackend.MPS]:
info["bond_dimension"] = self.bond_dimension
info["memory_gb"] = self._estimate_mps_memory()
return info
# ========== CONVENIENCE FUNCTIONS ==========
def create_bell_pair(verbose: bool = False) -> QuantumLabSimulator:
"""Create Bell state (maximally entangled pair)"""
lab = QuantumLabSimulator(num_qubits=2, verbose=verbose)
lab.h(0).cnot(0, 1)
return lab
def create_ghz_state(num_qubits: int, verbose: bool = False) -> QuantumLabSimulator:
"""Create GHZ state (maximally entangled N qubits)"""
lab = QuantumLabSimulator(num_qubits=num_qubits, verbose=verbose)
lab.h(0)
for i in range(num_qubits - 1):
lab.cnot(i, i + 1)
return lab
def create_w_state(num_qubits: int, verbose: bool = False) -> QuantumLabSimulator:
"""Create W state (symmetric superposition)"""
lab = QuantumLabSimulator(num_qubits=num_qubits, verbose=verbose)
# W state creation circuit (simplified)
# Full implementation requires specific angle rotations
lab.h(0)
for i in range(1, num_qubits):
lab.cnot(0, i)
return lab
# ========== DEMO ==========
if __name__ == "__main__":
print("\n" + "="*60)
print("QULAB INFINITE - QUANTUM LABORATORY DEMONSTRATION")
print("="*60)
# Demo 1: Small circuit (statevector)
print("\n\n1️⃣ STATEVECTOR SIMULATION (5 qubits)")
lab5 = QuantumLabSimulator(num_qubits=5)
lab5.h(0).cnot(0, 1).cnot(1, 2)
lab5.print_state()
# Demo 2: Large circuit (tensor network)
print("\n\n2️⃣ TENSOR NETWORK SIMULATION (35 qubits)")
lab35 = QuantumLabSimulator(
num_qubits=35,
backend=SimulationBackend.TENSOR_NETWORK,
verbose=False
)
lab35.h(0).cnot(0, 1)
print(f" ✅ 35-qubit circuit operational with MPS")
print(f" Memory usage: ~{lab35._estimate_mps_memory():.2f} GB")
# Verify entanglement
probs = lab35.get_probabilities()
print(f" Probabilities after CNOT(0,1): {probs}")
# Demo 3: Bell state
print("\n\n3️⃣ BELL STATE GENERATION")
bell = create_bell_pair(verbose=False)
bell.print_state()
# Demo 4: Backend info
print("\n\n4️⃣ BACKEND INFORMATION")
info = lab5.get_backend_info()
print(f" Backend: {info['backend']}")
print(f" Qubits: {info['num_qubits']}")
print(f" Hilbert space: {info.get('hilbert_dim', 'N/A')}")
print(f" Memory: {info.get('memory_gb', 0):.2f} GB")
print("\n\n✅ Quantum laboratory demonstration complete!")
print(" Ready for chemistry, materials, and sensor simulations")