Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
gate_operation.cpp
Go to the documentation of this file.
1 /*
2 Copyright 2025 SQUANDER Contributors
3 
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7 
8  http://www.apache.org/licenses/LICENSE-2.0
9 
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16 
17 #include "gate_operation.h"
18 #include "density_matrix.h"
19 #include <cmath>
20 
21 #ifndef M_PI
22 #define M_PI 3.14159265358979323846
23 #endif
24 
25 namespace squander {
26 namespace density {
27 
28 // ================================================================
29 // Constructor & Destructor
30 // ================================================================
31 
32 GateOperation::GateOperation(Gate *gate, bool owns_gate)
33  : gate_(gate), owns_gate_(owns_gate) {
34  if (!gate) {
35  throw std::invalid_argument("GateOperation: gate cannot be null");
36  }
37 }
38 
40  if (owns_gate_ && gate_) {
41  delete gate_;
42  gate_ = nullptr;
43  }
44 }
45 
46 // ================================================================
47 // Move Semantics
48 // ================================================================
49 
51  : gate_(other.gate_), owns_gate_(other.owns_gate_) {
52  other.gate_ = nullptr;
53  other.owns_gate_ = false;
54 }
55 
57  if (this != &other) {
58  if (owns_gate_ && gate_) {
59  delete gate_;
60  }
61  gate_ = other.gate_;
62  owns_gate_ = other.owns_gate_;
63  other.gate_ = nullptr;
64  other.owns_gate_ = false;
65  }
66  return *this;
67 }
68 
69 // ================================================================
70 // Core Operation
71 // ================================================================
72 
73 void GateOperation::apply_to_density(const double *params, int param_count,
74  DensityMatrix &rho) {
75  // Get the small unitary kernel (2x2 for single-qubit, 4x4 for general
76  // two-qubit)
77  Matrix kernel;
78  int num_params = gate_->get_parameter_num();
79 
80  if (num_params == 0) {
81  // Constant gate (H, X, CNOT, etc.) - use no-parameter version
82  Matrix_real empty_params(0, 0);
83  kernel = gate_->gate_kernel(empty_params);
84  } else {
85  // Parametric gate (RZ, U3, etc.) - compute kernel with parameters
86  if (param_count < num_params || params == nullptr) {
87  throw std::runtime_error(
88  "GateOperation::apply_to_density: not enough parameters");
89  }
90 
91  // Get the angles for this gate type
92  double theta_over_2 = 0, phi = 0, lambda = 0;
93 
94  // Extract parameters based on gate type
95  Matrix_real params_mat(const_cast<double *>(params), 1, param_count);
96 
97  // Different gates have different parameter mappings
98  // Most parametric single-qubit gates use theta/2, phi, lambda
99  if (num_params == 1) {
100  // Single parameter gates (RX, RY, RZ, U1)
101  // The parameter typically maps to theta/2 or lambda depending on gate
102  // type Use the gate's own parameter extraction
103  theta_over_2 = params[0] / 2.0;
104  phi = 0;
105  lambda = 0;
106 
107  // For RZ gate, the parameter goes to lambda
108  gate_type type = gate_->get_type();
109  if (type == RZ_OPERATION || type == U1_OPERATION) {
110  theta_over_2 = 0;
111  phi = 0;
112  lambda = params[0];
113  } else if (type == RX_OPERATION) {
114  theta_over_2 = params[0] / 2.0;
115  phi = -M_PI / 2.0;
116  lambda = M_PI / 2.0;
117  } else if (type == RY_OPERATION) {
118  theta_over_2 = params[0] / 2.0;
119  phi = 0;
120  lambda = 0;
121  }
122  } else if (num_params == 2) {
123  // U2 gate: phi, lambda (theta fixed at pi/2)
124  theta_over_2 = M_PI / 4.0;
125  phi = params[0];
126  lambda = params[1];
127  } else if (num_params == 3) {
128  // U3 gate: theta, phi, lambda
129  theta_over_2 = params[0] / 2.0;
130  phi = params[1];
131  lambda = params[2];
132  }
133 
134  kernel = Gate::calc_one_qubit_u3(theta_over_2 * 2, phi, lambda);
135  }
136 
137  // Get target and control qubits
138  int target = gate_->get_target_qbit();
139  int control = gate_->get_control_qbit();
140 
141  // Apply using optimized local method
142  if (control < 0) {
143  // Single-qubit gate
144  rho.apply_single_qubit_unitary(kernel, target);
145  } else {
146  // Two-qubit controlled gate
147  rho.apply_two_qubit_unitary(kernel, target, control);
148  }
149 }
150 
151 // ================================================================
152 // Property Accessors
153 // ================================================================
154 
156  return gate_ ? gate_->get_parameter_num() : 0;
157 }
158 
159 std::string GateOperation::get_name() const {
160  return gate_ ? gate_->get_name() : "Unknown";
161 }
162 
164  return gate_ ? gate_->get_target_qbit() : -1;
165 }
166 
168  return gate_ ? gate_->get_control_qbit() : -1;
169 }
170 
171 // ================================================================
172 // Clone
173 // ================================================================
174 
175 std::unique_ptr<IDensityOperation> GateOperation::clone() const {
176  if (!gate_) {
177  throw std::runtime_error("GateOperation::clone: gate is null");
178  }
179  return std::unique_ptr<GateOperation>(new GateOperation(gate_->clone(), true));
180 }
181 
182 } // namespace density
183 } // namespace squander
int get_control_qbit() const
Get control qubit index (-1 if not a controlled gate)
std::unique_ptr< IDensityOperation > clone() const override
Create a deep copy of this operation.
int get_parameter_num() const override
Get number of free parameters this operation requires.
GateOperation & operator=(const GateOperation &)=delete
virtual Gate * clone()
Call to create a clone of the present class.
Definition: Gate.cpp:1351
#define M_PI
static Matrix calc_one_qubit_u3(double ThetaOver2=0.0, double Phi=0.0, double Lambda=0.0)
Build a 2x2 U3 kernel from angles (theta/2, phi, lambda).
Definition: Gate.cpp:2650
gate_type get_type()
Call to get the type of the operation.
Definition: Gate.cpp:1333
Wraps an existing Gate for density matrix operations.
Quantum density matrix ρ for mixed-state representation.
int get_target_qbit() const
Get target qubit index.
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.
virtual int get_parameter_num()
Call to get the number of free parameters.
Definition: Gate.cpp:1324
virtual Matrix gate_kernel(const Matrix_real &precomputed_sincos)
Compute the gate kernel matrix from precomputed trigonometric values.
Definition: Gate.cpp:2546
int get_target_qbit()
Call to get the index of the target qubit.
Definition: Gate.cpp:1203
Base class for the representation of general gate operations.
Definition: Gate.h:86
std::string get_name()
Call to get the name label of the gate.
Definition: Gate.cpp:2740
std::string get_name() const override
Get operation name for debugging/display.
~GateOperation() override
Destructor - deletes gate if owns_gate was true.
gate_type
Type definition of operation types (also generalized for decomposition classes derived from the class...
Definition: Gate.h:39
GateOperation(Gate *gate, bool owns_gate=true)
Wrap an existing gate.
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)
int get_control_qbit()
Call to get the index of the control qubit.
Definition: Gate.cpp:1211
Class to store data of complex arrays and its properties.
Definition: matrix_real.h:41