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)