Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
test_basic.cpp
Go to the documentation of this file.
1 /*
2 Copyright 2025 SQUANDER Contributors
3 
4 C++ Unit Tests for Density Matrix Module - Approach B Implementation
5 */
6 
7 #include "Gate.h"
8 #include "H.h"
9 #include "density_matrix.h"
10 #include "gate_operation.h"
11 #include "matrix.h"
12 #include "noise_channel.h"
13 #include "noise_operation.h"
14 #include "noisy_circuit.h"
15 #include <cassert>
16 #include <cmath>
17 #include <iostream>
18 
19 using namespace squander::density;
20 
21 // Test utilities
22 #define ASSERT_NEAR(a, b, tol) \
23  if (std::abs((a) - (b)) > (tol)) { \
24  std::cerr << "FAILED: Expected " << (a) << " ≈ " << (b) \
25  << ", difference: " << std::abs((a) - (b)) << std::endl; \
26  return 1; \
27  }
28 
29 #define ASSERT_TRUE(cond) \
30  if (!(cond)) { \
31  std::cerr << "FAILED: Condition false: " #cond << std::endl; \
32  return 1; \
33  }
34 
35 // ================================================================
36 // Test 1: Basic Construction
37 // ================================================================
38 
40  std::cout << "Test 1: Construction..." << std::flush;
41 
42  // 2-qubit density matrix
43  DensityMatrix rho(2);
44 
45  ASSERT_TRUE(rho.get_qbit_num() == 2);
46  ASSERT_TRUE(rho.get_dim() == 4);
47 
48  std::cout << " PASSED" << std::endl;
49  return 0;
50 }
51 
52 // ================================================================
53 // Test 2: Ground State
54 // ================================================================
55 
57  std::cout << "Test 2: Ground state initialization..." << std::flush;
58 
59  DensityMatrix rho(2);
60 
61  // Should be |00⟩⟨00| = diag(1, 0, 0, 0)
62  ASSERT_NEAR(rho(0, 0).real, 1.0, 1e-10);
63  ASSERT_NEAR(rho(0, 0).imag, 0.0, 1e-10);
64 
65  for (int i = 1; i < 4; i++) {
66  ASSERT_NEAR(rho(i, i).real, 0.0, 1e-10);
67  ASSERT_NEAR(rho(i, i).imag, 0.0, 1e-10);
68  }
69 
70  // Check trace = 1
71  QGD_Complex16 tr = rho.trace();
72  ASSERT_NEAR(tr.real, 1.0, 1e-10);
73  ASSERT_NEAR(tr.imag, 0.0, 1e-10);
74 
75  // Check purity = 1 (pure state)
76  double pur = rho.purity();
77  ASSERT_NEAR(pur, 1.0, 1e-10);
78 
79  // Check entropy = 0 (pure state)
80  double S = rho.entropy();
81  ASSERT_NEAR(S, 0.0, 1e-10);
82 
83  // Check validity
84  ASSERT_TRUE(rho.is_valid());
85 
86  std::cout << " PASSED" << std::endl;
87  return 0;
88 }
89 
90 // ================================================================
91 // Test 3: State Vector Construction
92 // ================================================================
93 
95  std::cout << "Test 3: State vector construction..." << std::flush;
96 
97  // Create |+⟩ = (|0⟩ + |1⟩)/√2
98  Matrix psi(2, 1);
99  double inv_sqrt2 = 1.0 / std::sqrt(2.0);
100  psi[0].real = inv_sqrt2;
101  psi[0].imag = 0.0;
102  psi[1].real = inv_sqrt2;
103  psi[1].imag = 0.0;
104 
105  // Convert to density matrix
106  DensityMatrix rho(std::move(psi));
107 
108  // Check: ρ = |+⟩⟨+| = [[0.5, 0.5], [0.5, 0.5]]
109  ASSERT_NEAR(rho(0, 0).real, 0.5, 1e-10);
110  ASSERT_NEAR(rho(0, 1).real, 0.5, 1e-10);
111  ASSERT_NEAR(rho(1, 0).real, 0.5, 1e-10);
112  ASSERT_NEAR(rho(1, 1).real, 0.5, 1e-10);
113 
114  // Check purity = 1 (still pure)
115  double pur = rho.purity();
116  ASSERT_NEAR(pur, 1.0, 1e-10);
117 
118  std::cout << " PASSED" << std::endl;
119  return 0;
120 }
121 
122 // ================================================================
123 // Test 4: Maximally Mixed State
124 // ================================================================
125 
127  std::cout << "Test 4: Maximally mixed state..." << std::flush;
128 
130 
131  // Check trace = 1
132  QGD_Complex16 tr = rho.trace();
133  ASSERT_NEAR(tr.real, 1.0, 1e-10);
134 
135  // Check purity = 1/4 (maximally mixed for 2 qubits)
136  double pur = rho.purity();
137  ASSERT_NEAR(pur, 0.25, 1e-10);
138 
139  // Check entropy = 2 bits (maximal for 2 qubits)
140  double S = rho.entropy();
141  ASSERT_NEAR(S, 2.0, 1e-10);
142 
143  // Check validity
144  ASSERT_TRUE(rho.is_valid());
145 
146  std::cout << " PASSED" << std::endl;
147  return 0;
148 }
149 
150 // ================================================================
151 // Test 5: Unitary Evolution (Full Matrix)
152 // ================================================================
153 
155  std::cout << "Test 5: Unitary evolution..." << std::flush;
156 
157  // Start with |0⟩
158  Matrix psi(2, 1);
159  psi[0] = {1.0, 0.0};
160  psi[1] = {0.0, 0.0};
161 
162  DensityMatrix rho(psi);
163 
164  // Apply Hadamard: H = [[1, 1], [1, -1]] / √2
165  Matrix H_matrix(2, 2);
166  double inv_sqrt2 = 1.0 / std::sqrt(2.0);
167  H_matrix[0] = {inv_sqrt2, 0.0};
168  H_matrix[1] = {inv_sqrt2, 0.0};
169  H_matrix[2] = {inv_sqrt2, 0.0};
170  H_matrix[3] = {-inv_sqrt2, 0.0};
171 
172  rho.apply_unitary(H_matrix);
173 
174  // Check: ρ should be |+⟩⟨+| = [[0.5, 0.5], [0.5, 0.5]]
175  ASSERT_NEAR(rho(0, 0).real, 0.5, 1e-10);
176  ASSERT_NEAR(rho(0, 1).real, 0.5, 1e-10);
177  ASSERT_NEAR(rho(1, 0).real, 0.5, 1e-10);
178  ASSERT_NEAR(rho(1, 1).real, 0.5, 1e-10);
179 
180  // Still pure
181  double pur = rho.purity();
182  ASSERT_NEAR(pur, 1.0, 1e-10);
183 
184  std::cout << " PASSED" << std::endl;
185  return 0;
186 }
187 
188 // ================================================================
189 // Test 6: apply_single_qubit_unitary (Local Kernel)
190 // ================================================================
191 
193  std::cout << "Test 6: Single-qubit local kernel..." << std::flush;
194 
195  // Start with |00⟩
196  DensityMatrix rho(2);
197 
198  // Create Hadamard 2x2 kernel
199  Matrix H_kernel(2, 2);
200  // matrix_base<QGD_Complex16> H_kernel(2, 2);
201  double inv_sqrt2 = 1.0 / std::sqrt(2.0);
202  H_kernel[0] = {inv_sqrt2, 0.0};
203  H_kernel[1] = {inv_sqrt2, 0.0};
204  H_kernel[2] = {inv_sqrt2, 0.0};
205  H_kernel[3] = {-inv_sqrt2, 0.0};
206 
207  // Apply H on qubit 0
208  rho.apply_single_qubit_unitary(H_kernel, 0);
209 
210  // Should create |+0⟩ = (|00⟩ + |01⟩)/√2 in this qubit convention
211  // Qubit 0 is most significant bit: |q0 q1⟩
212  // ρ should have entries at (0,0), (0,1), (1,0), (1,1) = 0.5
213  ASSERT_NEAR(rho(0, 0).real, 0.5, 1e-10);
214  ASSERT_NEAR(rho(0, 1).real, 0.5, 1e-10);
215  ASSERT_NEAR(rho(1, 0).real, 0.5, 1e-10);
216  ASSERT_NEAR(rho(1, 1).real, 0.5, 1e-10);
217 
218  // Still pure
219  ASSERT_NEAR(rho.purity(), 1.0, 1e-10);
220 
221  std::cout << " PASSED" << std::endl;
222  return 0;
223 }
224 
225 // ================================================================
226 // Test 7: apply_two_qubit_unitary (Controlled Gate)
227 // ================================================================
228 
230  std::cout << "Test 7: Two-qubit controlled gate..." << std::flush;
231 
232  // Create |+0⟩ state: H on qubit 0 first
233  DensityMatrix rho(2);
234 
235  Matrix H_kernel(2, 2);
236  double inv_sqrt2 = 1.0 / std::sqrt(2.0);
237  H_kernel[0] = {inv_sqrt2, 0.0};
238  H_kernel[1] = {inv_sqrt2, 0.0};
239  H_kernel[2] = {inv_sqrt2, 0.0};
240  H_kernel[3] = {-inv_sqrt2, 0.0};
241 
242  rho.apply_single_qubit_unitary(H_kernel, 0);
243 
244  // Apply X kernel controlled by qubit 0 on qubit 1 (CNOT-like)
245  Matrix X_kernel(2, 2);
246  X_kernel[0] = {0.0, 0.0};
247  X_kernel[1] = {1.0, 0.0};
248  X_kernel[2] = {1.0, 0.0};
249  X_kernel[3] = {0.0, 0.0};
250 
251  rho.apply_two_qubit_unitary(X_kernel, 1, 0);
252 
253  // Should create Bell state |Φ+⟩ = (|00⟩ + |11⟩)/√2
254  // ρ should have entries at (0,0), (0,3), (3,0), (3,3) = 0.5
255  ASSERT_NEAR(rho(0, 0).real, 0.5, 1e-10);
256  ASSERT_NEAR(rho(0, 3).real, 0.5, 1e-10);
257  ASSERT_NEAR(rho(3, 0).real, 0.5, 1e-10);
258  ASSERT_NEAR(rho(3, 3).real, 0.5, 1e-10);
259 
260  // Still pure
261  ASSERT_NEAR(rho.purity(), 1.0, 1e-10);
262 
263  std::cout << " PASSED" << std::endl;
264  return 0;
265 }
266 
267 // ================================================================
268 // Test 8: GateOperation Wrapper
269 // ================================================================
270 
272  std::cout << "Test 8: GateOperation wrapper..." << std::flush;
273 
274  DensityMatrix rho(2);
275 
276  // Create H gate and wrap it
277  Gate *h_gate = new H(2, 0);
278  GateOperation h_op(h_gate, true);
279 
280  ASSERT_TRUE(h_op.is_unitary());
281  ASSERT_TRUE(h_op.get_name() == "H");
282  ASSERT_TRUE(h_op.get_parameter_num() == 0);
283 
284  // Apply via GateOperation
285  h_op.apply_to_density(nullptr, 0, rho);
286 
287  // Check result (qubit 0 is least-significant in this path)
288  ASSERT_NEAR(rho(0, 0).real, 0.5, 1e-10);
289  ASSERT_NEAR(rho(0, 1).real, 0.5, 1e-10);
290  ASSERT_NEAR(rho.purity(), 1.0, 1e-10);
291 
292  std::cout << " PASSED" << std::endl;
293  return 0;
294 }
295 
296 // ================================================================
297 // Test 9: Depolarizing Noise Operation (Fixed)
298 // ================================================================
299 
301  std::cout << "Test 9: Depolarizing noise (fixed)..." << std::flush;
302 
303  DensityMatrix rho(2);
304 
305  double initial_purity = rho.purity();
306  ASSERT_NEAR(initial_purity, 1.0, 1e-10);
307 
308  // Create fixed depolarizing noise
309  DepolarizingOp noise(2, 0.5); // 50% noise
310 
311  ASSERT_TRUE(!noise.is_unitary());
312  ASSERT_TRUE(noise.get_name() == "Depolarizing");
313  ASSERT_TRUE(noise.get_parameter_num() == 0);
314 
315  noise.apply_to_density(nullptr, 0, rho);
316 
317  // Purity should decrease
318  double final_purity = rho.purity();
319  ASSERT_TRUE(final_purity < initial_purity);
320 
321  // Trace should still be 1
322  QGD_Complex16 tr = rho.trace();
323  ASSERT_NEAR(tr.real, 1.0, 1e-10);
324 
325  std::cout << " PASSED" << std::endl;
326  return 0;
327 }
328 
329 // ================================================================
330 // Test 10: Depolarizing Noise Operation (Parametric)
331 // ================================================================
332 
334  std::cout << "Test 10: Depolarizing noise (parametric)..." << std::flush;
335 
336  DensityMatrix rho(2);
337 
338  // Create parametric depolarizing noise
339  DepolarizingOp noise(2); // Parametric
340 
341  ASSERT_TRUE(noise.is_parametric());
342  ASSERT_TRUE(noise.get_parameter_num() == 1);
343 
344  double p = 0.3;
345  noise.apply_to_density(&p, 1, rho);
346 
347  // Purity should decrease
348  double final_purity = rho.purity();
349  ASSERT_TRUE(final_purity < 1.0);
350 
351  // Trace should still be 1
352  QGD_Complex16 tr = rho.trace();
353  ASSERT_NEAR(tr.real, 1.0, 1e-10);
354 
355  std::cout << " PASSED" << std::endl;
356  return 0;
357 }
358 
359 // ================================================================
360 // Test 11: Amplitude Damping
361 // ================================================================
362 
364  std::cout << "Test 11: Amplitude damping..." << std::flush;
365 
366  // Start with |1⟩ state
367  Matrix psi(2, 1);
368  psi[0] = {0.0, 0.0};
369  psi[1] = {1.0, 0.0};
370  DensityMatrix rho(psi);
371 
372  ASSERT_NEAR(rho(1, 1).real, 1.0, 1e-10);
373 
374  // Apply amplitude damping with γ = 0.5
375  AmplitudeDampingOp noise(0, 0.5);
376 
377  ASSERT_TRUE(!noise.is_unitary());
378  ASSERT_TRUE(noise.get_name() == "AmplitudeDamping");
379 
380  noise.apply_to_density(nullptr, 0, rho);
381 
382  // Population should transfer from |1⟩ to |0⟩
383  // ρ(0,0) should increase, ρ(1,1) should decrease
384  ASSERT_TRUE(rho(0, 0).real > 0.0);
385  ASSERT_TRUE(rho(1, 1).real < 1.0);
386 
387  // Trace should still be 1
388  QGD_Complex16 tr = rho.trace();
389  ASSERT_NEAR(tr.real, 1.0, 1e-10);
390 
391  std::cout << " PASSED" << std::endl;
392  return 0;
393 }
394 
395 // ================================================================
396 // Test 12: Phase Damping
397 // ================================================================
398 
400  std::cout << "Test 12: Phase damping..." << std::flush;
401 
402  // Start with |+⟩ state (has off-diagonal coherence)
403  Matrix psi(2, 1);
404  double inv_sqrt2 = 1.0 / std::sqrt(2.0);
405  psi[0] = {inv_sqrt2, 0.0};
406  psi[1] = {inv_sqrt2, 0.0};
407  DensityMatrix rho(psi);
408 
409  double initial_coherence = std::abs(rho(0, 1).real);
410  ASSERT_NEAR(initial_coherence, 0.5, 1e-10);
411 
412  // Apply phase damping with λ = 0.5
413  PhaseDampingOp noise(0, 0.5);
414 
415  ASSERT_TRUE(!noise.is_unitary());
416  ASSERT_TRUE(noise.get_name() == "PhaseDamping");
417 
418  noise.apply_to_density(nullptr, 0, rho);
419 
420  // Off-diagonal elements should decay
421  double final_coherence = std::abs(rho(0, 1).real);
422  ASSERT_TRUE(final_coherence < initial_coherence);
423 
424  // Diagonal elements should be unchanged
425  ASSERT_NEAR(rho(0, 0).real, 0.5, 1e-10);
426  ASSERT_NEAR(rho(1, 1).real, 0.5, 1e-10);
427 
428  // Trace should still be 1
429  QGD_Complex16 tr = rho.trace();
430  ASSERT_NEAR(tr.real, 1.0, 1e-10);
431 
432  std::cout << " PASSED" << std::endl;
433  return 0;
434 }
435 
436 // ================================================================
437 // Test 13: NoisyCircuit Bell State
438 // ================================================================
439 
441  std::cout << "Test 13: NoisyCircuit Bell state..." << std::flush;
442 
443  NoisyCircuit circuit(2);
444  circuit.add_H(0);
445  circuit.add_CNOT(1, 0);
446 
447  ASSERT_TRUE(circuit.get_qbit_num() == 2);
448  ASSERT_TRUE(circuit.get_operation_count() == 2);
449  ASSERT_TRUE(circuit.get_parameter_num() == 0);
450 
451  DensityMatrix rho(2);
452  circuit.apply_to(nullptr, 0, rho);
453 
454  // Should create Bell state |Φ+⟩ = (|00⟩ + |11⟩)/√2
455  ASSERT_NEAR(rho(0, 0).real, 0.5, 1e-10);
456  ASSERT_NEAR(rho(0, 3).real, 0.5, 1e-10);
457  ASSERT_NEAR(rho(3, 0).real, 0.5, 1e-10);
458  ASSERT_NEAR(rho(3, 3).real, 0.5, 1e-10);
459 
460  // Still pure
461  ASSERT_NEAR(rho.purity(), 1.0, 1e-10);
462 
463  std::cout << " PASSED" << std::endl;
464  return 0;
465 }
466 
467 // ================================================================
468 // Test 14: NoisyCircuit with Fixed Noise
469 // ================================================================
470 
472  std::cout << "Test 14: NoisyCircuit with fixed noise..." << std::flush;
473 
474  NoisyCircuit circuit(2);
475  circuit.add_H(0);
476  circuit.add_CNOT(1, 0);
477  circuit.add_depolarizing(2, 0.1); // Fixed 10% depolarizing
478 
479  ASSERT_TRUE(circuit.get_operation_count() == 3);
480  ASSERT_TRUE(circuit.get_parameter_num() == 0);
481 
482  DensityMatrix rho(2);
483  circuit.apply_to(nullptr, 0, rho);
484 
485  // Should be mixed state (purity < 1)
486  ASSERT_TRUE(rho.purity() < 1.0);
487  ASSERT_TRUE(rho.is_valid());
488 
489  std::cout << " PASSED" << std::endl;
490  return 0;
491 }
492 
493 // ================================================================
494 // Test 15: NoisyCircuit with Parametric Noise
495 // ================================================================
496 
498  std::cout << "Test 15: NoisyCircuit with parametric noise..." << std::flush;
499 
500  NoisyCircuit circuit(2);
501  circuit.add_H(0);
502  circuit.add_CNOT(1, 0);
503  circuit.add_depolarizing(2); // Parametric (1 param)
504 
505  ASSERT_TRUE(circuit.get_operation_count() == 3);
506  ASSERT_TRUE(circuit.get_parameter_num() == 1);
507 
508  DensityMatrix rho(2);
509  double params[1] = {0.2}; // 20% depolarizing
510  circuit.apply_to(params, 1, rho);
511 
512  // Should be mixed state
513  ASSERT_TRUE(rho.purity() < 1.0);
514  ASSERT_TRUE(rho.is_valid());
515 
516  std::cout << " PASSED" << std::endl;
517  return 0;
518 }
519 
520 // ================================================================
521 // Test 16: Mixed Gates and Noise
522 // ================================================================
523 
525  std::cout << "Test 16: Mixed gates and noise..." << std::flush;
526 
527  NoisyCircuit circuit(2);
528  circuit.add_H(0);
529  circuit.add_amplitude_damping(0, 0.1);
530  circuit.add_CNOT(1, 0);
531  circuit.add_phase_damping(1, 0.05);
532  circuit.add_depolarizing(2, 0.02);
533 
534  ASSERT_TRUE(circuit.get_operation_count() == 5);
535  ASSERT_TRUE(circuit.get_parameter_num() == 0);
536 
537  DensityMatrix rho(2);
538  circuit.apply_to(nullptr, 0, rho);
539 
540  // Should be valid mixed state
541  ASSERT_TRUE(rho.is_valid());
542  ASSERT_TRUE(rho.purity() < 1.0);
543 
544  std::cout << " PASSED" << std::endl;
545  return 0;
546 }
547 
548 // ================================================================
549 // Test 17: Operation Info
550 // ================================================================
551 
553  std::cout << "Test 17: Operation info..." << std::flush;
554 
555  NoisyCircuit circuit(2);
556  circuit.add_H(0);
557  circuit.add_CNOT(1, 0);
558  circuit.add_depolarizing(2, 0.1);
559  circuit.add_phase_damping(0);
560 
561  auto info = circuit.get_operation_info();
562 
563  ASSERT_TRUE(info.size() == 4);
564  ASSERT_TRUE(info[0].name == "H");
565  ASSERT_TRUE(info[0].is_unitary == true);
566  ASSERT_TRUE(info[0].param_count == 0);
567 
568  ASSERT_TRUE(info[1].name == "CNOT");
569  ASSERT_TRUE(info[1].is_unitary == true);
570 
571  ASSERT_TRUE(info[2].name == "Depolarizing");
572  ASSERT_TRUE(info[2].is_unitary == false);
573  ASSERT_TRUE(info[2].param_count == 0);
574 
575  ASSERT_TRUE(info[3].name == "PhaseDamping");
576  ASSERT_TRUE(info[3].is_unitary == false);
577  ASSERT_TRUE(info[3].param_count == 1);
578 
579  std::cout << " PASSED" << std::endl;
580  return 0;
581 }
582 
583 // ================================================================
584 // Test 18: Partial Trace
585 // ================================================================
586 
588  std::cout << "Test 18: Partial trace..." << std::flush;
589 
590  // Create Bell state
591  NoisyCircuit circuit(2);
592  circuit.add_H(0);
593  circuit.add_CNOT(1, 0);
594 
595  DensityMatrix rho_full(2);
596  circuit.apply_to(nullptr, 0, rho_full);
597 
598  // Trace out qubit 1
599  std::vector<int> trace_out = {1};
600  DensityMatrix rho_reduced = rho_full.partial_trace(trace_out);
601 
602  // For Bell state, reduced density matrix should be maximally mixed: I/2
603  ASSERT_NEAR(rho_reduced(0, 0).real, 0.5, 1e-10);
604  ASSERT_NEAR(rho_reduced(1, 1).real, 0.5, 1e-10);
605  ASSERT_NEAR(rho_reduced(0, 1).real, 0.0, 1e-10);
606  ASSERT_NEAR(rho_reduced(1, 0).real, 0.0, 1e-10);
607 
608  // Purity should be 0.5 (maximally mixed single qubit)
609  double pur = rho_reduced.purity();
610  ASSERT_NEAR(pur, 0.5, 1e-10);
611 
612  std::cout << " PASSED" << std::endl;
613  return 0;
614 }
615 
616 // ================================================================
617 // Test 19: Clone Operations
618 // ================================================================
619 
621  std::cout << "Test 19: Clone operations..." << std::flush;
622 
623  // Test GateOperation clone
624  Gate *h_gate = new H(2, 0);
625  auto h_op = std::unique_ptr<GateOperation>(new GateOperation(h_gate, true));
626  auto h_clone = h_op->clone();
627 
628  ASSERT_TRUE(h_clone->get_name() == "H");
629  ASSERT_TRUE(h_clone->is_unitary());
630 
631  // Test noise operation clones
632  auto dep_op = std::unique_ptr<DepolarizingOp>(new DepolarizingOp(2, 0.1));
633  auto dep_clone = dep_op->clone();
634 
635  ASSERT_TRUE(dep_clone->get_name() == "Depolarizing");
636  ASSERT_TRUE(!dep_clone->is_unitary());
637 
638  auto amp_op =
639  std::unique_ptr<AmplitudeDampingOp>(new AmplitudeDampingOp(0, 0.2));
640  auto amp_clone = amp_op->clone();
641 
642  ASSERT_TRUE(amp_clone->get_name() == "AmplitudeDamping");
643 
644  std::cout << " PASSED" << std::endl;
645  return 0;
646 }
647 
648 // ================================================================
649 // Test 20: Legacy NoiseChannel Compatibility
650 // ================================================================
651 
653  std::cout << "Test 20: Legacy NoiseChannel compatibility..." << std::flush;
654 
655  DensityMatrix rho(2);
656 
657  // Use legacy interface
658  DepolarizingChannel noise(2, 0.5);
659  noise.apply(rho);
660 
661  // Should work the same
662  ASSERT_TRUE(rho.purity() < 1.0);
663  ASSERT_TRUE(rho.is_valid());
664 
665  std::cout << " PASSED" << std::endl;
666  return 0;
667 }
668 
669 // ================================================================
670 // Main Test Runner
671 // ================================================================
672 
673 int main() {
674  std::cout << "\n========================================" << std::endl;
675  std::cout << " Density Matrix C++ Unit Tests" << std::endl;
676  std::cout << " Approach B: Interface Segregation" << std::endl;
677  std::cout << "========================================\n" << std::endl;
678 
679  int result = 0;
680 
681  // Basic tests
682  result |= test_construction();
683  result |= test_ground_state();
684  result |= test_state_vector_construction();
685  result |= test_maximally_mixed();
686  result |= test_unitary_evolution();
687 
688  // Local kernel application tests
689  result |= test_single_qubit_local();
690  result |= test_two_qubit_controlled();
691 
692  // IDensityOperation interface tests
693  result |= test_gate_operation();
694  result |= test_depolarizing_fixed();
695  result |= test_depolarizing_parametric();
696  result |= test_amplitude_damping();
697  result |= test_phase_damping();
698 
699  // NoisyCircuit tests
700  result |= test_noisy_circuit_bell();
701  result |= test_noisy_circuit_with_noise();
703  result |= test_mixed_gates_and_noise();
704  result |= test_operation_info();
705 
706  // Additional tests
707  result |= test_partial_trace();
708  result |= test_clone_operations();
709  result |= test_legacy_noise_channel();
710 
711  if (result == 0) {
712  std::cout << "\n========================================" << std::endl;
713  std::cout << " All 20 tests PASSED ✓" << std::endl;
714  std::cout << "========================================\n" << std::endl;
715  } else {
716  std::cout << "\n========================================" << std::endl;
717  std::cout << " Some tests FAILED ✗" << std::endl;
718  std::cout << "========================================\n" << std::endl;
719  }
720 
721  return result;
722 }
int test_amplitude_damping()
Definition: test_basic.cpp:363
Header file for a class for the representation of general gate operations.
int test_mixed_gates_and_noise()
Definition: test_basic.cpp:524
void apply(DensityMatrix &rho) override
Apply noise channel to density matrix.
Definition: S.h:11
void add_CNOT(int target, int control)
int get_parameter_num() const override
Get number of free parameters this operation requires.
int main()
Definition: test_basic.cpp:673
int test_noisy_circuit_with_noise()
Definition: test_basic.cpp:471
void add_phase_damping(int target)
Add parametric phase damping noise (1 parameter)
int test_unitary_evolution()
Definition: test_basic.cpp:154
int test_depolarizing_parametric()
Definition: test_basic.cpp:333
int test_partial_trace()
Definition: test_basic.cpp:587
void apply_to(const double *params, int param_count, DensityMatrix &rho)
Apply entire circuit to density matrix.
std::vector< OperationInfo > get_operation_info() const
Get information about all operations.
bool is_unitary() const override
Check if this is a unitary operation (gate) or non-unitary (noise)
int test_gate_operation()
Definition: test_basic.cpp:271
void apply_to_density(const double *params, int param_count, DensityMatrix &rho) override
Apply this operation to the density matrix.
int test_legacy_noise_channel()
Definition: test_basic.cpp:652
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(ρ²)
Depolarizing channel: ρ → (1-p)ρ + p·I/2^n.
Definition: noise_channel.h:66
DensityMatrix partial_trace(const std::vector< int > &trace_out) const
Compute partial trace over specified qubits.
int get_qbit_num() const
Get number of qubits.
Amplitude damping channel (T1 relaxation): |1⟩ → |0⟩ decay.
std::string get_name() const override
Get operation name for debugging/display.
int test_clone_operations()
Definition: test_basic.cpp:620
int test_phase_damping()
Definition: test_basic.cpp:399
void add_depolarizing(int qbit_num)
Add parametric depolarizing noise channel (1 parameter)
int test_two_qubit_controlled()
Definition: test_basic.cpp:229
Header file of complex array storage array with automatic and thread safe reference counting...
Wraps an existing Gate for density matrix operations.
Quantum density matrix ρ for mixed-state representation.
Phase damping channel (T2 dephasing): loss of coherence.
QGD_Complex16 trace() const
Calculate trace: Tr(ρ)
void apply_unitary(const matrix_base< QGD_Complex16 > &U)
Apply unitary transformation: ρ → UρU†
Structure type representing complex numbers in the SQUANDER package.
Definition: QGDTypes.h:38
int get_parameter_num() const
Get total number of parameters needed.
std::string get_name() const override
Get operation name for debugging/display.
int get_qbit_num() const
Get number of qubits.
std::string get_name() const override
Get operation name for debugging/display.
int get_parameter_num() const override
Get number of free parameters this operation requires.
Depolarizing noise channel: ρ → (1-p)ρ + p·I/2^n.
Double-precision complex matrix (float64).
Definition: matrix.h:38
void apply_to_density(const double *params, int param_count, DensityMatrix &rho) override
Apply gate to density matrix using optimized local kernel.
size_t get_operation_count() const
Get number of operations in the circuit.
#define ASSERT_TRUE(cond)
Definition: test_basic.cpp:29
int test_construction()
Definition: test_basic.cpp:39
int test_operation_info()
Definition: test_basic.cpp:552
int test_state_vector_construction()
Definition: test_basic.cpp:94
void apply_to_density(const double *params, int param_count, DensityMatrix &rho) override
Apply this operation to the density matrix.
int get_dim() const
Get matrix dimension (2^qbit_num)
std::string get_name() const override
Get operation name for debugging/display.
int test_depolarizing_fixed()
Definition: test_basic.cpp:300
Definition: noise.py:1
double real
the real part of a complex number
Definition: QGDTypes.h:40
int test_single_qubit_local()
Definition: test_basic.cpp:192
bool is_unitary() const override
Check if this is a unitary operation (gate) or non-unitary (noise)
void apply_to_density(const double *params, int param_count, DensityMatrix &rho) override
Apply this operation to the density matrix.
Quantum circuit with noise for density matrix simulation.
Definition: noisy_circuit.h:57
int test_noisy_circuit_bell()
Definition: test_basic.cpp:440
#define ASSERT_NEAR(a, b, tol)
Definition: test_basic.cpp:22
void add_amplitude_damping(int target)
Add parametric amplitude damping noise (1 parameter)
int test_ground_state()
Definition: test_basic.cpp:56
int test_maximally_mixed()
Definition: test_basic.cpp:126
void apply_single_qubit_unitary(const matrix_base< QGD_Complex16 > &u_2x2, int target_qbit)
Apply single-qubit unitary using local kernel (optimized)
void apply_two_qubit_unitary(const matrix_base< QGD_Complex16 > &u_2x2, int target_qbit, int control_qbit)
Apply two-qubit controlled unitary using local kernel (optimized)
double imag
the imaginary part of a complex number
Definition: QGDTypes.h:42
static DensityMatrix maximally_mixed(int qbit_num)
Create maximally mixed state: ρ = I/2^n.
int test_noisy_circuit_parametric_noise()
Definition: test_basic.cpp:497