Sequential Quantum Gate Decomposer  v1.9.6
Powerful decomposition of general unitarias into one- and two-qubit gates gates
qgd_Variational_Quantum_Eigensolver_Base.py
Go to the documentation of this file.
1 
3 """
4 
5 Created on Fri Jun 26 14:13:26 2020
6 Copyright 2020 Peter Rakyta, Ph.D.
7 
8 Licensed under the Apache License, Version 2.0 (the "License");
9 you may not use this file except in compliance with the License.
10 You may obtain a copy of the License at
11 
12  http://www.apache.org/licenses/LICENSE-2.0
13 
14 Unless required by applicable law or agreed to in writing, software
15 distributed under the License is distributed on an "AS IS" BASIS,
16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 See the License for the specific language governing permissions and
18 limitations under the License.
19 
20 @author: Peter Rakyta, Ph.D.
21 
22 """
23 
24 
26 
27 
28 import numpy as np
29 from os import path
30 from squander.VQA.qgd_Variational_Quantum_Eigensolver_Base_Wrapper import qgd_Variational_Quantum_Eigensolver_Base_Wrapper
31 from squander.gates.qgd_Circuit import qgd_Circuit
32 
33 _VQE_BACKEND_NAME_TO_CODE = {
34  "state_vector": 0,
35  "density_matrix": 1,
36 }
37 _VQE_DEFAULT_BACKEND = "state_vector"
38 _VQE_BACKEND_CONFIG_KEY = "backend_mode"
39 _DENSITY_NOISE_CHANNEL_SPECS = {
40  "depolarizing": ("local_depolarizing", "error_rate"),
41  "local_depolarizing": ("local_depolarizing", "error_rate"),
42  "amplitude_damping": ("amplitude_damping", "gamma"),
43  "phase_damping": ("phase_damping", "lambda"),
44  "dephasing": ("phase_damping", "lambda"),
45 }
46 
47 
49 
50  if backend is None:
51  return _VQE_DEFAULT_BACKEND
52 
53  if not isinstance(backend, str):
54  raise TypeError(
55  "backend should be one of 'state_vector', 'density_matrix', or None"
56  )
57 
58  normalized_backend = backend.strip()
59  if normalized_backend not in _VQE_BACKEND_NAME_TO_CODE:
60  raise ValueError(
61  "Unsupported backend '{}'. Supported backends are 'state_vector' and "
62  "'density_matrix'.".format(backend)
63  )
64 
65  return normalized_backend
66 
67 
68 def _normalize_density_noise_spec(density_noise):
69 
70  if density_noise is None:
71  return []
72 
73  if not isinstance(density_noise, (list, tuple)):
74  raise TypeError("density_noise should be a list or tuple of dictionaries")
75 
76  normalized_density_noise = []
77  for item_idx, noise_item in enumerate(density_noise):
78  if not isinstance(noise_item, dict):
79  raise TypeError(
80  "density_noise[{}] should be a dictionary".format(item_idx)
81  )
82 
83  channel = noise_item.get("channel", noise_item.get("type"))
84  if not isinstance(channel, str):
85  raise TypeError(
86  "density_noise[{}] should define a string 'channel'".format(
87  item_idx
88  )
89  )
90 
91  channel_name = channel.strip()
92  if channel_name not in _DENSITY_NOISE_CHANNEL_SPECS:
93  raise ValueError(
94  "Unsupported density-noise channel '{}'. Supported channels are "
95  "'local_depolarizing', 'amplitude_damping', and "
96  "'phase_damping'.".format(channel)
97  )
98 
99  canonical_channel, value_key = _DENSITY_NOISE_CHANNEL_SPECS[channel_name]
100  raw_value = noise_item.get("value", noise_item.get(value_key))
101  if canonical_channel == "phase_damping" and raw_value is None:
102  raw_value = noise_item.get("lambda_param")
103 
104  if raw_value is None:
105  raise ValueError(
106  "density_noise[{}] is missing the '{}' value".format(
107  item_idx, value_key
108  )
109  )
110 
111  target = noise_item.get("target")
112  after_gate_index = noise_item.get("after_gate_index")
113  if isinstance(target, bool) or not isinstance(target, int):
114  raise TypeError(
115  "density_noise[{}].target should be an integer".format(item_idx)
116  )
117  if isinstance(after_gate_index, bool) or not isinstance(after_gate_index, int):
118  raise TypeError(
119  "density_noise[{}].after_gate_index should be an integer".format(
120  item_idx
121  )
122  )
123 
124  value = float(raw_value)
125  if not np.isfinite(value):
126  raise ValueError(
127  "density_noise[{}] has a non-finite value".format(item_idx)
128  )
129 
130  normalized_density_noise.append(
131  {
132  "channel": canonical_channel,
133  "target": int(target),
134  "after_gate_index": int(after_gate_index),
135  "value": value,
136  }
137  )
138 
139  return normalized_density_noise
140 
141 
142 
143 
145 class qgd_Variational_Quantum_Eigensolver_Base(qgd_Variational_Quantum_Eigensolver_Base_Wrapper):
146 
147 
148 
166  def __init__(
167  self,
168  Hamiltonian,
169  qbit_num,
170  config=None,
171  accelerator_num=0,
172  *,
173  backend=None,
174  density_noise=None,
175  ):
176 
177 
178  if config is None:
179  config = {}
180 
181  # config
182  if not isinstance(config, dict):
183  print("Input parameter config should be a dictionary describing the following hyperparameters:") #TODO
184  return
185 
186  normalized_backend = _normalize_vqe_backend_name(backend)
187  config_copy = dict(config)
188  config_copy[_VQE_BACKEND_CONFIG_KEY] = _VQE_BACKEND_NAME_TO_CODE[normalized_backend]
189 
190  # call the constructor of the wrapper class
191  super(qgd_Variational_Quantum_Eigensolver_Base, self).__init__(Hamiltonian.data, Hamiltonian.indices, Hamiltonian.indptr, qbit_num, config=config_copy, accelerator_num=accelerator_num)
192  self.qbit_num = qbit_num
193  # Keep the selected backend visible on the Python object so tests,
194  # validation harnesses, and reproducibility artifacts can attribute
195  # which execution path was requested.
196  self.backend = normalized_backend
197  self.density_noise = []
198  self.set_Density_Matrix_Noise(density_noise)
199 
200 
201 
204  def set_Optimizer(self, alg):
205 
206  super(qgd_Variational_Quantum_Eigensolver_Base, self).set_Optimizer(alg)
207 
208 
211 
212  # call the C wrapper function
213  super(qgd_Variational_Quantum_Eigensolver_Base, self).Start_Optimization()
214 
215 
219 
220  return super(qgd_Variational_Quantum_Eigensolver_Base, self).get_Optimized_Parameters()
221 
222 
223 
226  def set_Optimized_Parameters(self, new_params):
227 
228  super(qgd_Variational_Quantum_Eigensolver_Base, self).set_Optimized_Parameters(new_params)
229 
230 
231 
232 # TODO should be deleted!
233  def set_Optimization_Tolerance(self, tolerance):
234 
235  super(qgd_Variational_Quantum_Eigensolver_Base, self).set_Optimization_Tolerance(tolerance)
236 
237 
238 
241  def set_Project_Name(self, project_name):
242 
243  super(qgd_Variational_Quantum_Eigensolver_Base, self).set_Project_Name(project_name)
244 
245 
248  def set_Gate_Structure_from_Binary(self, filename):
249 
250  super(qgd_Variational_Quantum_Eigensolver_Base, self).set_Gate_Structure_From_Binary(filename)
251 
252 
253 
256  def set_Ansatz(self, ansatz_new):
257 
258  super(qgd_Variational_Quantum_Eigensolver_Base, self).set_Ansatz(ansatz_new)
259 
260 
264  def Generate_Circuit(self, layers, inner_blocks=1):
265 
266  super(qgd_Variational_Quantum_Eigensolver_Base, self).Generate_Circuit( layers, inner_blocks )
267 
268 
271  def Optimization_Problem(self, parameters):
272 
273  return super(qgd_Variational_Quantum_Eigensolver_Base, self).Optimization_Problem(parameters)
274 
275 
278  def Optimization_Problem_Grad(self, parameters):
279 
280  return super(qgd_Variational_Quantum_Eigensolver_Base, self).Optimization_Problem_Grad(parameters)
281 
282 
288  def get_Second_Renyi_Entropy(self, parameters=None, input_state=None, qubit_list=None ):
289 
290  qbit_num = self.get_Qbit_Num()
291 
292  qubit_list_validated = list()
293  if isinstance(qubit_list, list) or isinstance(qubit_list, tuple):
294  for item in qubit_list:
295  if isinstance(item, int):
296  qubit_list_validated.append(item)
297  qubit_list_validated = list(set(qubit_list_validated))
298  else:
299  print("Elements of qbit_list should be integers")
300  return
301  elif qubit_list == None:
302  qubit_list_validated = [ x for x in range(qbit_num) ]
303 
304  else:
305  print("Elements of qbit_list should be integers")
306  return
307 
308 
309  if parameters is None:
310  print( "get_Second_Renyi_entropy: array of input parameters is None")
311  return None
312 
313 
314  if input_state is None:
315  matrix_size = 1 << qbit_num
316  input_state = np.zeros( (matrix_size,1) )
317  input_state[0] = 1
318 
319  # evaluate the entropy
320  entropy = super(qgd_Variational_Quantum_Eigensolver_Base, self).get_Second_Renyi_Entropy( parameters, input_state, qubit_list_validated)
321 
322 
323  return entropy
324 
325 
326 
329  def get_Qbit_Num(self):
330 
331  return super(qgd_Variational_Quantum_Eigensolver_Base, self).get_Qbit_Num()
332 
333 
334 
336  def get_Parameter_Num( self ):
337 
338  return super(qgd_Variational_Quantum_Eigensolver_Base, self).get_Parameter_Num()
339 
340 
341 
342 #@brief Call to apply the gate operation on the input state
343 #@param parameters_mtx Python array ontaining the parameter set
344 #@param state_to_be_transformed Numpy array storing the state on which the transformation should be applied
345  def apply_to( self, parameters_mtx, state_to_be_transformed):
346 
347  # call the C wrapper function
348  super().apply_to( parameters_mtx, state_to_be_transformed )
349 
350 
351 
354  def get_Circuit( self ):
355 
356  # call the C wrapper function
357  return super().get_Circuit()
358 
359 
360 
361 
365 
366  from squander import Qiskit_IO
367 
368  squander_circuit = self.get_Circuit()
369  parameters = self.get_Optimized_Parameters()
370 
371  return Qiskit_IO.get_Qiskit_Circuit( squander_circuit, parameters )
372 
373 
374 
376  def set_Initial_State( self, initial_state ):
377 
378  super(qgd_Variational_Quantum_Eigensolver_Base, self).set_Initial_State( initial_state )
379 
380 
381 
391  def set_Density_Matrix_Noise(self, density_noise):
392 
393  normalized_density_noise = _normalize_density_noise_spec(density_noise)
394  super(qgd_Variational_Quantum_Eigensolver_Base, self).set_Density_Matrix_Noise(
395  normalized_density_noise
396  )
397  self.density_noise = [dict(item) for item in normalized_density_noise]
398 
399 
400 
405 
406  bridge_metadata = super(
407  qgd_Variational_Quantum_Eigensolver_Base, self
408  ).get_Density_Matrix_Bridge_Metadata()
409  bridge_metadata["density_noise"] = [dict(item) for item in self.density_noise]
410  return bridge_metadata
411 
412 
413 
416  def set_Gate_Structure( self, Gate_structure ):
417 
418  if not isinstance(Gate_structure, qgd_Circuit) :
419  raise Exception("Input parameter Gate_structure should be a an instance of Circuit")
420 
421 
422  return super().set_Gate_Structure( Gate_structure )
423 
424 
425 
def Optimization_Problem_Grad(self, parameters)
Call to evaluate the VQE energy.
def get_Parameter_Num(self)
Call to get the number of free parameters in the gate structure used for the decomposition.
def Generate_Circuit(self, layers, inner_blocks=1)
Call to generate the circuit ansatz.
def Optimization_Problem(self, parameters)
Call to evaluate the VQE energy.
def Start_Optimization(self)
Call to start solving the VQE problem to get the approximation for the ground state.
def set_Initial_State(self, initial_state)
Call to get the number of free parameters in the gate structure used for the decomposition.
def set_Optimizer(self, alg)
Call to set the optimizer used in the VQE process.
def get_Qiskit_Circuit(self)
Export the unitary decomposition into Qiskit format.
def get_Second_Renyi_Entropy(self, parameters=None, input_state=None, qubit_list=None)
Call to get the second Rényi entropy.
A QGD Python interface class for the decomposition of N-qubit unitaries into U3 and CNOT gates...
def get_Optimized_Parameters(self)
Call to get the optimized parameters set in numpy array.
def describe_density_bridge(self)
Return reviewable metadata for the currently supported density bridge.
def set_Gate_Structure_from_Binary(self, filename)
Call to set custom layers to the gate structure that are intended to be used in the decomposition fro...
def get_Circuit(self)
Call to retrieve the incorporated quantum circuit (Squander format)
def set_Project_Name(self, project_name)
Call to set the name of the SQUANDER project.
def set_Density_Matrix_Noise(self, density_noise)
Configure ordered fixed local-noise insertions for the density backend.
def __init__(self, Hamiltonian, qbit_num, config=None, accelerator_num=0, backend=None, density_noise=None)
Constructor of the class.
def get_Qbit_Num(self)
Call to get the number of qubits in the circuit.
def set_Optimized_Parameters(self, new_params)
Call to set the parameters which are used as a starting point in the optimization.
def set_Gate_Structure(self, Gate_structure)
Call to set custom gate structure to used in the decomposition.