Quantum Gate Decomposer  v1.3
Powerful decomposition of almost any unitary into U3 and CNOT gates
U3.cpp
Go to the documentation of this file.
1 /*
2 Created on Fri Jun 26 14:13:26 2020
3 Copyright (C) 2020 Peter Rakyta, Ph.D.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see http://www.gnu.org/licenses/.
17 
18 @author: Peter Rakyta, Ph.D.
19 */
24 #include "qgd/U3.h"
25 
26 
27 
36 U3::U3(int qbit_num_in, int target_qbit_in, bool theta_in, bool phi_in, bool lambda_in) {
37 
38  // number of qubits spanning the matrix of the operation
39  qbit_num = qbit_num_in;
40  // the size of the matrix
42  // A string describing the type of the operation
44 
45 
46  if (target_qbit_in >= qbit_num) {
47  printf("The index of the target qubit is larger than the number of qubits");
48  throw "The index of the target qubit is larger than the number of qubits";
49  }
50  // The index of the qubit on which the operation acts (target_qbit >= 0)
51  target_qbit = target_qbit_in;
52  // The index of the qubit which acts as a control qubit (control_qbit >= 0) in controlled operations
53  control_qbit = -1;
54  // the base indices of the target qubit for state |0>
56  // the base indices of the target qubit for state |1>
58 
59  // logical value indicating whether the matrix creation takes an argument theta
60  theta = theta_in;
61  // logical value indicating whether the matrix creation takes an argument phi
62  phi = phi_in;
63  // logical value indicating whether the matrix creation takes an argument lambda
64  lambda = lambda_in;
65 
66 
67  // The number of free parameters
68  if (theta && !phi && lambda) {
69  parameter_num = 2;
70  }
71 
72  else if (theta && phi && lambda) {
73  parameter_num = 3;
74  }
75 
76  else if (!theta && phi && lambda) {
77  parameter_num = 2;
78  }
79 
80  else if (theta && phi && !lambda) {
81  parameter_num = 2;
82  }
83 
84  else if (!theta && !phi && lambda) {
85  parameter_num = 1;
86  }
87 
88  else if (!theta && phi && !lambda) {
89  parameter_num = 1;
90  }
91 
92  else if (theta && !phi && !lambda) {
93  parameter_num = 1;
94  }
95 
96  else {
97  parameter_num = 0;
98  }
99 
100  // Parameters theta, phi, lambda of the U3 operation after the decomposition of the unitary is done
101  parameters = NULL;
102 
103  // determione the basis indices of the |0> and |1> states of the target qubit
105 
106 }
107 
108 
113 
114  if ( indexes_target_qubit_0 != NULL ) {
116  indexes_target_qubit_0 = NULL;
117  }
118 
119  if ( indexes_target_qubit_1 != NULL ) {
121  indexes_target_qubit_1 = NULL;
122  }
123 
124  if ( parameters != NULL ) {
126  parameters = NULL;
127  }
128 }
129 
130 
131 
137 Matrix
138 U3::get_matrix( const double* parameters ) {
139 
140  // preallocate array for the composite u3 operation
141  Matrix U3_matrix = Matrix(matrix_size, matrix_size);
142 
143  if (theta && !phi && lambda) {
144  // function handle to calculate the operation on the target qubit
146  }
147 
148  else if (theta && phi && lambda) {
149  // function handle to calculate the operation on the target qubit
151  }
152 
153  else if (!theta && phi && lambda) {
154  // function handle to calculate the operation on the target qubit
155  composite_u3_Phi_Lambda( parameters, U3_matrix );
156  }
157 
158  else if (theta && phi && !lambda) {
159  // function handle to calculate the operation on the target qubit
160  composite_u3_Theta_Phi( parameters, U3_matrix );
161  }
162 
163  else if (!theta && !phi && lambda) {
164  // function handle to calculate the operation on the target qubit
165  composite_u3_Lambda( parameters, U3_matrix );
166  }
167 
168  else if (!theta && phi && !lambda) {
169  // function handle to calculate the operation on the target qubit
170  composite_u3_Phi( parameters, U3_matrix );
171  }
172 
173  else if (theta && !phi && !lambda) {
174  // function handle to calculate the operation on the target qubit
175  composite_u3_Theta( parameters, U3_matrix );
176  }
177 
178  else {
179  composite_u3(0, 0, 0, U3_matrix );
180  }
181 
182 
183  return U3_matrix;
184 
185 }
186 
187 
194 int U3::composite_u3_Theta_Phi_Lambda( const double* parameters, Matrix& U3_matrix ) {
195  return composite_u3( parameters[0], parameters[1], parameters[2], U3_matrix );
196 }
197 
204 int U3::composite_u3_Phi_Lambda( const double* parameters, Matrix& U3_matrix ) {
205  return composite_u3( 0, parameters[0], parameters[1], U3_matrix );
206 }
207 
214 int U3::composite_u3_Theta_Lambda( const double* parameters, Matrix& U3_matrix ) {
215  return composite_u3( parameters[0], 0, parameters[1], U3_matrix );
216 }
217 
224 int U3::composite_u3_Theta_Phi( const double* parameters, Matrix& U3_matrix ){
225  return composite_u3( parameters[0], parameters[1], 0, U3_matrix );
226 }
227 
234 int U3::composite_u3_Lambda( const double* parameters, Matrix& U3_matrix ) {
235  return composite_u3( 0, 0, parameters[0], U3_matrix );
236 }
237 
244 int U3::composite_u3_Phi( const double* parameters, Matrix& U3_matrix ) {
245  return composite_u3( 0, parameters[0], 0, U3_matrix );
246 }
247 
254 int U3::composite_u3_Theta( const double* parameters, Matrix& U3_matrix ) {
255  return composite_u3( parameters[0], 0, 0, U3_matrix );
256 }
257 
265 QGD_Complex16* U3::composite_u3(double Theta, double Phi, double Lambda ) {
266 
267  // preallocate array for the composite u3 operation
268  Matrix U3_matrix = Matrix(matrix_size, matrix_size);
269 
270  composite_u3(Theta, Phi, Lambda, U3_matrix );
271 
272  U3_matrix.set_owner(false);
273  return U3_matrix.get_data();
274 
275 }
276 
285 int U3::composite_u3(double Theta, double Phi, double Lambda, Matrix& U3_matrix ) {
286 
287  // set to zero all the elements of the matrix
288  memset(U3_matrix.get_data(), 0, U3_matrix.size()*sizeof(QGD_Complex16) );
289 
290 
291  // get the U3 operation of one qubit
292  Matrix u3_1qbit = calc_one_qubit_u3(Theta, Phi, Lambda );
293 
294  // setting the operation elements
295  for(int idx = 0; idx < matrix_size/2; ++idx)
296  {
297  int element_index;
298 
299  // element |0> -> |0>
300  element_index = indexes_target_qubit_0[idx]*matrix_size + indexes_target_qubit_0[idx];
301  U3_matrix[element_index] = u3_1qbit[0];
302 
303  // element |1> -> |0>
304  element_index = indexes_target_qubit_0[idx]*matrix_size + indexes_target_qubit_1[idx];
305  U3_matrix[element_index] = u3_1qbit[1];
306 
307  // element |0> -> |1>
308  element_index = indexes_target_qubit_1[idx]*matrix_size + indexes_target_qubit_0[idx];
309  U3_matrix[element_index] = u3_1qbit[2];
310 
311  // element |1> -> |1>
312  element_index = indexes_target_qubit_1[idx]*matrix_size + indexes_target_qubit_1[idx];
313  U3_matrix[element_index] = u3_1qbit[3];
314 
315 
316  }
317 
318 
319  return 0;
320 }
321 
322 
327 
328 
329  // fre the previously allocated memories
330  if ( indexes_target_qubit_1 != NULL ) {
332  indexes_target_qubit_1 = NULL;
333  }
334  if ( indexes_target_qubit_0 != NULL ) {
336  indexes_target_qubit_0 = NULL;
337  }
338 
339  indexes_target_qubit_1 = (int*)qgd_calloc(matrix_size/2,sizeof(int), 64);
340  indexes_target_qubit_0 = (int*)qgd_calloc(matrix_size/2,sizeof(int), 64);
341 
342  int target_qbit_power = Power_of_2(target_qbit);
343  int indexes_target_qubit_1_idx = 0;
344  int indexes_target_qubit_0_idx = 0;
345 
346  // generate the reordered basis set
347  for(int idx = 0; idx<matrix_size; ++idx)
348  {
349  int bit = int(idx/target_qbit_power) % 2;
350  if (bit == 0) {
351  indexes_target_qubit_0[indexes_target_qubit_0_idx] = idx;
352  indexes_target_qubit_0_idx++;
353  }
354  else {
355  indexes_target_qubit_1[indexes_target_qubit_1_idx] = idx;
356  indexes_target_qubit_1_idx++;
357  }
358  }
359 
360  /*
361  // print the result
362  for(int idx = 0; idx<matrix_size/2; ++idx) {
363  printf ("base indexes for target bit state 0 and 1 are: %d and %d \n", indexes_target_qubit_0[idx], indexes_target_qubit_1[idx]);
364  }
365  */
366 }
367 
372 void U3::set_qbit_num(int qbit_num_in) {
373  // setting the number of qubits
374  Operation::set_qbit_num(qbit_num_in);
375 
376  // get the base indices of the target qubit
378 
379 }
380 
381 
382 
387 void U3::reorder_qubits( std::vector<int> qbit_list) {
388 
389  Operation::reorder_qubits(qbit_list);
390 
391  // get the base indices of the target qubit
393 
394 }
395 
396 
397 
403  return theta;
404 }
405 
406 
412  return phi;
413 }
414 
420  return lambda;
421 }
422 
423 
424 
432 Matrix U3::calc_one_qubit_u3(double Theta, double Phi, double Lambda ) {
433 
434  Matrix u3_1qbit = Matrix(2,2);
435 
436  double cos_theta = cos(Theta/2);
437  double sin_theta = sin(Theta/2);
438 
439  // the 1,1 element
440  u3_1qbit[0].real = cos_theta;
441  u3_1qbit[0].imag = 0;
442  // the 1,2 element
443  u3_1qbit[1].real = -cos(Lambda)*sin_theta;
444  u3_1qbit[1].imag = -sin(Lambda)*sin_theta;
445  // the 2,1 element
446  u3_1qbit[2].real = cos(Phi)*sin_theta;
447  u3_1qbit[2].imag = sin(Phi)*sin_theta;
448  // the 2,2 element
449  u3_1qbit[3].real = cos(Phi+Lambda)*cos_theta;
450  u3_1qbit[3].imag = sin(Phi+Lambda)*cos_theta;
451 
452 
453  return u3_1qbit;
454 
455 }
456 
457 
463 
464  U3* ret = new U3(qbit_num, target_qbit, theta, phi, lambda);
465 
466  if ( parameters != NULL ) {
468  }
469 
470 
471  return ret;
472 
473 }
474 
475 
476 
483 void U3::set_optimized_parameters(double Theta, double Phi, double Lambda ) {
484 
485  if ( parameters == NULL ) {
486  parameters = (double*)qgd_calloc( 3, sizeof(double), 16 );
487  }
488 
489  memset( parameters, 0, 3*sizeof(double) );
490 
491  parameters[0] = Theta;
492  parameters[1] = Phi;
493  parameters[2] = Lambda;
494 
495 }
496 
497 
502 void U3::get_optimized_parameters(double *parameters_in ) {
503 
504  memcpy( parameters_in, parameters, 3*sizeof(double) );
505 
506 }
A class representing a U3 operation.
Definition: U3.h:35
int composite_u3_Lambda(const double *parameters, Matrix &U3_matrix)
Calculate the matrix of a U3 gate operation corresponding to the given parameters acting on the space...
Definition: U3.cpp:234
int composite_u3_Theta_Lambda(const double *parameters, Matrix &U3_matrix)
Calculate the matrix of a U3 gate operation corresponding to the given parameters acting on the space...
Definition: U3.cpp:214
int * indexes_target_qubit_0
the base indices of the target qubit for state |0>
Definition: U3.h:40
int target_qbit
The index of the qubit on which the operation acts (target_qbit >= 0)
Definition: Operation.h:50
int control_qbit
The index of the qubit which acts as a control qubit (control_qbit >= 0) in controlled operations.
Definition: Operation.h:52
void determine_base_indices()
Determine the base indices corresponding to the target qubit states |0> and |1>
Definition: U3.cpp:326
void * qgd_calloc(size_t element_num, size_t size, size_t alignment)
custom defined memory allocation function.
Definition: common.cpp:38
void qgd_free(void *ptr)
custom defined memory release function.
Definition: common.cpp:66
scalar * get_data()
Call to get the pointer to the stored data.
Definition: matrix_base.h:221
void get_optimized_parameters(double *parameters_in)
Call to get the final optimized parameters of the operation.
Definition: U3.cpp:502
Matrix calc_one_qubit_u3(double Theta, double Phi, double Lambda)
Calculate the matrix of a U3 gate operation corresponding to the given parameters acting on a single ...
Definition: U3.cpp:432
void set_optimized_parameters(double Theta, double Phi, double Lambda)
Call to set the final optimized parameters of the operation.
Definition: U3.cpp:483
int composite_u3_Theta_Phi_Lambda(const double *parameters, Matrix &U3_matrix)
Calculate the matrix of a U3 gate operation corresponding to the given parameters acting on the space...
Definition: U3.cpp:194
int matrix_size
The size N of the NxN matrix associated with the operations.
Definition: Operation.h:54
bool is_theta_parameter()
Call to check whether theta is a free parameter of the gate.
Definition: U3.cpp:402
virtual void reorder_qubits(std::vector< int > qbit_list)
Call to reorder the qubits in the matrix of the operation.
Definition: Operation.cpp:118
void reorder_qubits(std::vector< int > qbit_list)
Call to reorder the qubits in the matrix of the operation.
Definition: U3.cpp:387
U3 * clone()
Call to create a clone of the present class.
Definition: U3.cpp:462
int composite_u3_Phi_Lambda(const double *parameters, Matrix &U3_matrix)
Calculate the matrix of a U3 gate operation corresponding to the given parameters acting on the space...
Definition: U3.cpp:204
operation_type type
The type of the operation (see enumeration operation_type)
Definition: Operation.h:48
U3(int qbit_num_in, int target_qbit_in, bool theta_in, bool phi_in, bool lambda_in)
Constructor of the class.
Definition: U3.cpp:36
QGD_Complex16 * composite_u3(double Theta, double Phi, double Lambda)
Calculate the matrix of a U3 gate operation corresponding corresponding to the given parameters actin...
Definition: U3.cpp:265
int composite_u3_Theta(const double *parameters, Matrix &U3_matrix)
Calculate the matrix of a U3 gate operation corresponding to the given parameters acting on the space...
Definition: U3.cpp:254
int qbit_num
number of qubits spanning the matrix of the operation
Definition: Operation.h:46
bool phi
logical value indicating whether the matrix creation takes an argument phi
Definition: U3.h:46
int * indexes_target_qubit_1
the base indices of the target qubit for state |1>
Definition: U3.h:42
unsigned int parameter_num
the number of free parameters of the operation
Definition: Operation.h:56
void set_owner(bool owner_in)
Call to set the current class instance to be (or not to be) the owner of the stored data array.
Definition: matrix_base.h:295
Structure type representing complex numbers in the QGD package.
Definition: QGDTypes.h:39
int Power_of_2(int n)
Calculates the n-th power of 2.
Definition: common.cpp:81
Matrix get_matrix()
Call to retrieve the operation matrix.
Definition: Operation.cpp:97
Class to store data of complex arrays and its properties.
Definition: matrix.h:12
Header file for a class representing a U3 operation.
bool theta
logical value indicating whether the matrix creation takes an argument theta
Definition: U3.h:44
~U3()
Destructor of the class.
Definition: U3.cpp:112
bool is_lambda_parameter()
Call to check whether Lambda is a free parameter of the gate.
Definition: U3.cpp:419
bool is_phi_parameter()
Call to check whether Phi is a free parameter of the gate.
Definition: U3.cpp:411
bool lambda
logical value indicating whether the matrix creation takes an argument lambda
Definition: U3.h:48
virtual void set_qbit_num(int qbit_num_in)
Set the number of qubits spanning the matrix of the operation.
Definition: Operation.cpp:82
double * parameters
Parameters theta, phi, lambda of the U3 operation after the decomposition of the unitary is done.
Definition: U3.h:50
int composite_u3_Phi(const double *parameters, Matrix &U3_matrix)
Calculate the matrix of a U3 gate operation corresponding to the given parameters acting on the space...
Definition: U3.cpp:244
void set_qbit_num(int qbit_num_in)
Call to set the number of qubits spanning the matrix of the operation.
Definition: U3.cpp:372
size_t size()
Call to get the number of the allocated elements.
Definition: matrix_base.h:374
int composite_u3_Theta_Phi(const double *parameters, Matrix &U3_matrix)
Calculate the matrix of a U3 gate operation corresponding to the given parameters acting on the space...
Definition: U3.cpp:224