Sequential Quantum Gate Decomposer  v1.9.7
Powerful decomposition of general unitarias into one- and two-qubit gates gates
test_QX2.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 """
3 Created on Fri Jun 26 14:42:56 2020
4 Copyright 2020 Peter Rakyta, Ph.D.
5 
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9 
10  http://www.apache.org/licenses/LICENSE-2.0
11 
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see http://www.gnu.org/licenses/.
20 
21 @author: Peter Rakyta, Ph.D.
22 """
23 
25 
26 
27 
28 import numpy as np
29 from scipy.stats import unitary_group
30 
31 from squander import utils
32 
33 
35  """This is a test class of the python iterface to the decompsition classes of the QGD package"""
36 
38  r"""
39  Test a custom QX2 gate structure in a four-qubit decomposition.
40 
41  Both the target and optimizer are seeded because convergence time for
42  this strong numerical test otherwise varies by thousands of optimizer
43  iterations.
44  """
45 
46  from squander import N_Qubit_Decomposition
47 
48  qbit_num = 4
49  matrix_size = 2**qbit_num
50 
51  # Keep both halves of the stochastic workload reproducible.
52  Umtx = unitary_group.rvs(matrix_size, random_state=0)
53  decomp = N_Qubit_Decomposition(
54  Umtx.conj().T,
55  config={
56  "random_seed": 1,
57  "max_outer_iterations": 600,
58  # The explicit iteration cap bounds runtime; do not let the
59  # looser stagnation heuristic pre-empt the 1e-7 tolerance.
60  "convergence_threshold": 0.0,
61  },
62  )
63 
64  reordered_qbits = (2, 3, 1, 0)
65  decomp.Reorder_Qubits(reordered_qbits)
66  decomp.set_Gate_Structure(
67  {
70  }
71  )
72  decomp.set_Max_Layer_Num({4: 60, 3: 16})
73  decomp.set_Optimization_Blocks(20)
74  decomp.set_Optimization_Tolerance(1e-7)
75  decomp.Start_Decomposition()
76 
77  assert decomp.get_Decomposition_Error() < 1e-7
78 
79  revert_qbits = (3, 2, 0, 1)
80  decomp.Reorder_Qubits(revert_qbits)
81  quantum_circuit = decomp.get_Qiskit_Circuit()
82 
83  decomposed_matrix = np.asarray(
84  utils.get_unitary_from_qiskit_circuit(quantum_circuit)
85  )
86  product_matrix = Umtx @ decomposed_matrix.conj().T
87  phase = np.angle(product_matrix[0, 0])
88  product_matrix *= np.exp(-1j * phase)
89  product_matrix = (
90  np.eye(matrix_size) * 2
91  - product_matrix
92  - product_matrix.conj().T
93  )
94  decomposition_error = np.real(np.trace(product_matrix)) / 2
95 
96  assert decomposition_error < 1e-3
97 
99  """Validate every QX2 coupling independently of optimizer convergence."""
100 
101  gate_structure = self.create_custom_gate_structure_QX2(4)
102  cnot_qbits = [
103  tuple(gate.get_Involved_Qbits())
104  for layer in gate_structure.get_Gates()
105  for gate in layer.get_Gates()
106  if gate.get_Name() == "CNOT"
107  ]
108 
109  assert cnot_qbits == [(0, 3), (0, 1), (2, 3)]
110 
111 
112  def create_custom_gate_structure_QX2(self, qbit_num):
113  r"""
114  This method is called to create custom gate structure for the decomposition on IBM QX2
115 
116  """
117 
118  from squander import Circuit
119 
120  # creating an instance of the wrapper class Circuit
121  Circuit_ret = Circuit( qbit_num )
122 
123  disentangle_qbit = qbit_num - 1
124 
125  for qbit in range(0, disentangle_qbit ):
126 
127  # creating an instance of the wrapper class Circuit
128  Layer = Circuit( qbit_num )
129 
130  if qbit == 0:
131 
132  # add U3 fate to the block
133  Layer.add_U3( 0 )
134  Layer.add_U3( disentangle_qbit )
135 
136  # add CNOT gate to the block
137  Layer.add_CNOT( 0, disentangle_qbit)
138 
139  elif qbit == 1:
140 
141  # add U3 fate to the block
142  Layer.add_U3( 0 )
143  Layer.add_U3( 1 )
144 
145  # add CNOT gate to the block
146  Layer.add_CNOT( 0, 1)
147 
148 
149 
150  elif qbit == 2:
151 
152  # add U3 fate to the block
153  Layer.add_U3( 2 )
154  Layer.add_U3( disentangle_qbit )
155 
156  # add CNOT gate to the block
157  Layer.add_CNOT( 2, disentangle_qbit )
158 
159  Circuit_ret.add_Circuit( Layer )
160 
161  return Circuit_ret
def create_custom_gate_structure_QX2(self, qbit_num)
Definition: test_QX2.py:112
A base class to determine the decomposition of an N-qubit unitary into a sequence of CNOT and U3 gate...