Sequential Quantum Gate Decomposer  v1.9.3
Powerful decomposition of general unitarias into one- and two-qubit gates gates
state_preparation_evolutionary.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 @author: Peter Rakyta, Ph.D.
19 """
20 
22 
23 from squander import N_Qubit_State_Preparation_adaptive
24 
25 import numpy as np
26 from qiskit import QuantumCircuit
27 
28 
29 #Here we provide an example to use the SQUANDER package. The following python interface is accessible from version 1.8.8. In this example we use two optimization engines for the sate preparation:
30 
31 # An evolutionary engine called AGENTS
32 # Second order gradient descend algorithm (BFGS)
33 
34 # A state to be generated will be a single column of a unitary
35 
36 # to construct the unitary we load the quantum circuit from a file and retrieve the unitary of the circuit
37 qc = QuantumCircuit.from_qasm_file( '../../benchmarks/IBM/alu-v4_37.qasm')
38 
39 
40 from qiskit import execute
41 from qiskit import Aer
42 import numpy.linalg as LA
43 
44 # test the decomposition of the matrix
45 
46 backend = Aer.get_backend('unitary_simulator')
47 
48 
49 job = execute(qc, backend)
50 
51 result = job.result()
52 
53 
54 Umtx = result.get_unitary(qc)
55 Umtx = np.asarray(Umtx)
56 
57 
58 # now take the first column of the unitary to get the state
59 State = np.ascontiguousarray( Umtx[:,0].copy() )
60 
61 
62 
63 
64 
65 # Firstly we construct a Python map to set hyper-parameters during the gate synthesis.
66 
67 # Python map containing hyper-parameters
68 config = { 'agent_lifetime':1000,
69  'agent_num': 64,
70  'max_inner_iterations_agent': 100000,
71  'max_inner_iterations_compression': 1000,
72  'max_inner_iterations' : 100,
73  'max_inner_iterations_final': 100,
74  'Randomized_Radius': 0.3,
75  'randomized_adaptive_layers': 1,
76  'optimization_tolerance_agent': 1e-8,
77  'optimization_tolerance': 1e-16,
78  'convergence_length': 10}
79 
80 
81 # Next we initialize the decomposition class with the unitary Umtx to be decomposed.
82 
83 # creating a class to decompose the unitary
84 cStatePrep = N_Qubit_State_Preparation_adaptive( State, config=config )
85 
86 # The verbosity of the execution output can be controlled by the function call
87 
88 # setting the verbosity of the decomposition
89 # set -1 to fully suppress verbosity
90 cStatePrep.set_Verbose( 3 )
91 
92 # We construct the initial trial gate structure for the optimization consisting of 2 levels of adaptive layer. (a level is made of qubit_num*(qubit_num-1) two-qubit building blocks if all-to-all connectivity is assumed)
93 
94 # add initial decomposing layers to the gate structure
95 levels = 1
96 for idx in range(levels):
97  cStatePrep.add_Adaptive_Layers()
98 
99 cStatePrep.add_Finalyzing_Layer_To_Gate_Structure()
100 
101 # We can construct an initial parameters set for the optimization by retrieving the number of free parameters. If the initial parameter set is not set, random parameters are used by default.
102 # setting intial parameter set
103 parameter_num = cStatePrep.get_Parameter_Num()
104 #parameters = np.zeros( (parameter_num,1), dtype=np.float64 )
105 parameters = np.random.randn( parameter_num )
106 cStatePrep.set_Optimized_Parameters( parameters )
107 
108 
109 # adding new layer to the decomposition until a threshold reached
110 #The optimization process terminates by either reaching the tolerance 'optimization_tolerance_agent' or by reaching the maximal iteration number 'max_inner_iterations_agent', or if the engines identifies a convergence to a local minimum. The SQUANDER framework enables one to continue the optimization using a different engine. In particular we set a second order gradient descend method 'BFGS' In order to achieve the best performance one can play around with the hyper-parameters in the map 'config'. (Optimization strategy AGENTS is good in avoiding local minima or get through flat areas of the optimization landscape. Then a gradient descend method can be used for faster convergence toward a solution.)
111 
112 for new_layer in range(4):
113 
114  print(' ')
115  print(' ')
116  print(' Adding new layer to the gate structure')
117  print(' ')
118  print(' ')
119 
120 
121  # We can choose between several engines to solve the optimization problem. Here we use an evolutionary based algorithm named 'AGENTS'
122 
123 
124  # setting optimizer
125  cStatePrep.set_Optimizer("AGENTS")
126 
127  # starting the decomposition
128  cStatePrep.get_Initial_Circuit()
129 
130  # store parameters after the evolutionary optimization
131  # (After BFGS converge to local minimum, evolutionary optimization wont make it better)
132  params_AGENTS = cStatePrep.get_Optimized_Parameters()
133 
134  # setting optimizer
135  cStatePrep.set_Optimizer("BFGS")
136 
137  # continue the decomposition with a second optimizer method
138  cStatePrep.get_Initial_Circuit()
139 
140  params_BFGS = cStatePrep.get_Optimized_Parameters()
141  decomp_error = cStatePrep.Optimization_Problem( params_BFGS )
142 
143  if decomp_error <= config['optimization_tolerance_agent']:
144  break
145  else:
146  # restore parameters to the evolutionary ones since BFGS iterates to local minima
147  cStatePrep.set_Optimized_Parameters( params_AGENTS )
148 
149  # add new layer to the decomposition if toleranace was not reached
150  cStatePrep.add_Layer_To_Imported_Gate_Structure()
151 
152 
153 
154 #After solving the optimization problem for the initial gate structure, we can initiate gate compression iterations. (This step can be omited.)
155 
156 
157 # starting compression iterations
158 #cStatePrep.Compress_Circuit()
159 
160 #By finalizing the gate structure we replace the CRY gates with CNOT gates. (CRY gates with small rotation angle are approximately expressed with a single CNOT gate, so further optimization process needs to be initiated.)
161 
162 # finalize the gate structure (replace CRY gates with CNOT gates)
163 cStatePrep.Finalize_Circuit()
164 
165 # Finally, we can retrieve the decomposed quantum circuit in QISKIT format.
166 
167 print(' ')
168 print('Constructing quantum circuit:')
169 print(' ')
170 
171 quantum_circuit = cStatePrep.get_Qiskit_Circuit()
172 
173 print( quantum_circuit )
174 
175