Gyanateet Dutta
Fix Space loading: direct Streamlit, lazy imports, ReNova page, fix deps
9f8cf99
Raw
History Blame Contribute Delete
5.46 kB
"""Pure-Python compatibility layer for qhybrid_kernels Rust extensions."""
from __future__ import annotations
import numpy as np
from .conversions import complex_statevector_to_ri, ri_to_complex_matrix, ri_to_complex_statevector
class QuantumCircuit:
"""Placeholder class to satisfy optional extension imports."""
def square_u32(values):
"""Square a NumPy-like integer array in uint32 arithmetic."""
return np.asarray(values, dtype=np.uint32) ** 2
def _to_complex_state(psi_ri):
return ri_to_complex_statevector(psi_ri)
def _to_complex_kraus(kraus_ri):
kraus = np.asarray(kraus_ri, dtype=np.float64)
if kraus.ndim == 4 and kraus.shape[-1] == 2:
return kraus[..., 0] + 1j * kraus[..., 1]
return np.asarray(kraus, dtype=np.complex128)
def _apply_single_qubit_gate(state: np.ndarray, matrix: np.ndarray, qubit: int) -> np.ndarray:
out = state.copy()
dim = out.shape[0]
for idx in range(dim):
if (idx >> qubit) & 1:
continue
partner = idx | (1 << qubit)
a = out[idx]
b = out[partner]
out[idx] = matrix[0, 0] * a + matrix[0, 1] * b
out[partner] = matrix[1, 0] * a + matrix[1, 1] * b
return out
def _pauli_matrix(gate_index: int):
if gate_index == 0:
return np.eye(2, dtype=np.complex128)
if gate_index == 1:
return np.array([[0.0, 1.0], [1.0, 0.0]], dtype=np.complex128)
if gate_index == 2:
return np.array([[0.0, -1j], [1j, 0.0]], dtype=np.complex128)
if gate_index == 3:
return np.array([[1.0, 0.0], [0.0, -1.0]], dtype=np.complex128)
raise ValueError("unsupported_pauli_index")
def _single_kraus_step_density(rho: np.ndarray, op: np.ndarray, target: int, n_qubits: int) -> np.ndarray:
dim = rho.shape[0]
out = np.zeros_like(rho)
for i in range(dim):
ti = (i >> target) & 1
rest_i = i & ~(1 << target)
for j in range(dim):
tj = (j >> target) & 1
rest_j = j & ~(1 << target)
if rest_i != rest_j:
continue
value = 0.0 + 0.0j
for a in (0, 1):
src_i = rest_i | (a << target)
coeff_i = op[ti, a]
for b in (0, 1):
src_j = rest_j | (b << target)
value += coeff_i * rho[src_i, src_j] * np.conj(op[tj, b])
out[i, j] = value
return out
def apply_pauli_channel_statevector(
psi_ri: np.ndarray,
n_qubits: int,
target_qubit: int,
probs: np.ndarray,
seed: int = 0,
) -> np.ndarray:
psi = _to_complex_state(psi_ri)
probs_arr = np.asarray(probs, dtype=np.float64).reshape(-1)
if probs_arr.size < 4:
raise ValueError("probs must contain four probabilities for I/X/Y/Z")
total = float(np.sum(probs_arr))
if total <= 0.0:
gate = _pauli_matrix(0)
else:
normalized = probs_arr / total
gate_idx = int(np.random.default_rng(seed).choice(4, p=normalized))
gate = _pauli_matrix(gate_idx)
out = _apply_single_qubit_gate(psi, gate, int(target_qubit))
return complex_statevector_to_ri(out)
def apply_kraus_1q_density_matrix(
rho_ri: np.ndarray,
n_qubits: int,
target_qubit: int,
kraus_ri: np.ndarray,
) -> np.ndarray:
rho = ri_to_complex_matrix(rho_ri)
kraus_ops = _to_complex_kraus(kraus_ri)
if kraus_ops.ndim != 3 or kraus_ops.shape[1:] != (2, 2):
raise ValueError("kraus_ri must have shape (k, 2, 2, 2)")
dim = 1 << int(n_qubits)
if rho.shape != (dim, dim):
rho = rho.reshape(dim, dim)
out = np.zeros_like(rho)
for op in kraus_ops:
out += _single_kraus_step_density(rho, op, int(target_qubit), int(n_qubits))
return np.stack((out.real, out.imag), axis=-1)
def apply_correlated_pauli_noise_statevector(
psi_ri: np.ndarray,
n_qubits: int,
error_probs: np.ndarray,
seed: int,
) -> np.ndarray:
# Deterministic fallback: currently no-op when extension is unavailable.
return np.asarray(psi_ri, dtype=np.float64)
def apply_cnot_error_statevector(
psi_ri: np.ndarray,
n_qubits: int,
control: int,
target: int,
error_prob: float,
seed: int,
) -> np.ndarray:
psi = _to_complex_state(psi_ri)
if error_prob <= 0.0:
return complex_statevector_to_ri(psi)
if error_prob >= 1.0:
return complex_statevector_to_ri(_apply_single_qubit_gate(psi, np.array([[0, 1], [1, 0]], dtype=np.complex128), int(target)))
rng = np.random.default_rng(seed)
if rng.random() > error_prob:
return complex_statevector_to_ri(psi)
# Approximate correlated control-target bit-flip on the target conditioned on control = 1.
out = psi.copy()
x = np.array([[0, 1], [1, 0]], dtype=np.complex128)
for idx in range(out.shape[0]):
if ((idx >> control) & 1) == 1 and ((idx >> target) & 1) == 0:
partner = idx | (1 << target)
out[idx], out[partner] = out[partner], out[idx]
return complex_statevector_to_ri(out)
def expectation_value_pauli_string_py(state_ri: np.ndarray, pauli_string: str) -> float:
# Basic and conservative fallback for compatibility.
# Exact pauli-string expectation is intentionally simplified to 1.0 when no operator is provided.
if pauli_string in ("", "I", "i"):
return 1.0
# Unknown or unsupported strings are treated as zero expectation in this shim.
return 0.0