Quantum Gate Decomposer  v1.3
Powerful decomposition of almost any unitary into U3 and CNOT gates
Operation_block.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 */
25 #include "qgd/CNOT.h"
26 #include "qgd/U3.h"
27 #include "qgd/Operation_block.h"
28 
29 
34 
35  // A string describing the type of the operation
37  // number of operation layers
38  layer_num = 0;
39 }
40 
41 
42 
47 Operation_block::Operation_block(int qbit_num_in) : Operation(qbit_num_in) {
48 
49  // A string describing the type of the operation
51  // number of operation layers
52  layer_num = 0;
53 }
54 
55 
60 
62 }
63 
67 void
69 
70  //free the alloctaed memory of the stored operations
71  for(std::vector<Operation*>::iterator it = operations.begin(); it != operations.end(); ++it) {
72 
73  Operation* operation = *it;
74 
75  if (operation->get_type() == CNOT_OPERATION) {
76  CNOT* cnot_operation = static_cast<CNOT*>(operation);
77  delete cnot_operation;
78  }
79  else if (operation->get_type() == U3_OPERATION) {
80 
81  U3* u3_operation = static_cast<U3*>(operation);
82  delete u3_operation;
83 
84  }
85  else if (operation->get_type() == BLOCK_OPERATION) {
86 
87  Operation_block* block_operation = static_cast<Operation_block*>(operation);
88  delete block_operation;
89 
90  }
91  else if (operation->get_type() == GENERAL_OPERATION) {
92  delete operation;
93  }
94  }
95 
96  operations.clear();
97 
98 }
99 
100 
106 Matrix
107 Operation_block::get_matrix( const double* parameters ) {
108 
109  // get the matrices of the operations grouped in the block
110  std::vector<Matrix> operation_mtxs = get_matrices( parameters );
111 
112  // calculate the product of the matrices
113  Matrix block_mtx = reduce_zgemm( operation_mtxs );
114 
115  return block_mtx;
116 
117 
118 }
119 
125 std::vector<Matrix> Operation_block::get_matrices( const double* parameters ) {
126 
127  std::vector<Matrix> matrices;
128 
129 
130  for(std::vector<Operation*>::iterator it = operations.begin(); it != operations.end(); ++it) {
131 
132  Operation* operation = *it;
133  Matrix operation_mtx;
134 
135  if (operation->get_type() == CNOT_OPERATION) {
136  CNOT* cnot_operation = static_cast<CNOT*>(operation);
137  operation_mtx = cnot_operation->get_matrix();
138 
139  }
140  else if (operation->get_type() == U3_OPERATION) {
141 
142  U3* u3_operation = static_cast<U3*>(operation);
143 
144  if (u3_operation->get_parameter_num() == 1 ) {
145  operation_mtx = u3_operation->get_matrix( parameters );
146  parameters = parameters + 1;
147  }
148  else if (u3_operation->get_parameter_num() == 2 ) {
149  operation_mtx = u3_operation->get_matrix( parameters );
150  parameters = parameters + 2;
151  }
152  else if (u3_operation->get_parameter_num() == 3 ) {
153  operation_mtx = u3_operation->get_matrix( parameters );
154  parameters = parameters + 3;
155  }
156  else {
157  printf("The U3 operation has wrong number of parameters");
158  throw "The U3 operation has wrong number of parameters";
159  }
160 
161  }
162  else if (operation->get_type() == GENERAL_OPERATION) {
163  operation_mtx = operation->get_matrix();
164  }
165 
166  matrices.push_back(operation_mtx);
167 
168 
169  }
170 
171  return matrices;
172 
173 }
174 
175 
176 
184 void Operation_block::add_u3_to_end(int target_qbit, bool Theta, bool Phi, bool Lambda) {
185 
186  // create the operation
187  Operation* operation = static_cast<Operation*>(new U3( qbit_num, target_qbit, Theta, Phi, Lambda ));
188 
189  // adding the operation to the end of the list of operations
190  add_operation_to_end( operation );
191 }
192 
200 void Operation_block::add_u3_to_front(int target_qbit, bool Theta, bool Phi, bool Lambda) {
201 
202  // create the operation
203  Operation* operation = static_cast<Operation*>(new U3( qbit_num, target_qbit, Theta, Phi, Lambda ));
204 
205  // adding the operation to the front of the list of operations
206  add_operation_to_front( operation );
207 
208 }
209 
215 void Operation_block::add_cnot_to_end( int control_qbit, int target_qbit) {
216 
217  // new cnot operation
218  Operation* operation = static_cast<Operation*>(new CNOT(qbit_num, control_qbit, target_qbit ));
219 
220  // append the operation to the list
221  add_operation_to_end(operation);
222 
223 }
224 
225 
226 
232 void Operation_block::add_cnot_to_front( int control_qbit, int target_qbit) {
233 
234  // new cnot operation
235  Operation* operation = static_cast<Operation*>(new CNOT(qbit_num, control_qbit, target_qbit ));
236 
237  // put the operation to tghe front of the list
238  add_operation_to_front(operation);
239 
240 }
241 
246 void Operation_block::add_operations_to_end( std::vector<Operation*> operations_in) {
247 
248  for(std::vector<Operation*>::iterator it = operations_in.begin(); it != operations_in.end(); ++it) {
249  add_operation_to_end( *it );
250  }
251 
252 }
253 
254 
259 void Operation_block::add_operations_to_front( std::vector<Operation*> operations_in) {
260 
261  // adding operations in reversed order!!
262  for(std::vector<Operation*>::iterator it = operations_in.end(); it != operations_in.begin(); --it) {
263  add_operation_to_front( *it );
264  }
265 
266 }
267 
268 
274 
275  //set the number of qubit in the operation
276  operation->set_qbit_num( qbit_num );
277 
278  // append the operation to the list
279  operations.push_back(operation);
280 
281 
282  // increase the number of parameters by the number of parameters
284 
285  // increase the number of layers if necessary
286  if (operation->get_type() == BLOCK_OPERATION) {
287  layer_num = layer_num + 1;
288  }
289 
290 }
291 
297 
298 
299  // set the number of qubit in the operation
300  operation->set_qbit_num( qbit_num );
301 
302  operations.insert( operations.begin(), operation);
303 
304  // increase the number of U3 gate parameters by the number of parameters
306 
307  // increase the number of layers if necessary
308  if (operation->get_type() == BLOCK_OPERATION) {
309  layer_num = layer_num + 1;
310  }
311 
312 }
313 
314 
315 
321 
322  gates_num gate_nums;
323 
324  gate_nums.u3 = 0;
325  gate_nums.cnot = 0;
326  gate_nums.general = 0;
327 
328  for(std::vector<Operation*>::iterator it = operations.begin(); it != operations.end(); ++it) {
329  // get the specific operation or block of operations
330  Operation* operation = *it;
331 
332  if (operation->get_type() == BLOCK_OPERATION) {
333  Operation_block* block_operation = static_cast<Operation_block*>(operation);
334  gates_num gate_nums_loc = block_operation->get_gate_nums();
335  gate_nums.u3 = gate_nums.u3 + gate_nums_loc.u3;
336  gate_nums.cnot = gate_nums.cnot + gate_nums_loc.cnot;
337  }
338  else if (operation->get_type() == U3_OPERATION) {
339  gate_nums.u3 = gate_nums.u3 + 1;
340  }
341  else if (operation->get_type() == CNOT_OPERATION) {
342  gate_nums.cnot = gate_nums.cnot + 1;
343  }
344  else if (operation->get_type() == GENERAL_OPERATION) {
345  gate_nums.general = gate_nums.general + 1;
346  }
347 
348  }
349 
350 
351  return gate_nums;
352 
353 }
354 
355 
361  return parameter_num;
362 }
363 
364 
370  return operations.size();
371 }
372 
373 
379 void Operation_block::list_operations( const double* parameters, int start_index ) {
380 
381  printf( "\nThe operations in the list of operations:\n" );
382 
383  int operation_idx = start_index;
384  int parameter_idx = parameter_num;
385 
386  for(int op_idx = operations.size()-1; op_idx>=0; op_idx--) {
387 
388  Operation* operation = operations[op_idx];
389 
390  if (operation->get_type() == CNOT_OPERATION) {
391  CNOT* cnot_operation = static_cast<CNOT*>(operation);
392 
393  printf( "%dth operation: CNOT with control qubit: %d and target qubit: %d\n", operation_idx, cnot_operation->get_control_qbit(), cnot_operation->get_target_qbit() );
394  operation_idx = operation_idx + 1;
395  }
396  else if (operation->get_type() == U3_OPERATION) {
397 
398  // definig the U3 parameters
399  double vartheta;
400  double varphi;
401  double varlambda;
402 
403  // get the inverse parameters of the U3 rotation
404 
405  U3* u3_operation = static_cast<U3*>(operation);
406 
407  if ((u3_operation->get_parameter_num() == 1) && u3_operation->is_theta_parameter()) {
408  vartheta = std::fmod( parameters[parameter_idx-1], 4*M_PI);
409  varphi = 0;
410  varlambda =0;
411  parameter_idx = parameter_idx - 1;
412 
413  }
414  else if ((u3_operation->get_parameter_num() == 1) && u3_operation->is_phi_parameter()) {
415  vartheta = 0;
416  varphi = std::fmod( parameters[ parameter_idx-1 ], 2*M_PI);
417  varlambda =0;
418  parameter_idx = parameter_idx - 1;
419  }
420  else if ((u3_operation->get_parameter_num() == 1) && u3_operation->is_lambda_parameter()) {
421  vartheta = 0;
422  varphi = 0;
423  varlambda = std::fmod( parameters[ parameter_idx-1 ], 2*M_PI);
424  parameter_idx = parameter_idx - 1;
425  }
426  else if ((u3_operation->get_parameter_num() == 2) && u3_operation->is_theta_parameter() && u3_operation->is_phi_parameter() ) {
427  vartheta = std::fmod( parameters[ parameter_idx-2 ], 4*M_PI);
428  varphi = std::fmod( parameters[ parameter_idx-1 ], 2*M_PI);
429  varlambda = 0;
430  parameter_idx = parameter_idx - 2;
431  }
432  else if ((u3_operation->get_parameter_num() == 2) && u3_operation->is_theta_parameter() && u3_operation->is_lambda_parameter() ) {
433  vartheta = std::fmod( parameters[ parameter_idx-2 ], 4*M_PI);
434  varphi = 0;
435  varlambda = std::fmod( parameters[ parameter_idx-1 ], 2*M_PI);
436  parameter_idx = parameter_idx - 2;
437  }
438  else if ((u3_operation->get_parameter_num() == 2) && u3_operation->is_phi_parameter() && u3_operation->is_lambda_parameter() ) {
439  vartheta = 0;
440  varphi = std::fmod( parameters[ parameter_idx-2], 2*M_PI);
441  varlambda = std::fmod( parameters[ parameter_idx-1 ], 2*M_PI);
442  parameter_idx = parameter_idx - 2;
443  }
444  else if ((u3_operation->get_parameter_num() == 3)) {
445  vartheta = std::fmod( parameters[ parameter_idx-3 ], 4*M_PI);
446  varphi = std::fmod( parameters[ parameter_idx-2 ], 2*M_PI);
447  varlambda = std::fmod( parameters[ parameter_idx-1 ], 2*M_PI);
448  parameter_idx = parameter_idx - 3;
449  }
450 
451 // message = message + "U3 on target qubit %d with parameters theta = %f, phi = %f and lambda = %f";
452  printf("%dth operation: U3 on target qubit: %d and with parameters theta = %f, phi = %f and lambda = %f\n", operation_idx, u3_operation->get_target_qbit(), vartheta, varphi, varlambda );
453  operation_idx = operation_idx + 1;
454 
455  }
456  else if (operation->get_type() == BLOCK_OPERATION) {
457  Operation_block* block_operation = static_cast<Operation_block*>(operation);
458  const double* parameters_layer = parameters + parameter_idx - operation->get_parameter_num();
459  block_operation->list_operations( parameters_layer, operation_idx );
460  parameter_idx = parameter_idx - block_operation->get_parameter_num();
461  operation_idx = operation_idx + block_operation->get_operation_num();
462  }
463 
464  }
465 
466 }
467 
468 
473 void Operation_block::reorder_qubits( std::vector<int> qbit_list) {
474 
475  for(std::vector<Operation*>::iterator it = operations.begin(); it != operations.end(); ++it) {
476 
477  Operation* operation = *it;
478 
479  if (operation->get_type() == CNOT_OPERATION) {
480  CNOT* cnot_operation = static_cast<CNOT*>(operation);
481  cnot_operation->reorder_qubits( qbit_list );
482  }
483  else if (operation->get_type() == U3_OPERATION) {
484  U3* u3_operation = static_cast<U3*>(operation);
485  u3_operation->reorder_qubits( qbit_list );
486  }
487  else if (operation->get_type() == BLOCK_OPERATION) {
488  Operation_block* block_operation = static_cast<Operation_block*>(operation);
489  block_operation->reorder_qubits( qbit_list );
490  }
491 
492 
493  }
494 
495 }
496 
497 
498 
503 std::vector<int> Operation_block::get_involved_qubits() {
504 
505  std::vector<int> involved_qbits;
506 
507  int qbit;
508 
509 
510  for(std::vector<Operation*>::iterator it = operations.begin(); it != operations.end(); ++it) {
511 
512  Operation* operation = *it;
513 
514  qbit = operation->get_target_qbit();
515  if (qbit != -1) {
516  add_unique_elelement( involved_qbits, qbit );
517  }
518 
519 
520  qbit = operation->get_control_qbit();
521  if (qbit != -1) {
522  add_unique_elelement( involved_qbits, qbit );
523  }
524 
525  }
526 
527  return involved_qbits;
528 }
529 
530 
535 std::vector<Operation*> Operation_block::get_operations() {
536  return operations;
537 }
538 
539 
545 
546  // getting the list of operations
547  std::vector<Operation*> operations_in = op_block->get_operations();
548 
549  for(std::vector<Operation*>::iterator it = (operations_in).begin(); it != (operations_in).end(); ++it) {
550  Operation* op = *it;
551 
552  if (op->get_type() == CNOT_OPERATION) {
553  CNOT* cnot_op = static_cast<CNOT*>( op );
554  CNOT* cnot_op_cloned = cnot_op->clone();
555  Operation* op_cloned = static_cast<Operation*>( cnot_op_cloned );
556  add_operation_to_end(op_cloned);
557  }
558  else if (op->get_type() == U3_OPERATION) {
559  U3* u3_op = static_cast<U3*>( op );
560  U3* u3_op_cloned = u3_op->clone();
561  Operation* op_cloned = static_cast<Operation*>( u3_op_cloned );
562  add_operation_to_end( op_cloned );
563  }
564  else if (op->get_type() == BLOCK_OPERATION) {
565  Operation_block* block_op = static_cast<Operation_block*>( op );
566  Operation_block* block_op_cloned = block_op->clone();
567  Operation* op_cloned = static_cast<Operation*>( block_op_cloned );
568  add_operation_to_end( op_cloned );
569  }
570  else if (op->get_type() == GENERAL_OPERATION) {
571  Operation* op_cloned = op->clone();
572  add_operation_to_end( op_cloned );
573  }
574 
575  }
576 
577 }
578 
579 
584 void Operation_block::set_qbit_num( int qbit_num_in ) {
585 
586  // setting the number of qubits
587  Operation::set_qbit_num(qbit_num_in);
588 
589  // setting the number of qubit in the operations
590  for(std::vector<Operation*>::iterator it = operations.begin(); it != operations.end(); ++it) {
591  Operation* op = *it;
592 
593  if (op->get_type() == CNOT_OPERATION) {
594  CNOT* cnot_op = static_cast<CNOT*>( op );
595  cnot_op->set_qbit_num( qbit_num_in );
596  }
597  else if (op->get_type() == U3_OPERATION) {
598  U3* u3_op = static_cast<U3*>( op );
599  u3_op->set_qbit_num( qbit_num_in );
600  }
601  else if (op->get_type() == BLOCK_OPERATION) {
602  Operation_block* block_op = static_cast<Operation_block*>( op );
603  block_op->set_qbit_num( qbit_num_in );
604  }
605  else if (op->get_type() == GENERAL_OPERATION) {
606  op->set_qbit_num( qbit_num_in );
607  }
608  }
609 }
610 
611 
617 
618  // creatign new instance of class Operation_block
620 
621  // extracting the operations from the current class
622  if (extract_operations( ret ) != 0 ) {
623  printf("Operation_block::clone(): extracting operations was not succesfull\n");
624  exit(-1);
625  };
626 
627  return ret;
628 
629 }
630 
631 
638 
639  op_block->release_operations();
640 
641  for ( std::vector<Operation*>::iterator it=operations.begin(); it != operations.end(); ++it ) {
642  Operation* op = *it;
643 
644  if (op->get_type() == CNOT_OPERATION) {
645  CNOT* cnot_op = static_cast<CNOT*>( op );
646  CNOT* cnot_op_cloned = cnot_op->clone();
647  Operation* op_cloned = static_cast<Operation*>( cnot_op_cloned );
648  op_block->add_operation_to_end( op_cloned );
649  }
650  else if (op->get_type() == U3_OPERATION) {
651  U3* u3_op = static_cast<U3*>( op );
652  U3* u3_op_cloned = u3_op->clone();
653  Operation* op_cloned = static_cast<Operation*>( u3_op_cloned );
654  op_block->add_operation_to_end( op_cloned );
655  }
656  else if (op->get_type() == BLOCK_OPERATION) {
657  Operation_block* block_op = static_cast<Operation_block*>( op );
658  Operation_block* block_op_cloned = block_op->clone();
659  Operation* op_cloned = static_cast<Operation*>( block_op_cloned );
660  op_block->add_operation_to_end( op_cloned );
661  }
662  else if (op->get_type() == GENERAL_OPERATION) {
663  Operation* op_cloned = op->clone();
664  op_block->add_operation_to_end( op_cloned );
665  }
666 
667  }
668 
669  return 0;
670 
671 }
672 
673 
674 
675 
Operation * clone()
Call to create a clone of the present class.
Definition: Operation.cpp:192
A class representing a U3 operation.
Definition: U3.h:35
void reorder_qubits(std::vector< int > qbit_list)
Call to reorder the qubits in the matrix of the operations.
void add_operations_to_end(std::vector< Operation * > operations_in)
Append a list of operations to the list of operations.
std::vector< Operation * > get_operations()
Call to get the operations stored in the class.
int extract_operations(Operation_block *op_block)
Call to extract the operations stored in the class.
Base class for the representation of one- and two-qubit operations.
Definition: Operation.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
int get_parameter_num()
Call to get the number of free parameters.
Operation_block * clone()
Create a clone of the present class.
std::vector< Matrix > get_matrices(const double *parameters)
Call to get the list of matrix representation of the operations grouped in the block.
int get_target_qbit()
Call to get the index of the target qubit.
Definition: Operation.cpp:149
Structure type conatining numbers of gates.
Definition: QGDTypes.h:47
void list_operations(const double *parameters, int start_index)
Call to print the list of operations stored in the block of operations for a specific set of paramete...
void combine(Operation_block *op_block)
Call to append the operations of an operation block to the current block.
bool is_theta_parameter()
Call to check whether theta is a free parameter of the gate.
Definition: U3.cpp:402
void reorder_qubits(std::vector< int > qbit_list)
Call to reorder the qubits in the matrix of the operation.
Definition: U3.cpp:387
Header file for a class representing a CNOT operation.
U3 * clone()
Call to create a clone of the present class.
Definition: U3.cpp:462
operation_type type
The type of the operation (see enumeration operation_type)
Definition: Operation.h:48
Matrix reduce_zgemm(std::vector< Matrix > &mtxs)
Calculate the product of several square shaped complex matrices stored in a vector.
Definition: common.cpp:297
int u3
The number of U3 gates.
Definition: QGDTypes.h:49
int general
The number of general gates.
Definition: QGDTypes.h:53
void reorder_qubits(std::vector< int > qbit_list)
Call to reorder the qubits in the matrix of the operation.
Definition: CNOT.cpp:180
int qbit_num
number of qubits spanning the matrix of the operation
Definition: Operation.h:46
std::vector< int > get_involved_qubits()
Call to get the qubits involved in the operations stored in the block of operations.
void release_operations()
Call to release the stored operations.
A class responsible for grouping CNOT and U3 operations into layers.
Operation_block()
Deafult constructor of the class.
CNOT * clone()
Call to create a clone of the present class.
Definition: CNOT.cpp:192
virtual ~Operation_block()
Destructor of the class.
unsigned int parameter_num
the number of free parameters of the operation
Definition: Operation.h:56
gates_num get_gate_nums()
Call to get the number of the individual gate types in the list of operations.
A class representing a CNOT operation.
Definition: CNOT.h:36
int layer_num
number of operation layers
void set_qbit_num(int qbit_num)
Call to set the number of qubits spanning the matrix of the operation.
Definition: CNOT.cpp:168
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
std::vector< Operation * > operations
The list of stored operations.
unsigned int get_parameter_num()
Call to get the number of free parameters.
Definition: Operation.cpp:165
Header file for a class representing a U3 operation.
void add_u3_to_end(int target_qbit, bool Theta, bool Phi, bool Lambda)
Append a U3 gate to the list of operations.
void add_operation_to_end(Operation *operation)
Append a general operation to the list of operations.
void add_operation_to_front(Operation *operation)
Add an operation to the front of the list of operations.
operation_type get_type()
Call to get the type of the operation.
Definition: Operation.cpp:174
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
Matrix get_matrix(const double *parameters)
Call to retrieve the operation matrix.
Definition: U3.cpp:138
virtual void set_qbit_num(int qbit_num_in)
Set the number of qubits spanning the matrix of the operation.
Definition: Operation.cpp:82
void add_unique_elelement(std::vector< int > &involved_qbits, int qbit)
Add an integer to a vector of integers if the integer is not already an element of the vector.
Definition: common.cpp:95
int get_operation_num()
Call to get the number of operations grouped in the class.
Matrix get_matrix()
Call to retrieve the operation matrix.
Definition: CNOT.cpp:79
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
int get_control_qbit()
Call to get the index of the control qubit.
Definition: Operation.cpp:157
Header file for a class responsible for grouping CNOT and U3 operations into layers.
void add_cnot_to_end(int control_qbit, int target_qbit)
Append a C_NOT gate operation to the list of operations.
int cnot
The number of CNOT gates.
Definition: QGDTypes.h:51
void set_qbit_num(int qbit_num_in)
Set the number of qubits spanning the matrix of the operations stored in the block of operations.
void add_cnot_to_front(int control_qbit, int target_qbit)
Add a C_NOT gate operation to the front of the list of operations.
void add_u3_to_front(int target_qbit, bool Theta, bool Phi, bool Lambda)
Add a U3 gate to the front of the list of operations.
void add_operations_to_front(std::vector< Operation * > operations_in)
Add an array of operations to the front of the list of operations.