7 #include <pybind11/complex.h> 8 #include <pybind11/numpy.h> 9 #include <pybind11/operators.h> 10 #include <pybind11/pybind11.h> 11 #include <pybind11/stl.h> 19 namespace py = pybind11;
27 auto buf = arr.request();
30 throw std::runtime_error(
"Input must be 2D array");
32 if (buf.shape[0] != buf.shape[1]) {
33 throw std::runtime_error(
"Input must be square matrix");
36 int dim =
static_cast<int>(buf.shape[0]);
40 while (temp > 1 && temp % 2 == 0) {
44 throw std::runtime_error(
"Matrix dimension must be power of 2");
57 auto *src =
static_cast<std::complex<double> *
>(buf.ptr);
58 for (
int i = 0; i < dim; i++) {
59 for (
int j = 0; j < dim; j++) {
60 std::complex<double> val = src[i * dim + j];
61 rho(i, j).real = val.real();
62 rho(i, j).imag = val.imag();
69 py::array_t<std::complex<double>>
73 py::array_t<std::complex<double>>
result({dim, dim});
74 auto buf =
result.request();
75 auto *ptr =
static_cast<std::complex<double> *
>(buf.ptr);
77 for (
int i = 0; i < dim; i++) {
78 for (
int j = 0; j < dim; j++) {
80 ptr[i * dim + j] = std::complex<double>(val.
real, val.
imag);
93 SQUANDER Density Matrix Module - C++ Backend (Approach B) 95 High-performance density matrix simulation with: 96 - Optimized local kernel application (O(4^N) per gate) 97 - Unified interface for gates and noise channels 98 - Support for parametric noise 105 py::class_<DensityMatrix>(m,
"DensityMatrix", R
"pbdoc( 106 Quantum density matrix Ï for mixed-state representation. 109 - Automatic memory management 110 - Optimized local unitary application 111 - Quantum properties (purity, entropy, eigenvalues) 114 .def(py::init<int>(), py::arg("qbit_num"),
115 "Create density matrix for n qubits (initialized to |0â©â¨0|)")
117 .def(py::init([](py::array_t<std::complex<double>>
state) {
118 auto buf =
state.request();
121 throw std::runtime_error(
"State vector must be 1D");
124 int dim =
static_cast<int>(buf.shape[0]);
127 auto *src =
static_cast<std::complex<double> *
>(buf.ptr);
128 for (
int i = 0; i < dim; i++) {
135 py::arg(
"state_vector"),
"Create from state vector: Ï = |Ïâ©â¨Ï|")
144 return std::complex<double>(tr.
real, tr.
imag);
146 "Calculate trace: Tr(Ï)")
150 "von Neumann entropy: S(Ï) = -Tr(Ï logâ Ï)")
152 "Check if valid density matrix")
154 "Get eigenvalues (sorted descending)")
158 [](
DensityMatrix &
self, py::array_t<std::complex<double>> U) {
159 auto buf = U.request();
160 if (buf.ndim != 2 || buf.shape[0] != buf.shape[1]) {
161 throw std::runtime_error(
"Unitary must be square 2D array");
164 int dim =
static_cast<int>(buf.shape[0]);
166 auto *src =
static_cast<std::complex<double> *
>(buf.ptr);
167 for (
int i = 0; i < dim * dim; i++) {
172 self.apply_unitary(U_mat);
174 py::arg(
"U"),
"Apply unitary: Ï â UÏUâ ")
177 "apply_single_qubit_unitary",
180 auto buf = u.request();
181 if (buf.ndim != 2 || buf.shape[0] != 2 || buf.shape[1] != 2) {
182 throw std::runtime_error(
"Kernel must be 2x2");
186 auto *src =
static_cast<std::complex<double> *
>(buf.ptr);
187 for (
int i = 0; i < 4; i++) {
192 self.apply_single_qubit_unitary(u_mat, target);
195 "Apply single-qubit unitary using optimized local kernel")
198 "apply_local_unitary",
200 const std::vector<int> &target_qbits) {
201 auto buf = u.request();
202 if (buf.ndim != 2 || buf.shape[0] != buf.shape[1]) {
203 throw std::runtime_error(
"Kernel must be square 2D array");
206 int dim =
static_cast<int>(buf.shape[0]);
208 auto *src =
static_cast<std::complex<double> *
>(buf.ptr);
209 for (
int i = 0; i < dim * dim; i++) {
214 self.apply_local_unitary(u_mat, target_qbits);
217 "Apply a k-qubit local unitary using optimized local kernel")
220 "Partial trace over specified qubits")
224 py::arg(
"qbit_num"),
"Create maximally mixed state: Ï = I/2^n")
231 "Convert to NumPy array")
235 [](py::array_t<std::complex<double>> arr) {
238 py::arg(
"array"),
"Create from NumPy array")
241 return "<DensityMatrix: " + std::to_string(
self.get_qbit_num()) +
242 " qubits, purity=" + std::to_string(
self.purity()) +
">";
249 py::class_<NoisyCircuit::OperationInfo>(m,
"OperationInfo",
250 "Information about a circuit operation")
256 return "<OperationInfo: " +
self.name +
257 (
self.is_unitary ?
" (gate)" :
" (noise)") +
258 ", params=" + std::to_string(
self.param_count) +
">";
265 py::class_<NoisyCircuit>(m,
"NoisyCircuit", R
"pbdoc( 266 Noisy quantum circuit for density matrix simulation. 268 Supports unitary gates and noise channels with unified interface. 269 Uses optimized local kernel application for O(4^N) per-gate performance. 272 circuit = NoisyCircuit(2) 274 circuit.add_CNOT(1, 0) 275 circuit.add_depolarizing(2, error_rate=0.01) 277 circuit.add_phase_damping(0) # Parametric 279 rho = DensityMatrix(2) 280 params = np.array([0.5, 0.02]) # RZ angle, phase damping λ 281 circuit.apply_to(params, rho) 284 .def(py::init<int>(), py::arg("qbit_num"),
285 "Create empty circuit for n qubits")
300 "Add RX rotation (1 param)")
302 "Add RY rotation (1 param)")
304 "Add RZ rotation (1 param)")
306 "Add U1 gate (1 param)")
308 "Add U2 gate (2 params)")
310 "Add U3 gate (3 params)")
314 py::arg(
"control"),
"Add CNOT gate")
316 py::arg(
"control"),
"Add CZ gate")
318 py::arg(
"control"),
"Add CH gate")
322 py::arg(
"control"),
"Add CRY gate (1 param)")
324 py::arg(
"control"),
"Add CRZ gate (1 param)")
326 py::arg(
"control"),
"Add CRX gate (1 param)")
328 py::arg(
"control"),
"Add CP gate (1 param)")
335 self.add_depolarizing(qbit_num);
337 self.add_depolarizing(qbit_num, p.cast<
double>());
342 Add depolarizing noise: Ï â (1-p)Ï + p·I/2^n 345 qbit_num: Number of qubits the noise acts on 346 error_rate: Fixed p, or None for parametric (1 param) 350 "add_local_depolarizing",
353 self.add_local_depolarizing(target);
355 self.add_local_depolarizing(target, p.cast<
double>());
360 Add local single-qubit depolarizing noise on one target qubit. 363 target: Target qubit index 364 error_rate: Fixed p, or None for parametric (1 param) 368 "add_amplitude_damping",
370 if (gamma.is_none()) {
371 self.add_amplitude_damping(target);
373 self.add_amplitude_damping(target, gamma.cast<
double>());
378 Add amplitude damping (T1 relaxation) 381 target: Target qubit index 382 gamma: Fixed γ = 1-exp(-t/T1), or None for parametric (1 param) 387 [](
NoisyCircuit &
self,
int target, py::object lambda_val) {
388 if (lambda_val.is_none()) {
389 self.add_phase_damping(target);
391 self.add_phase_damping(target, lambda_val.cast<
double>());
396 Add phase damping (T2 dephasing) 399 target: Target qubit index 400 lambda_param: Fixed λ = 1-exp(-t/T2), or None for parametric (1 param) 408 auto buf = params.request();
409 self.apply_to(static_cast<double *>(buf.ptr), static_cast<int>(buf.size), rho);
412 "Apply circuit to density matrix")
418 "Total number of parameters")
420 "Number of operations")
424 "Get list of all operations with their info")
427 return "<NoisyCircuit: " + std::to_string(
self.get_qbit_num()) +
428 " qubits, " + std::to_string(
self.get_operation_count()) +
429 " ops, " + std::to_string(
self.get_parameter_num()) +
" params>";
436 py::class_<NoiseChannel, std::shared_ptr<NoiseChannel>>(
437 m,
"NoiseChannel",
"Base class for quantum noise channels (legacy)")
439 "Apply noise channel to density matrix")
443 std::shared_ptr<DepolarizingChannel>>(
444 m,
"DepolarizingChannel",
"Depolarizing channel (legacy standalone)")
445 .def(py::init<int, double>(),
py::arg(
"qbit_num"),
py::arg(
"error_rate"))
450 std::shared_ptr<AmplitudeDampingChannel>>(
451 m,
"AmplitudeDampingChannel",
452 "Amplitude damping channel (legacy standalone)")
453 .def(py::init<int, double>(),
py::arg(
"target_qbit"),
py::arg(
"gamma"))
455 .def_property_readonly(
"target_qbit",
459 std::shared_ptr<PhaseDampingChannel>>(
460 m,
"PhaseDampingChannel",
"Phase damping channel (legacy standalone)")
461 .def(py::init<int, double>(),
py::arg(
"target_qbit"),
py::arg(
"lambda"))
463 .def_property_readonly(
"target_qbit",
470 m.attr(
"__version__") =
"1.0.0";
virtual std::string get_name() const =0
Get channel name.
void add_CZ(int target, int control)
void add_RZ(int target)
1 parameter
void add_CRZ(int target, int control)
1 parameter
void add_CNOT(int target, int control)
void add_U2(int target)
2 parameters
void add_U1(int target)
1 parameter
std::vector< OperationInfo > get_operation_info() const
Get information about all operations.
py::array_t< std::complex< double > > density_matrix_to_numpy(const DensityMatrix &rho)
scalar * get_data() const
Call to get the pointer to the stored data.
void add_CP(int target, int control)
1 parameter
DensityMatrix clone() const
Create deep copy.
bool is_valid(double tol=1e-10) const
Check if valid density matrix.
double entropy() const
von Neumann entropy: S(Ï) = -Tr(Ï logâ Ï)
double purity() const
Calculate purity: Tr(ϲ)
void add_U3(int target)
3 parameters
Amplitude damping (T1 relaxation): |1â© â |0â© decay.
int get_target_qbit() const
Depolarizing channel: Ï â (1-p)Ï + p·I/2^n.
DensityMatrix partial_trace(const std::vector< int > &trace_out) const
Compute partial trace over specified qubits.
virtual void apply(DensityMatrix &rho)=0
Apply noise channel to density matrix.
Base class for quantum noise channels.
int get_qbit_num() const
Get number of qubits.
double arg(const QGD_Complex16 &a)
Call to retrieve the phase of a complex number.
DensityMatrix numpy_to_density_matrix(py::array_t< std::complex< double >> arr)
PYBIND11_MODULE(_density_matrix_cpp, m)
void add_CRY(int target, int control)
1 parameter
std::vector< double > eigenvalues() const
Get eigenvalues (sorted descending)
Header file of complex array storage array with automatic and thread safe reference counting...
Quantum density matrix Ï for mixed-state representation.
Structure type representing complex numbers in the SQUANDER package.
int get_parameter_num() const
Get total number of parameters needed.
int get_qbit_num() const
Get number of qubits.
double get_error_rate() const
Double-precision complex matrix (float64).
void add_RY(int target)
1 parameter
Information about a circuit operation.
void add_CH(int target, int control)
size_t get_operation_count() const
Get number of operations in the circuit.
void add_RX(int target)
1 parameter
int get_dim() const
Get matrix dimension (2^qbit_num)
int get_target_qbit() const
void add_CRX(int target, int control)
1 parameter
double real
the real part of a complex number
Phase damping (T2 dephasing): loss of coherence.
Quantum circuit with noise for density matrix simulation.
double get_lambda() const
double imag
the imaginary part of a complex number
static DensityMatrix maximally_mixed(int qbit_num)
Create maximally mixed state: Ï = I/2^n.