File size: 3,836 Bytes
9f8cf99 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | from __future__ import annotations
import numpy as np
from numpy.typing import ArrayLike, NDArray
from .conversions import (
complex_matrix_to_ri,
complex_statevector_to_ri,
kraus_1q_to_ri,
ri_to_complex_matrix,
ri_to_complex_statevector,
)
try:
from . import rust_kernels as _rk # built by maturin from ../rust_kernels
except Exception as e: # pragma: no cover
_rk = None
_import_error = e
def _require_rust_kernels():
if _rk is None: # pragma: no cover
raise ImportError(
"rust_kernels extension is not available. "
"Did you run `maturin develop` in quantumforge/python/?"
) from _import_error
def apply_pauli_channel_statevector(
psi: ArrayLike,
n_qubits: int,
target_qubit: int,
probs: ArrayLike,
seed: int = 0,
) -> NDArray[np.complex128]:
"""
Apply a 1-qubit Pauli channel to a statevector by sampling one of {I,X,Y,Z}.
"""
_require_rust_kernels()
psi_ri = complex_statevector_to_ri(psi)
probs_f = np.asarray(probs, dtype=np.float64)
out_ri = _rk.apply_pauli_channel_statevector(
psi_ri, int(n_qubits), int(target_qubit), probs_f, int(seed)
)
return ri_to_complex_statevector(out_ri)
def apply_kraus_1q_density_matrix(
rho: ArrayLike,
n_qubits: int,
target_qubit: int,
kraus_ops: ArrayLike,
) -> NDArray[np.complex128]:
"""
Apply a 1-qubit Kraus channel to a density matrix: rho' = Σ K rho K†.
"""
_require_rust_kernels()
rho_ri = complex_matrix_to_ri(rho)
kraus_ri = kraus_1q_to_ri(kraus_ops)
out_ri = _rk.apply_kraus_1q_density_matrix(
rho_ri, int(n_qubits), int(target_qubit), kraus_ri
)
return ri_to_complex_matrix(out_ri)
def apply_correlated_pauli_noise_statevector(
psi: ArrayLike,
n_qubits: int,
error_probs: ArrayLike,
seed: int,
) -> NDArray[np.complex128]:
"""Apply correlated multi-qubit Pauli noise to a statevector.
Args:
psi: Complex statevector (shape will be inferred)
n_qubits: Number of qubits
error_probs: Correlation matrix (2^n x 2^n) of error probabilities
seed: Random seed for reproducibility
Returns:
Noisy statevector (same shape as input)
"""
_require_rust_kernels()
psi_ri = complex_statevector_to_ri(psi)
error_probs_array = np.asarray(error_probs, dtype=np.float64)
out_ri = _rk.apply_correlated_pauli_noise_statevector(
psi_ri, n_qubits, error_probs_array, seed
)
return ri_to_complex_statevector(out_ri)
def apply_cnot_error_statevector(
psi: ArrayLike,
n_qubits: int,
control: int,
target: int,
error_prob: float,
seed: int,
) -> NDArray[np.complex128]:
"""Apply CNOT gate error (correlated bit flips) to a statevector.
Args:
psi: Complex statevector
n_qubits: Number of qubits
control: Control qubit index
target: Target qubit index
error_prob: Probability of correlated error
seed: Random seed
Returns:
Statevector with potential CNOT error applied
"""
_require_rust_kernels()
psi_ri = complex_statevector_to_ri(psi)
out_ri = _rk.apply_cnot_error_statevector(
psi_ri, n_qubits, control, target, error_prob, seed
)
return ri_to_complex_statevector(out_ri)
def expectation_value_pauli_string_py(
state: ArrayLike,
pauli_string: str,
) -> float:
"""Compute expectation value ⟨ψ|P|ψ⟩ for a Pauli string P.
Args:
state: Complex statevector
pauli_string: String like "XYZI" (one Pauli per qubit)
Returns:
Expectation value (float)
"""
_require_rust_kernels()
state_ri = complex_statevector_to_ri(state)
return _rk.expectation_value_pauli_string_py(state_ri, pauli_string)
|