From 6991eb9379312094b22c6970b5b5724e06103eb1 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Mon, 16 Sep 2019 12:04:58 -0400 Subject: [PATCH 01/21] support parameterized circuits --- .../aqua/algorithms/adaptive/qaoa/var_form.py | 5 +- qiskit/aqua/algorithms/adaptive/vqc/vqc.py | 44 ++++++++++++------ qiskit/aqua/algorithms/adaptive/vqe/vqe.py | 46 ++++++++++++++----- qiskit/aqua/components/eigs/eigs.py | 2 + .../components/feature_maps/feature_map.py | 17 +++++++ .../feature_maps/pauli_expansion.py | 13 +++--- .../aqua/components/variational_forms/ry.py | 3 ++ .../aqua/components/variational_forms/ryrz.py | 3 ++ .../components/variational_forms/swaprz.py | 3 ++ .../variational_forms/variational_form.py | 25 ++++++++++ qiskit/aqua/operators/common.py | 19 ++++++-- qiskit/aqua/quantum_instance.py | 39 ++++++++++++---- qiskit/aqua/utils/run_circuits.py | 10 ++-- .../components/variational_forms/uccsd.py | 12 +++-- 14 files changed, 185 insertions(+), 56 deletions(-) diff --git a/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py b/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py index 6828d2b24d..2955ac756c 100644 --- a/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py +++ b/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py @@ -15,10 +15,12 @@ """Global X phases and parameterized problem hamiltonian.""" from functools import reduce -import numpy as np +import numpy as np from qiskit import QuantumRegister, QuantumCircuit +from qiskit.circuit import Parameter from qiskit.quantum_info import Pauli + from qiskit.aqua.operators import WeightedPauliOperator, op_converter # pylint: disable=invalid-name @@ -70,6 +72,7 @@ def __init__(self, cost_operator, p, initial_state=None, mixer_operator=None): raise TypeError('The mixer should be a qiskit.aqua.operators.WeightedPauliOperator ' + 'object, found {} instead'.format(type(mixer_operator))) self._mixer_operator = mixer_operator + self.parameters = [Parameter('x{}'.format(i)) for i in range(self.num_parameters)] def construct_circuit(self, angles): """ construct circuit """ diff --git a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py index 9834c09c4a..df88de07f5 100644 --- a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py +++ b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py @@ -265,6 +265,7 @@ def __init__( self._ret = {} self._feature_map = feature_map self._num_qubits = feature_map.num_qubits + self._parameterized_circuits = None @classmethod def init_params(cls, params, algo_input): @@ -338,27 +339,40 @@ def _get_prediction(self, data, theta): Union(numpy.ndarray or [numpy.ndarray], numpy.ndarray or [numpy.ndarray]): list of NxK array, list of Nx1 array """ - # if self._quantum_instance.is_statevector: - # raise ValueError('Selected backend "{}" is not supported.'.format( - # self._quantum_instance.backend_name)) - - circuits = {} - circuit_id = 0 + circuits = [] num_theta_sets = len(theta) // self._var_form.num_parameters theta_sets = np.split(theta, num_theta_sets) + def _build_parameterized_circuits(): + if self._var_form.is_paramterized_circuit and \ + self._feature_map.is_paramterized_circuit and \ + self._parameterized_circuits is None: + + parameterized_circuits = self.construct_circuit( + self._feature_map.parameters, self._var_form.parameters, + measurement=not self._quantum_instance.is_statevector) + self._parameterized_circuits = \ + self._quantum_instance.transpile(parameterized_circuits)[0] + + _build_parameterized_circuits() for thet in theta_sets: for datum in data: - if self._quantum_instance.is_statevector: - circuit = self.construct_circuit(datum, thet, measurement=False) + if self._var_form.is_paramterized_circuit and \ + self._feature_map.is_paramterized_circuit: + curr_params = {p: datum[i] for i, p in enumerate(self._feature_map.parameters)} + curr_params.update({p: thet[i] for i, p in + enumerate(self._var_form.parameters)}) + circuit = self._parameterized_circuits + circuit = circuit.bind_parameters(curr_params) else: - circuit = self.construct_circuit(datum, thet, measurement=True) - - circuits[circuit_id] = circuit - circuit_id += 1 + circuit = self.construct_circuit( + datum, thet, measurement=not self._quantum_instance.is_statevector) + circuits.append(circuit) - results = self._quantum_instance.execute(list(circuits.values())) + results = self._quantum_instance.execute( + circuits, had_transpiled=self._var_form.is_paramterized_circuit and + self._feature_map.is_paramterized_circuit) circuit_id = 0 predicted_probs = [] @@ -367,7 +381,7 @@ def _get_prediction(self, data, theta): counts = [] for _ in data: if self._quantum_instance.is_statevector: - temp = results.get_statevector(circuits[circuit_id]) + temp = results.get_statevector(circuit_id) outcome_vector = (temp * temp.conj()).real # convert outcome_vector to outcome_dict, where key # is a basis state and value is the count. @@ -379,7 +393,7 @@ def _get_prediction(self, data, theta): bitstr_i = format(i, '0' + str(bitstr_size) + 'b') outcome_dict[bitstr_i] = outcome_vector[i] else: - outcome_dict = results.get_counts(circuits[circuit_id]) + outcome_dict = results.get_counts(circuit_id) counts.append(outcome_dict) circuit_id += 1 diff --git a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py index 6ada2153a8..0c1abb0bf1 100644 --- a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py +++ b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py @@ -127,6 +127,7 @@ def __init__(self, operator, var_form, optimizer, self._aux_operators.append(aux_op) self._auto_conversion = auto_conversion logger.info(self.print_settings()) + self._parameterized_circuits = None @classmethod def init_params(cls, params, algo_input): @@ -369,21 +370,41 @@ def _energy_evaluation(self, parameters): Union(float, list[float]): energy of the hamiltonian of each parameter. """ num_parameter_sets = len(parameters) // self._var_form.num_parameters - circuits = [] parameter_sets = np.split(parameters, num_parameter_sets) mean_energy = [] std_energy = [] - for idx, _ in enumerate(parameter_sets): - parameter = parameter_sets[idx] - circuit = self.construct_circuit( - parameter, - statevector_mode=self._quantum_instance.is_statevector, - use_simulator_operator_mode=self._use_simulator_operator_mode, - circuit_name_prefix=str(idx)) - circuits.append(circuit) + def _build_parameterized_circuits(): + if self._var_form.is_paramterized_circuit and self._parameterized_circuits is None: + parameterized_circuits = self.construct_circuit( + self._var_form.parameters, + statevector_mode=self._quantum_instance.is_statevector, + use_simulator_operator_mode=self._use_simulator_operator_mode) + + self._parameterized_circuits = \ + self._quantum_instance.transpile(parameterized_circuits) + + _build_parameterized_circuits() + circuits = [] + # binding parameters here since the circuits had been transpiled + if self._var_form.is_paramterized_circuit: + for idx, parameter in enumerate(parameter_sets): + curr_param = {p: parameter[i] for i, p in enumerate(self._var_form.parameters)} + for qc in self._parameterized_circuits: + tmp = qc.bind_parameters(curr_param) + tmp.name = str(idx) + tmp.name + circuits.append(tmp) + to_be_simulated_circuits = circuits + else: + for idx, parameter in enumerate(parameter_sets): + circuit = self.construct_circuit( + parameter, + statevector_mode=self._quantum_instance.is_statevector, + use_simulator_operator_mode=self._use_simulator_operator_mode, + circuit_name_prefix=str(idx)) + circuits.append(circuit) + to_be_simulated_circuits = functools.reduce(lambda x, y: x + y, circuits) - to_be_simulated_circuits = functools.reduce(lambda x, y: x + y, circuits) if self._use_simulator_operator_mode: extra_args = { 'expectation': @@ -394,7 +415,10 @@ def _energy_evaluation(self, parameters): } else: extra_args = {} - result = self._quantum_instance.execute(to_be_simulated_circuits, **extra_args) + + result = self._quantum_instance.execute(to_be_simulated_circuits, + self._var_form.is_paramterized_circuit, + **extra_args) for idx, _ in enumerate(parameter_sets): mean, std = self._operator.evaluate_with_result( diff --git a/qiskit/aqua/components/eigs/eigs.py b/qiskit/aqua/components/eigs/eigs.py index 658d0fef3b..0ef6bbaa66 100644 --- a/qiskit/aqua/components/eigs/eigs.py +++ b/qiskit/aqua/components/eigs/eigs.py @@ -83,5 +83,7 @@ def construct_inverse(self, mode, circuit): raise NotImplementedError('The matrix mode is not supported.') if circuit is None: raise ValueError('Circuit was not constructed beforehand.') + # TODO: need to check if circuit is empty, now, it enforce to put a barrier in evolution + # instruction self._inverse = circuit.inverse() return self._inverse diff --git a/qiskit/aqua/components/feature_maps/feature_map.py b/qiskit/aqua/components/feature_maps/feature_map.py index 7735af0c1a..05bbd5b27c 100644 --- a/qiskit/aqua/components/feature_maps/feature_map.py +++ b/qiskit/aqua/components/feature_maps/feature_map.py @@ -36,6 +36,8 @@ def __init__(self): super().__init__() self._num_qubits = 0 self._feature_dimension = 0 + self._is_parameterized_circuit = False + self._parameters = None @classmethod def init_params(cls, params): @@ -78,3 +80,18 @@ def feature_dimension(self): def num_qubits(self): """ returns number of qubits """ return self._num_qubits + + @property + def parameters(self): + """ returns Parameters """ + return self._parameters + + @property + def is_parameterized_circuit(self): + """ returns whether or not the sub-class support parameterized circuit """ + return self._is_parameterized_circuit + + @is_parameterized_circuit.setter + def is_parameterized_circuit(self, new_value): + """ set whether or not the sub-class support parameterized circuit """ + self._is_parameterized_circuit = new_value diff --git a/qiskit/aqua/components/feature_maps/pauli_expansion.py b/qiskit/aqua/components/feature_maps/pauli_expansion.py index 9840d52560..a5c62514b1 100644 --- a/qiskit/aqua/components/feature_maps/pauli_expansion.py +++ b/qiskit/aqua/components/feature_maps/pauli_expansion.py @@ -24,6 +24,7 @@ from qiskit import QuantumCircuit, QuantumRegister from qiskit.quantum_info import Pauli from qiskit.qasm import pi +from qiskit.circuit import Parameter from qiskit.aqua.operators import evolution_instruction from qiskit.aqua.components.feature_maps import FeatureMap, self_product @@ -104,6 +105,8 @@ def __init__(self, feature_dimension, depth=2, entangler_map=None, self._pauli_strings = self._build_subset_paulis_string(paulis) self._data_map_func = data_map_func + self._parameters = [Parameter('f{}'.format(i)) for i in range(self._feature_dimension)] + self._is_parameterized_circuit = True def _build_subset_paulis_string(self, paulis): # fill out the paulis to the number of qubits @@ -137,6 +140,7 @@ def _build_subset_paulis_string(self, paulis): def _extract_data_for_rotation(self, pauli, x): where_non_i = np.where(np.asarray(list(pauli[::-1])) != 'I')[0] + x = np.asarray(x) return x[where_non_i] def construct_circuit(self, x, qr=None, inverse=False): @@ -155,11 +159,7 @@ def construct_circuit(self, x, qr=None, inverse=False): TypeError: invalid input ValueError: invalid input """ - if not isinstance(x, np.ndarray): - raise TypeError("x must be numpy array.") - if x.ndim != 1: - raise ValueError("x must be 1-D array.") - if x.shape[0] != self._num_qubits: + if len(x) != self._num_qubits: raise ValueError("number of qubits and data dimension must be the same.") if qr is None: @@ -172,8 +172,7 @@ def construct_circuit(self, x, qr=None, inverse=False): for pauli in self._pauli_strings: coeff = self._data_map_func(self._extract_data_for_rotation(pauli, x)) p = Pauli.from_label(pauli) - - inst = evolution_instruction([[coeff, p]], 1, 1) + inst = evolution_instruction([[1, p]], coeff, 1) qc.append(inst, qr) qc = qc.decompose() return qc diff --git a/qiskit/aqua/components/variational_forms/ry.py b/qiskit/aqua/components/variational_forms/ry.py index 1f3ad7f452..50fb18d03d 100644 --- a/qiskit/aqua/components/variational_forms/ry.py +++ b/qiskit/aqua/components/variational_forms/ry.py @@ -16,6 +16,7 @@ import numpy as np from qiskit import QuantumRegister, QuantumCircuit +from qiskit.circuit import Parameter from qiskit.aqua.components.variational_forms import VariationalForm @@ -124,6 +125,8 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, self._num_parameters += len(self._entangler_map) * depth self._bounds = [(-np.pi, np.pi)] * self._num_parameters + self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] + self._is_parameterized_circuits = True def construct_circuit(self, parameters, q=None): """ diff --git a/qiskit/aqua/components/variational_forms/ryrz.py b/qiskit/aqua/components/variational_forms/ryrz.py index fbeefcc3bf..d5a6cdde14 100644 --- a/qiskit/aqua/components/variational_forms/ryrz.py +++ b/qiskit/aqua/components/variational_forms/ryrz.py @@ -16,6 +16,7 @@ import numpy as np from qiskit import QuantumRegister, QuantumCircuit +from qiskit.circuit import Parameter from qiskit.aqua.components.variational_forms import VariationalForm @@ -106,6 +107,8 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, # for repeated block self._num_parameters += len(self._entangled_qubits) * depth * 2 self._bounds = [(-np.pi, np.pi)] * self._num_parameters + self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] + self._is_parameterized_circuits = True def construct_circuit(self, parameters, q=None): """ diff --git a/qiskit/aqua/components/variational_forms/swaprz.py b/qiskit/aqua/components/variational_forms/swaprz.py index 958dae6f2f..943db18fc8 100644 --- a/qiskit/aqua/components/variational_forms/swaprz.py +++ b/qiskit/aqua/components/variational_forms/swaprz.py @@ -16,6 +16,7 @@ import numpy as np from qiskit import QuantumRegister, QuantumCircuit +from qiskit.circuit import Parameter from qiskit.aqua.components.variational_forms import VariationalForm @@ -100,6 +101,8 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, # for repeated block self._num_parameters += (len(self._entangled_qubits) + len(self._entangler_map)) * depth self._bounds = [(-np.pi, np.pi)] * self._num_parameters + self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] + self._is_parameterized_circuits = True def construct_circuit(self, parameters, q=None): """ diff --git a/qiskit/aqua/components/variational_forms/variational_form.py b/qiskit/aqua/components/variational_forms/variational_form.py index fd8279fe71..2fa03cd37e 100644 --- a/qiskit/aqua/components/variational_forms/variational_form.py +++ b/qiskit/aqua/components/variational_forms/variational_form.py @@ -37,6 +37,8 @@ def __init__(self): self._num_parameters = 0 self._num_qubits = 0 self._bounds = list() + self._is_parameterized_circuit = False + self._parameters = None pass @classmethod @@ -75,6 +77,29 @@ def num_parameters(self): """ return self._num_parameters + @property + def parameters(self): + """Parameters of the variational form. + + Returns: + list[Parameter]: the Parameter for the variational form. + """ + return self._parameters + + @property + def is_parameterized_circuit(self): + """ Whether or not the sub-class support parameterized circuit. + + Returns: + boolean: indicate the sub-class support parameterized circuit + """ + return self._is_parameterized_circuit + + @is_parameterized_circuit.setter + def is_parameterized_circuit(self, new_value): + """ set whether or not the sub-class support parameterized circuit """ + self._is_parameterized_circuit = new_value + @property def num_qubits(self): """Number of qubits of the variational form. diff --git a/qiskit/aqua/operators/common.py b/qiskit/aqua/operators/common.py index c3fd4121a2..2861cc255a 100644 --- a/qiskit/aqua/operators/common.py +++ b/qiskit/aqua/operators/common.py @@ -21,6 +21,7 @@ from qiskit.quantum_info import Pauli # pylint: disable=unused-import from qiskit import QuantumCircuit, QuantumRegister from qiskit.qasm import pi +from qiskit.circuit import Parameter from qiskit.aqua import AquaError @@ -234,7 +235,7 @@ def evolution_instruction(pauli_list, evo_time, num_time_slices, Args: pauli_list (list([[complex, Pauli]])): The list of pauli terms corresponding to a single time slice to be evolved - evo_time (Union(complex, float)): The evolution time + evo_time (Union(complex, float, Parameter)): The evolution time num_time_slices (int): The number of time slices for the expansion controlled (bool, optional): Controlled circuit or not power (int, optional): The power to which the unitary operator is to be raised @@ -242,7 +243,6 @@ def evolution_instruction(pauli_list, evo_time, num_time_slices, gates when building circuit. shallow_slicing (bool, optional): boolean flag for indicating using shallow qc.data reference repetition for slicing - Returns: InstructionSet: The InstructionSet corresponding to specified evolution. Raises: @@ -313,15 +313,24 @@ def evolution_instruction(pauli_list, evo_time, num_time_slices, # insert Rz gate if top_xyz_pauli_indices[pauli_idx] >= 0: - lam = (2.0 * pauli[0] * evo_time / num_time_slices).real - if not controlled: + # Because Parameter does not support complexity number operation; thus, we do + # the following tricks to generate parameterized instruction. + # We assume the coefficient in the pauli is always real. and can not do imaginary time + # evolution + if isinstance(evo_time, Parameter): + lam = 2.0 * pauli[0] / num_time_slices + lam = lam.real if lam.imag == 0 else lam + lam = lam * evo_time + else: + lam = (2.0 * pauli[0] * evo_time / num_time_slices).real + + if not controlled: if use_basis_gates: qc_slice.u1(lam, state_registers[top_xyz_pauli_indices[pauli_idx]]) else: qc_slice.rz(lam, state_registers[top_xyz_pauli_indices[pauli_idx]]) else: - # unitary_power = (2 ** ctl_idx) if unitary_power is None else unitary_power if use_basis_gates: qc_slice.u1(lam / 2, state_registers[top_xyz_pauli_indices[pauli_idx]]) qc_slice.cx(ancillary_registers[0], diff --git a/qiskit/aqua/quantum_instance.py b/qiskit/aqua/quantum_instance.py index 4fa38ee32e..c7c84a7674 100644 --- a/qiskit/aqua/quantum_instance.py +++ b/qiskit/aqua/quantum_instance.py @@ -20,10 +20,12 @@ import os from qiskit.assembler.run_config import RunConfig +from qiskit import compiler from .aqua_error import AquaError from .utils import CircuitCache from .utils.backend_utils import (is_ibmq_provider, + is_aer_provider, is_statevector_backend, is_simulator_backend, is_local_backend, @@ -242,28 +244,47 @@ def __str__(self): return info - def execute(self, circuits, **kwargs): + def transpile(self, circuits): + """ + A wrapper to transpile circuits to allow algorithm access the transpiled circuits. + Args: + circuits (QuantumCircuit or list[QuantumCircuit]): circuits to transpile + + Returns: + list[QuantumCircuit]: the transpiled circuits, it is always a list even though + the length is one. + """ + transpiled_circuits = compiler.transpile(circuits, self._backend, **self._backend_config, + **self._compile_config) + if not isinstance(transpiled_circuits, list): + transpiled_circuits = [transpiled_circuits] + return transpiled_circuits + + def execute(self, circuits, had_transpiled=False, **kwargs): """ A wrapper to interface with quantum backend. Args: circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute - kwargs (list): additional parameters + had_transpiled (bool, optional): whether or not circuits had been transpiled + kwargs (dict, optional): additional parameters Returns: Result: Result object """ from .utils.run_circuits import (run_qobj, - compile_circuits) + maybe_add_aer_expectation_instruction) from .utils.measurement_error_mitigation import (get_measured_qubits_from_qobj, build_measurement_error_mitigation_qobj) - - qobj = compile_circuits(circuits, self._backend, self._backend_config, - self._compile_config, self._run_config, - show_circuit_summary=self._circuit_summary, - circuit_cache=self._circuit_cache, - **kwargs) + # maybe compile + if not had_transpiled: + circuits = self.transpile(circuits) + + # assemble + qobj = compiler.assemble(circuits, **self._run_config.to_dict()) + if is_aer_provider(self._backend): + qobj = maybe_add_aer_expectation_instruction(qobj, kwargs) if self._meas_error_mitigation_cls is not None: qubit_index = get_measured_qubits_from_qobj(qobj) diff --git a/qiskit/aqua/utils/run_circuits.py b/qiskit/aqua/utils/run_circuits.py index 8ae1410dce..b31e82fe01 100644 --- a/qiskit/aqua/utils/run_circuits.py +++ b/qiskit/aqua/utils/run_circuits.py @@ -93,7 +93,7 @@ def _combine_result_objects(results): # pylint: disable=invalid-name -def _maybe_add_aer_expectation_instruction(qobj, options): +def maybe_add_aer_expectation_instruction(qobj, options): if 'expectation' in options: from qiskit.providers.aer.utils.qobj_utils \ import snapshot_instr, append_instr, get_instr_pos @@ -203,7 +203,7 @@ def compile_circuits(circuits, backend, backend_config=None, compile_config=None compile_config, run_config) if is_aer_provider(backend): - qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) + qobj = maybe_add_aer_expectation_instruction(qobj, kwargs) circuit_cache.cache_circuit(qobj, [circuits[0]], 0) transpiled_circuits = None @@ -218,7 +218,7 @@ def compile_circuits(circuits, backend, backend_config=None, compile_config=None qobj = circuit_cache.load_qobj_from_cache(circuits, 0, run_config=run_config) if is_aer_provider(backend): - qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) + qobj = maybe_add_aer_expectation_instruction(qobj, kwargs) # cache miss, fail gracefully except (TypeError, IndexError, FileNotFoundError, EOFError, AquaError, AttributeError) as e: circuit_cache.try_reusing_qobjs = False # Reusing Qobj didn't work @@ -232,7 +232,7 @@ def compile_circuits(circuits, backend, backend_config=None, compile_config=None qobj, transpiled_circuits = _compile_wrapper(circuits, backend, backend_config, compile_config, run_config) if is_aer_provider(backend): - qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) + qobj = maybe_add_aer_expectation_instruction(qobj, kwargs) try: circuit_cache.cache_circuit(qobj, circuits, 0) except (TypeError, IndexError, AquaError, AttributeError, KeyError): @@ -248,7 +248,7 @@ def compile_circuits(circuits, backend, backend_config=None, compile_config=None backend_config, compile_config, run_config) if is_aer_provider(backend): - qobj = _maybe_add_aer_expectation_instruction(qobj, kwargs) + qobj = maybe_add_aer_expectation_instruction(qobj, kwargs) if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary: logger.debug("==== Before transpiler ====") diff --git a/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py b/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py index d620f7b319..461fb50db0 100644 --- a/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py +++ b/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py @@ -22,6 +22,7 @@ import numpy as np from qiskit import QuantumRegister, QuantumCircuit +from qiskit.circuit import Parameter from qiskit.tools import parallel_map from qiskit.tools.events import TextProgressBar @@ -168,6 +169,8 @@ def __init__(self, num_qubits, depth, num_orbitals, num_particles, self._bounds = [(-np.pi, np.pi) for _ in range(self._num_parameters)] self._logging_construct_circuit = True + self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] + self._is_parameterized_circuit = True def _build_hopping_operators(self): if logger.isEnabledFor(logging.DEBUG): @@ -223,7 +226,7 @@ def construct_circuit(self, parameters, q=None): Args: parameters (numpy.ndarray): circuit parameters - q (QuantumRegister): Quantum Register for the circuit. + q (QuantumRegister, optional): Quantum Register for the circuit. Returns: QuantumCircuit: a quantum circuit with given `parameters` @@ -263,8 +266,11 @@ def construct_circuit(self, parameters, q=None): @staticmethod def _construct_circuit_for_one_excited_operator(qubit_op_and_param, qr, num_time_slices): qubit_op, param = qubit_op_and_param - qc = qubit_op.evolve(state_in=None, evo_time=param * -1j, - num_time_slices=num_time_slices, quantum_registers=qr) + # need to put -1j in the coeff of pauli since the Parameter does not support complex number + qubit_op = qubit_op * -1j + qc = qubit_op.evolve(state_in=None, evo_time=param, + num_time_slices=num_time_slices, + quantum_registers=qr) return qc @property From d433c63daf85afe5a29cb0e3bb2100b5f3b150a2 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Mon, 16 Sep 2019 12:09:56 -0400 Subject: [PATCH 02/21] bug fix --- qiskit/aqua/components/variational_forms/ry.py | 2 +- qiskit/aqua/components/variational_forms/ryrz.py | 2 +- qiskit/aqua/components/variational_forms/swaprz.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/aqua/components/variational_forms/ry.py b/qiskit/aqua/components/variational_forms/ry.py index 50fb18d03d..808b4b0168 100644 --- a/qiskit/aqua/components/variational_forms/ry.py +++ b/qiskit/aqua/components/variational_forms/ry.py @@ -126,7 +126,7 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, self._bounds = [(-np.pi, np.pi)] * self._num_parameters self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] - self._is_parameterized_circuits = True + self._is_parameterized_circuit = True def construct_circuit(self, parameters, q=None): """ diff --git a/qiskit/aqua/components/variational_forms/ryrz.py b/qiskit/aqua/components/variational_forms/ryrz.py index d5a6cdde14..c8f7301533 100644 --- a/qiskit/aqua/components/variational_forms/ryrz.py +++ b/qiskit/aqua/components/variational_forms/ryrz.py @@ -108,7 +108,7 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, self._num_parameters += len(self._entangled_qubits) * depth * 2 self._bounds = [(-np.pi, np.pi)] * self._num_parameters self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] - self._is_parameterized_circuits = True + self._is_paramterized_circuit = True def construct_circuit(self, parameters, q=None): """ diff --git a/qiskit/aqua/components/variational_forms/swaprz.py b/qiskit/aqua/components/variational_forms/swaprz.py index 943db18fc8..c15f3d3a1f 100644 --- a/qiskit/aqua/components/variational_forms/swaprz.py +++ b/qiskit/aqua/components/variational_forms/swaprz.py @@ -102,7 +102,7 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, self._num_parameters += (len(self._entangled_qubits) + len(self._entangler_map)) * depth self._bounds = [(-np.pi, np.pi)] * self._num_parameters self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] - self._is_parameterized_circuits = True + self._is_parameterized_circuit = True def construct_circuit(self, parameters, q=None): """ From 266f6e1d02ae04b61ccf56d71ec5fbd5948a6564 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Mon, 16 Sep 2019 13:08:41 -0400 Subject: [PATCH 03/21] typo fix --- qiskit/aqua/algorithms/adaptive/vqc/vqc.py | 8 ++++---- qiskit/aqua/algorithms/adaptive/vqe/vqe.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py index df88de07f5..b982a95437 100644 --- a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py +++ b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py @@ -358,8 +358,8 @@ def _build_parameterized_circuits(): _build_parameterized_circuits() for thet in theta_sets: for datum in data: - if self._var_form.is_paramterized_circuit and \ - self._feature_map.is_paramterized_circuit: + if self._var_form.is_parameterized_circuit and \ + self._feature_map.is_parameterized_circuit: curr_params = {p: datum[i] for i, p in enumerate(self._feature_map.parameters)} curr_params.update({p: thet[i] for i, p in enumerate(self._var_form.parameters)}) @@ -371,8 +371,8 @@ def _build_parameterized_circuits(): circuits.append(circuit) results = self._quantum_instance.execute( - circuits, had_transpiled=self._var_form.is_paramterized_circuit and - self._feature_map.is_paramterized_circuit) + circuits, had_transpiled=self._var_form.is_parameterized_circuit and + self._feature_map.is_parameterized_circuit) circuit_id = 0 predicted_probs = [] diff --git a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py index 0c1abb0bf1..f14234fdd1 100644 --- a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py +++ b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py @@ -375,7 +375,7 @@ def _energy_evaluation(self, parameters): std_energy = [] def _build_parameterized_circuits(): - if self._var_form.is_paramterized_circuit and self._parameterized_circuits is None: + if self._var_form.is_parameterized_circuit and self._parameterized_circuits is None: parameterized_circuits = self.construct_circuit( self._var_form.parameters, statevector_mode=self._quantum_instance.is_statevector, @@ -387,7 +387,7 @@ def _build_parameterized_circuits(): _build_parameterized_circuits() circuits = [] # binding parameters here since the circuits had been transpiled - if self._var_form.is_paramterized_circuit: + if self._var_form.is_parameterized_circuit: for idx, parameter in enumerate(parameter_sets): curr_param = {p: parameter[i] for i, p in enumerate(self._var_form.parameters)} for qc in self._parameterized_circuits: @@ -417,7 +417,7 @@ def _build_parameterized_circuits(): extra_args = {} result = self._quantum_instance.execute(to_be_simulated_circuits, - self._var_form.is_paramterized_circuit, + self._var_form.is_parameterized_circuit, **extra_args) for idx, _ in enumerate(parameter_sets): From 41d51e8f6807119d706dc9abede4962bdbb21e58 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Mon, 16 Sep 2019 14:03:43 -0400 Subject: [PATCH 04/21] more bug fix --- qiskit/aqua/algorithms/adaptive/qaoa/var_form.py | 1 + qiskit/aqua/algorithms/adaptive/vqc/vqc.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py b/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py index 2955ac756c..db14541a01 100644 --- a/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py +++ b/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py @@ -73,6 +73,7 @@ def __init__(self, cost_operator, p, initial_state=None, mixer_operator=None): + 'object, found {} instead'.format(type(mixer_operator))) self._mixer_operator = mixer_operator self.parameters = [Parameter('x{}'.format(i)) for i in range(self.num_parameters)] + self.is_parameterized_circuit = True def construct_circuit(self, angles): """ construct circuit """ diff --git a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py index b982a95437..eccd1bc02d 100644 --- a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py +++ b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py @@ -345,8 +345,8 @@ def _get_prediction(self, data, theta): theta_sets = np.split(theta, num_theta_sets) def _build_parameterized_circuits(): - if self._var_form.is_paramterized_circuit and \ - self._feature_map.is_paramterized_circuit and \ + if self._var_form.is_parameterized_circuit and \ + self._feature_map.is_parameterized_circuit and \ self._parameterized_circuits is None: parameterized_circuits = self.construct_circuit( From 9def58694658a41244572c94edd6271e7f6ce3a9 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Mon, 16 Sep 2019 15:15:10 -0400 Subject: [PATCH 05/21] more bug fix --- qiskit/aqua/operators/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit/aqua/operators/common.py b/qiskit/aqua/operators/common.py index 2861cc255a..4823d985d5 100644 --- a/qiskit/aqua/operators/common.py +++ b/qiskit/aqua/operators/common.py @@ -21,7 +21,7 @@ from qiskit.quantum_info import Pauli # pylint: disable=unused-import from qiskit import QuantumCircuit, QuantumRegister from qiskit.qasm import pi -from qiskit.circuit import Parameter +from qiskit.circuit import Parameter, ParameterExpression from qiskit.aqua import AquaError @@ -318,7 +318,7 @@ def evolution_instruction(pauli_list, evo_time, num_time_slices, # the following tricks to generate parameterized instruction. # We assume the coefficient in the pauli is always real. and can not do imaginary time # evolution - if isinstance(evo_time, Parameter): + if isinstance(evo_time, Parameter) or isinstance(evo_time, ParameterExpression): lam = 2.0 * pauli[0] / num_time_slices lam = lam.real if lam.imag == 0 else lam lam = lam * evo_time From c64bf9d4cf16de6f99d25207c96e25e7abd25082 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 18 Sep 2019 23:35:08 -0400 Subject: [PATCH 06/21] clean up isinstance --- qiskit/aqua/operators/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/aqua/operators/common.py b/qiskit/aqua/operators/common.py index 4823d985d5..54ff5e08fe 100644 --- a/qiskit/aqua/operators/common.py +++ b/qiskit/aqua/operators/common.py @@ -318,7 +318,7 @@ def evolution_instruction(pauli_list, evo_time, num_time_slices, # the following tricks to generate parameterized instruction. # We assume the coefficient in the pauli is always real. and can not do imaginary time # evolution - if isinstance(evo_time, Parameter) or isinstance(evo_time, ParameterExpression): + if isinstance(evo_time, (Parameter, ParameterExpression)): lam = 2.0 * pauli[0] / num_time_slices lam = lam.real if lam.imag == 0 else lam lam = lam * evo_time From 295b9e88d1911bf8366984c9ce9f859882a850f2 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Thu, 19 Sep 2019 00:26:30 -0400 Subject: [PATCH 07/21] remove circuit cache --- qiskit/aqua/algorithms/adaptive/vqe/vqe.py | 3 - .../aqua/operators/weighted_pauli_operator.py | 9 +- qiskit/aqua/quantum_instance.py | 60 ++--- qiskit/aqua/utils/__init__.py | 2 - .../utils/measurement_error_mitigation.py | 8 +- qiskit/aqua/utils/run_circuits.py | 185 +------------- test/aqua/common.py | 1 - test/aqua/test_amplitude_estimation.py | 8 +- test/aqua/test_caching.py | 228 ------------------ test/aqua/test_qaoa.py | 8 +- test/aqua/test_qgan.py | 4 - test/aqua/test_skip_qobj_validation.py | 13 +- test/aqua/test_vqc.py | 19 +- 13 files changed, 56 insertions(+), 492 deletions(-) delete mode 100644 test/aqua/test_caching.py diff --git a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py index f14234fdd1..e35357f815 100644 --- a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py +++ b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py @@ -462,10 +462,7 @@ def get_optimal_vector(self): qc.add_register(c) qc.barrier(q) qc.measure(q, c) - tmp_cache = self._quantum_instance.circuit_cache - self._quantum_instance._circuit_cache = None ret = self._quantum_instance.execute(qc) - self._quantum_instance._circuit_cache = tmp_cache self._ret['min_vector'] = ret.get_counts(qc) return self._ret['min_vector'] diff --git a/qiskit/aqua/operators/weighted_pauli_operator.py b/qiskit/aqua/operators/weighted_pauli_operator.py index df1f9d60bd..9fe83e3687 100644 --- a/qiskit/aqua/operators/weighted_pauli_operator.py +++ b/qiskit/aqua/operators/weighted_pauli_operator.py @@ -655,8 +655,7 @@ def construct_evaluation_circuit(self, wave_function, statevector_mode, if inst is not None: circuit = wave_function.copy(name=circuit_name_prefix + pauli.to_label()) circuit.append(inst, qr) - # TODO: this decompose is used because of cache - circuits.append(circuit.decompose()) + circuits.append(circuit) else: base_circuit = wave_function.copy() if cr is not None: @@ -671,8 +670,7 @@ def construct_evaluation_circuit(self, wave_function, statevector_mode, for basis, _ in self._basis: circuit = base_circuit.copy(name=circuit_name_prefix + basis.to_label()) circuit.append(instructions[basis.to_label()], qargs=qr, cargs=cr) - # TODO: this decompose is used because of cache - circuits.append(circuit.decompose()) + circuits.append(circuit) return circuits @@ -854,8 +852,7 @@ def evolve(self, state_in=None, evo_time=0, num_time_slices=1, quantum_registers instruction = self.evolve_instruction(evo_time, num_time_slices, expansion_mode, expansion_order) qc.append(instruction, quantum_registers) - # TODO: this decompose is used because of cache - return qc.decompose() + return qc def evolve_instruction(self, evo_time=0, num_time_slices=1, expansion_mode='trotter', expansion_order=1): diff --git a/qiskit/aqua/quantum_instance.py b/qiskit/aqua/quantum_instance.py index c7c84a7674..87f04b88ad 100644 --- a/qiskit/aqua/quantum_instance.py +++ b/qiskit/aqua/quantum_instance.py @@ -18,12 +18,12 @@ import logging import time import os +import warnings from qiskit.assembler.run_config import RunConfig from qiskit import compiler from .aqua_error import AquaError -from .utils import CircuitCache from .utils.backend_utils import (is_ibmq_provider, is_aer_provider, is_statevector_backend, @@ -31,6 +31,7 @@ is_local_backend, is_aer_qasm, support_backend_options) +from .utils.circuit_utils import summarize_circuits logger = logging.getLogger(__name__) @@ -201,23 +202,25 @@ def __init__(self, backend, "and re-build it after that.", self._cals_matrix_refresh_period) # setup others - # TODO: allow an external way to overwrite the setting circuit cache temporally - # when either setup cache via environment variable or constructor, - # the optimization level will be change to 0 to assure cache works. - circuit_cache = None if os.environ.get('QISKIT_AQUA_CIRCUIT_CACHE', False) or circuit_caching: - if optimization_level is None or optimization_level == 0: - self._compile_config['optimization_level'] = 0 - skip_qobj_deepcopy = True if os.environ.get('QISKIT_AQUA_CIRCUIT_CACHE', False) \ - else skip_qobj_deepcopy - circuit_cache = CircuitCache(skip_qobj_deepcopy=skip_qobj_deepcopy, - cache_file=cache_file) - else: - logger.warning('CircuitCache cannot be used with optimization_level %s. ' - 'Caching has been disabled. To re-enable, please set ' - 'optimization_level = 0 or None.', optimization_level) + warnings.warn("circuit_caching will be removed at Qiskit Aqua 0.7+, " + "this setting will be ignored. " + "On the other hand, Qiskit Aqua does support " + "parameterized circuits for adaptive algorithms (e.g. VQE and VQC) " + "to avoid for compiling the circuit with the same topology " + "multiple times", + UserWarning) + if skip_qobj_deepcopy: + warnings.warn("skip_qobj_deepcopy was used along with circuit_caching, and since " + "circuit_cache will be removed at Qiskit Aqua 0.7+, " + "this setting will be ignored, too.", + UserWarning) + if cache_file: + warnings.warn("cache_file was used along with circuit_caching, and since " + "circuit_cache will be removed at Qiskit Aqua 0.7+, " + "this setting will be ignored, too.", + UserWarning) - self._circuit_cache = circuit_cache if is_ibmq_provider(self._backend): if skip_qobj_validation: logger.warning("The skip Qobj validation does not work " @@ -249,7 +252,6 @@ def transpile(self, circuits): A wrapper to transpile circuits to allow algorithm access the transpiled circuits. Args: circuits (QuantumCircuit or list[QuantumCircuit]): circuits to transpile - Returns: list[QuantumCircuit]: the transpiled circuits, it is always a list even though the length is one. @@ -258,6 +260,14 @@ def transpile(self, circuits): **self._compile_config) if not isinstance(transpiled_circuits, list): transpiled_circuits = [transpiled_circuits] + + if logger.isEnabledFor(logging.DEBUG) and self._circuit_summary: + logger.debug("==== Before transpiler ====") + logger.debug(summarize_circuits(circuits)) + if transpiled_circuits is not None: + logger.debug("==== After transpiler ====") + logger.debug(summarize_circuits(transpiled_circuits)) + return transpiled_circuits def execute(self, circuits, had_transpiled=False, **kwargs): @@ -271,6 +281,9 @@ def execute(self, circuits, had_transpiled=False, **kwargs): Returns: Result: Result object + + TODO: Maybe we can combine the circuits for the main ones and calibration circuits before + assembling to the qobj. """ from .utils.run_circuits import (run_qobj, maybe_add_aer_expectation_instruction) @@ -340,6 +353,7 @@ def execute(self, circuits, had_transpiled=False, **kwargs): self._backend_options, self._noise_config, self._skip_qobj_validation, self._job_callback) else: + # insert the calibration circuit into main qobj if the shots are the same qobj.experiments[0:0] = cals_qobj.experiments result = run_qobj(qobj, self._backend, self._qjob_config, self._backend_options, self._noise_config, @@ -504,16 +518,6 @@ def is_local(self): """Return True if backend is a local backend.""" return is_local_backend(self._backend) - @property - def circuit_cache(self): - """ returns circuit cache """ - return self._circuit_cache - - @property - def has_circuit_caching(self): - """ checks if it has circuit cache """ - return self._circuit_cache is not None - @property def skip_qobj_validation(self): """ checks if skip qobj validation """ @@ -545,7 +549,7 @@ def cals_matrix(self, qubit_index=None): Get the stored calibration matrices and its timestamp. Args: - qubit_index (int): the qubit index of corresponding calibration matrix. + qubit_index (list[int]): the qubit index of corresponding calibration matrix. If None, return all stored calibration matrices. Returns: diff --git a/qiskit/aqua/utils/__init__.py b/qiskit/aqua/utils/__init__.py index 809f138456..931e965d56 100644 --- a/qiskit/aqua/utils/__init__.py +++ b/qiskit/aqua/utils/__init__.py @@ -28,7 +28,6 @@ map_label_to_class_name, reduce_dim_to_via_pca) from .qp_solver import optimize_svm from .circuit_factory import CircuitFactory -from .circuit_cache import CircuitCache from .backend_utils import has_ibmq, has_aer __all__ = [ @@ -53,7 +52,6 @@ 'reduce_dim_to_via_pca', 'optimize_svm', 'CircuitFactory', - 'CircuitCache', 'has_ibmq', 'has_aer', ] diff --git a/qiskit/aqua/utils/measurement_error_mitigation.py b/qiskit/aqua/utils/measurement_error_mitigation.py index 87b36f21ec..4a094f5f2e 100644 --- a/qiskit/aqua/utils/measurement_error_mitigation.py +++ b/qiskit/aqua/utils/measurement_error_mitigation.py @@ -16,10 +16,10 @@ import logging +from qiskit import compiler from qiskit.ignis.mitigation.measurement import (complete_meas_cal, CompleteMeasFitter, TensoredMeasFitter) -from .run_circuits import compile_circuits from ..aqua_error import AquaError logger = logging.getLogger(__name__) @@ -121,7 +121,7 @@ def build_measurement_error_mitigation_qobj(qubit_list, fitter_cls, backend, else: raise AquaError("Unknown fitter {}".format(fitter_cls)) - cals_qobj = compile_circuits(meas_calibs_circuits, - backend, backend_config, compile_config, run_config) - + t_meas_calibs_circuits = compiler.transpile(meas_calibs_circuits, backend, + **backend_config, **compile_config) + cals_qobj = compiler.assemble(t_meas_calibs_circuits, backend, **run_config.to_dict()) return cals_qobj, state_labels, circlabel diff --git a/qiskit/aqua/utils/run_circuits.py b/qiskit/aqua/utils/run_circuits.py index b31e82fe01..2abbafc47b 100644 --- a/qiskit/aqua/utils/run_circuits.py +++ b/qiskit/aqua/utils/run_circuits.py @@ -22,13 +22,11 @@ import uuid import numpy as np -from qiskit import compiler from qiskit.providers import BaseBackend, JobStatus, JobError from qiskit.providers.jobstatus import JOB_FINAL_STATES from qiskit.providers.basicaer import BasicAerJob from qiskit.qobj import QasmQobj from qiskit.aqua.aqua_error import AquaError -from qiskit.aqua.utils import summarize_circuits from qiskit.aqua.utils.backend_utils import (is_aer_provider, is_basicaer_provider, is_simulator_backend, @@ -60,21 +58,6 @@ def find_regs_by_name(circuit, name, qreg=True): return found_reg -def _avoid_empty_circuits(circuits): - new_circuits = [] - for qc in circuits: - if not qc: - tmp_q = None - for q in qc.qregs: - tmp_q = q - break - if tmp_q is None: - raise NameError("A QASM without any quantum register is invalid.") - qc.iden(tmp_q[0]) - new_circuits.append(qc) - return new_circuits - - def _combine_result_objects(results): """Temporary helper function. @@ -94,6 +77,14 @@ def _combine_result_objects(results): # pylint: disable=invalid-name def maybe_add_aer_expectation_instruction(qobj, options): + """ + Add aer expectation instruction if `expectation` in the options. + Args: + qobj (QasmQobj): qobj + options (dict): the setting for aer expectation instruction + Returns: + QasmQobj: a mutated qobj with aer expectation instruction inserted + """ if 'expectation' in options: from qiskit.providers.aer.utils.qobj_utils \ import snapshot_instr, append_instr, get_instr_pos @@ -119,14 +110,6 @@ def maybe_add_aer_expectation_instruction(qobj, options): return qobj -def _compile_wrapper(circuits, backend, backend_config, compile_config, run_config): - transpiled_circuits = compiler.transpile(circuits, backend, **backend_config, **compile_config) - if not isinstance(transpiled_circuits, list): - transpiled_circuits = [transpiled_circuits] - qobj = compiler.assemble(transpiled_circuits, **run_config.to_dict()) - return qobj, transpiled_circuits - - def _split_qobj_to_qobjs(qobj, chunk_size): qobjs = [] num_chunks = int(np.ceil(len(qobj.experiments) / chunk_size)) @@ -147,119 +130,6 @@ def _split_qobj_to_qobjs(qobj, chunk_size): return qobjs -def compile_circuits(circuits, backend, backend_config=None, compile_config=None, run_config=None, - show_circuit_summary=False, circuit_cache=None, **kwargs): - """ - An execution wrapper with Qiskit-Terra, with job auto recover capability. - - The autorecovery feature is only applied for non-simulator backend. - This wrapper will try to get the result no matter how long it costs. - - Args: - circuits (QuantumCircuit or list[QuantumCircuit]): circuits to execute - backend (BaseBackend): backend instance - backend_config (dict, optional): configuration for backend - compile_config (dict, optional): configuration for compilation - run_config (RunConfig, optional): configuration for running a circuit - show_circuit_summary (bool, optional): showing the summary of submitted circuits. - circuit_cache (CircuitCache, optional): A CircuitCache to use - when calling compile_and_run_circuits - kwargs (optional): special aer instructions to evaluation the expectation of a hamiltonian - - Returns: - QasmObj: compiled qobj. - - Raises: - ValueError: backend type is wrong or not given - ValueError: no circuit in the circuits - - """ - backend_config = backend_config or {} - compile_config = compile_config or {} - run_config = run_config or {} - - if backend is None or not isinstance(backend, BaseBackend): - raise ValueError('Backend is missing or not an instance of BaseBackend') - - if not isinstance(circuits, list): - circuits = [circuits] - - if not circuits: - raise ValueError("The input circuit is empty.") - - if is_simulator_backend(backend): - circuits = _avoid_empty_circuits(circuits) - - if circuit_cache is not None and circuit_cache.try_reusing_qobjs: - # Check if all circuits are the same length. - # If not, don't try to use the same qobj.experiment for all of them. - if len({len(circ.data) for circ in circuits}) > 1: - circuit_cache.try_reusing_qobjs = False - else: # Try setting up the reusable qobj - # Compile and cache first circuit if cache is empty. - # The load method will try to reuse it - if circuit_cache.qobjs is None: - qobj, _ = _compile_wrapper([circuits[0]], backend, backend_config, - compile_config, run_config) - - if is_aer_provider(backend): - qobj = maybe_add_aer_expectation_instruction(qobj, kwargs) - circuit_cache.cache_circuit(qobj, [circuits[0]], 0) - - transpiled_circuits = None - if circuit_cache is not None and circuit_cache.misses < circuit_cache.allowed_misses: - try: - if circuit_cache.cache_transpiled_circuits: - transpiled_circuits = compiler.transpile(circuits, backend, **backend_config, - **compile_config) - qobj = circuit_cache.load_qobj_from_cache(transpiled_circuits, - 0, run_config=run_config) - else: - qobj = circuit_cache.load_qobj_from_cache(circuits, 0, run_config=run_config) - - if is_aer_provider(backend): - qobj = maybe_add_aer_expectation_instruction(qobj, kwargs) - # cache miss, fail gracefully - except (TypeError, IndexError, FileNotFoundError, EOFError, AquaError, AttributeError) as e: - circuit_cache.try_reusing_qobjs = False # Reusing Qobj didn't work - if circuit_cache.qobjs: - logger.info('Circuit cache miss, recompiling. Cache miss reason: %s', repr(e)) - circuit_cache.misses += 1 - else: - logger.info('Circuit cache is empty, compiling from scratch.') - circuit_cache.clear_cache() - - qobj, transpiled_circuits = _compile_wrapper(circuits, backend, backend_config, - compile_config, run_config) - if is_aer_provider(backend): - qobj = maybe_add_aer_expectation_instruction(qobj, kwargs) - try: - circuit_cache.cache_circuit(qobj, circuits, 0) - except (TypeError, IndexError, AquaError, AttributeError, KeyError): - try: - circuit_cache.cache_transpiled_circuits = True - circuit_cache.cache_circuit(qobj, transpiled_circuits, 0) - except (TypeError, IndexError, AquaError, AttributeError, KeyError) as e: - logger.info('Circuit could not be cached for reason: %s', repr(e)) - logger.info('Transpilation may be too aggressive. Try skipping transpiler.') - - else: - qobj, transpiled_circuits = _compile_wrapper(circuits, backend, - backend_config, compile_config, - run_config) - if is_aer_provider(backend): - qobj = maybe_add_aer_expectation_instruction(qobj, kwargs) - - if logger.isEnabledFor(logging.DEBUG) and show_circuit_summary: - logger.debug("==== Before transpiler ====") - logger.debug(summarize_circuits(circuits)) - if transpiled_circuits is not None: - logger.debug("==== After transpiler ====") - logger.debug(summarize_circuits(transpiled_circuits)) - - return qobj - - def _safe_submit_qobj(qobj, backend, backend_options, noise_config, skip_qobj_validation): # assure get job ids while True: @@ -425,45 +295,6 @@ def run_qobj(qobj, backend, qjob_config=None, backend_options=None, return result -def compile_and_run_circuits(circuits, backend, backend_config=None, - compile_config=None, run_config=None, - qjob_config=None, backend_options=None, - noise_config=None, show_circuit_summary=False, - circuit_cache=None, skip_qobj_validation=False, **kwargs): - """ - An execution wrapper with Qiskit-Terra, with job auto recover capability. - - The autorecovery feature is only applied for non-simulator backend. - This wrapper will try to get the result no matter how long it costs. - - Args: - circuits (Union(QuantumCircuit,list[QuantumCircuit])): circuits to execute - backend (BaseBackend): backend instance - backend_config (Optional(dict)): configuration for backend - compile_config (Optional(dict)): configuration for compilation - run_config (Optional(RunConfig)): configuration for running a circuit - qjob_config (Optional(dict)): configuration for quantum job object - backend_options (Optional(dict)): configuration for simulator - noise_config (Optional(dict)): configuration for noise model - show_circuit_summary (Optional(bool)): showing the summary of submitted circuits. - circuit_cache (Optional(CircuitCache)): A CircuitCache to use when - calling compile_and_run_circuits - skip_qobj_validation (Optional(bool)): Bypass Qobj validation to decrease submission time - kwargs (dict): additional parameters - - Returns: - Result: Result object - - Raises: - AquaError: Any error except for JobError raised by Qiskit Terra - """ - qobjs = compile_circuits(circuits, backend, backend_config, compile_config, run_config, - show_circuit_summary, circuit_cache, **kwargs) - result = run_qobj(qobjs, backend, qjob_config, - backend_options, noise_config, skip_qobj_validation) - return result - - # skip_qobj_validation = True does what backend.run # and aerjob.submit do, but without qobj validation. def run_on_backend(backend, qobj, backend_options=None, diff --git a/test/aqua/common.py b/test/aqua/common.py index d432598798..20cb5f1eac 100644 --- a/test/aqua/common.py +++ b/test/aqua/common.py @@ -36,7 +36,6 @@ class QiskitAquaTestCase(unittest.TestCase): """Helper class that contains common functionality.""" def setUp(self): - os.environ['QISKIT_AQUA_CIRCUIT_CACHE'] = '1' self._started_at = time.time() def tearDown(self): diff --git a/test/aqua/test_amplitude_estimation.py b/test/aqua/test_amplitude_estimation.py index f022a1658a..aa8f3458f1 100644 --- a/test/aqua/test_amplitude_estimation.py +++ b/test/aqua/test_amplitude_estimation.py @@ -91,13 +91,12 @@ def setUp(self): super().setUp() self._statevector = QuantumInstance(backend=BasicAer.get_backend('statevector_simulator'), - circuit_caching=False, seed_simulator=2, seed_transpiler=2) def qasm(shots=100): return QuantumInstance(backend=BasicAer.get_backend('qasm_simulator'), shots=shots, - circuit_caching=False, seed_simulator=2, seed_transpiler=2) + seed_simulator=2, seed_transpiler=2) self._qasm = qasm @@ -211,11 +210,10 @@ def setUp(self): ) self._statevector = QuantumInstance(backend=BasicAer.get_backend('statevector_simulator'), - circuit_caching=False, seed_simulator=2, seed_transpiler=2) self._qasm = QuantumInstance(backend=BasicAer.get_backend('qasm_simulator'), shots=100, - circuit_caching=False, seed_simulator=2, seed_transpiler=2) + seed_simulator=2, seed_transpiler=2) @parameterized.expand([ ['statevector', AmplitudeEstimation(3), @@ -270,12 +268,10 @@ def setUp(self): super().setUp() self._statevector = QuantumInstance(backend=BasicAer.get_backend('statevector_simulator'), - circuit_caching=False, seed_simulator=2, seed_transpiler=2) self._qasm = QuantumInstance(backend=BasicAer.get_backend('qasm_simulator'), shots=100, - circuit_caching=False, seed_simulator=2, seed_transpiler=2) diff --git a/test/aqua/test_caching.py b/test/aqua/test_caching.py deleted file mode 100644 index f3b85a0003..0000000000 --- a/test/aqua/test_caching.py +++ /dev/null @@ -1,228 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" Test Caching """ - -import unittest -import pickle -import tempfile -import os -from test.aqua.common import QiskitAquaTestCase - -import numpy as np -from parameterized import parameterized -from qiskit import BasicAer -from qiskit.qobj import Qobj - -from qiskit.aqua import QuantumInstance, QiskitAqua, aqua_globals -from qiskit.aqua.input import EnergyInput -from qiskit.aqua.components.variational_forms import RY, RYRZ -from qiskit.aqua.components.optimizers import L_BFGS_B -from qiskit.aqua.components.initial_states import Zero -from qiskit.aqua.algorithms.adaptive import VQE -from qiskit.aqua.utils import CircuitCache -from qiskit.aqua.operators import WeightedPauliOperator - - -class TestCaching(QiskitAquaTestCase): - """ Test Caching """ - def setUp(self): - super().setUp() - self.seed = 50 - aqua_globals.random_seed = self.seed - pauli_dict = { - 'paulis': [{"coeff": {"imag": 0.0, "real": -1.052373245772859}, "label": "II"}, - {"coeff": {"imag": 0.0, "real": 0.39793742484318045}, "label": "IZ"}, - {"coeff": {"imag": 0.0, "real": -0.39793742484318045}, "label": "ZI"}, - {"coeff": {"imag": 0.0, "real": -0.01128010425623538}, "label": "ZZ"}, - {"coeff": {"imag": 0.0, "real": 0.18093119978423156}, "label": "XX"} - ] - } - qubit_op = WeightedPauliOperator.from_dict(pauli_dict) - self.algo_input = EnergyInput(qubit_op) - self.reference_vqe_result = None - - def _build_refrence_result(self, backends): - res = {} - os.environ.pop('QISKIT_AQUA_CIRCUIT_CACHE', None) - for backend in backends: - params_no_caching = { - 'algorithm': {'name': 'VQE'}, - 'problem': {'name': 'energy', - 'random_seed': self.seed, - 'circuit_caching': False, - 'skip_qobj_deepcopy': False, - 'skip_qobj_validation': False, - 'circuit_cache_file': None, - }, - 'backend': {'provider': 'qiskit.BasicAer', 'name': backend}, - } - if backend != 'statevector_simulator': - params_no_caching['backend']['shots'] = 1000 - params_no_caching['optimizer'] = {'name': 'SPSA', 'max_trials': 15} - qiskit_aqua = QiskitAqua(params_no_caching, self.algo_input) - res[backend] = qiskit_aqua.run() - os.environ['QISKIT_AQUA_CIRCUIT_CACHE'] = '1' - self.reference_vqe_result = res - - @parameterized.expand([ - ['statevector_simulator', True, True], - ['qasm_simulator', True, True], - ['statevector_simulator', True, False], - ['qasm_simulator', True, False], - ]) - def test_vqe_caching_via_run_algorithm(self, backend, caching, skip_qobj_deepcopy): - """ VQE Caching Via Run Algorithm test """ - self._build_refrence_result(backends=[backend]) - skip_validation = True - params_caching = { - 'algorithm': {'name': 'VQE'}, - 'problem': {'name': 'energy', - 'random_seed': self.seed, - 'circuit_caching': caching, - 'skip_qobj_deepcopy': skip_qobj_deepcopy, - 'skip_qobj_validation': skip_validation, - 'circuit_cache_file': None, - }, - 'backend': {'provider': 'qiskit.BasicAer', 'name': backend}, - } - if backend != 'statevector_simulator': - params_caching['backend']['shots'] = 1000 - params_caching['optimizer'] = {'name': 'SPSA', 'max_trials': 15} - qiskit_aqua = QiskitAqua(params_caching, self.algo_input) - result_caching = qiskit_aqua.run() - - self.assertAlmostEqual(result_caching['energy'], - self.reference_vqe_result[backend]['energy']) - - np.testing.assert_array_almost_equal(self.reference_vqe_result[backend]['eigvals'], - result_caching['eigvals'], 5) - np.testing.assert_array_almost_equal(self.reference_vqe_result[backend]['opt_params'], - result_caching['opt_params'], 5) - if qiskit_aqua.quantum_instance.has_circuit_caching: - self.assertEqual(qiskit_aqua.quantum_instance._circuit_cache.misses, 0) - self.assertIn('eval_count', result_caching) - self.assertIn('eval_time', result_caching) - - @parameterized.expand([ - [4], - [1] - ]) - def test_vqe_caching_direct(self, max_evals_grouped): - """ VQE Caching Direct test """ - self._build_refrence_result(backends=['statevector_simulator']) - backend = BasicAer.get_backend('statevector_simulator') - num_qubits = self.algo_input.qubit_op.num_qubits - init_state = Zero(num_qubits) - var_form = RY(num_qubits, 3, initial_state=init_state) - optimizer = L_BFGS_B() - algo = VQE(self.algo_input.qubit_op, - var_form, - optimizer, - max_evals_grouped=max_evals_grouped) - quantum_instance_caching = QuantumInstance(backend, - circuit_caching=True, - skip_qobj_deepcopy=True, - skip_qobj_validation=True, - seed_simulator=self.seed, - seed_transpiler=self.seed) - result_caching = algo.run(quantum_instance_caching) - self.assertLessEqual(quantum_instance_caching.circuit_cache.misses, 0) - self.assertAlmostEqual(self.reference_vqe_result['statevector_simulator']['energy'], - result_caching['energy']) - speedup_min = 3 - speedup = result_caching['eval_time'] / \ - self.reference_vqe_result['statevector_simulator']['eval_time'] - self.assertLess(speedup, speedup_min) - - def test_saving_and_loading_e2e(self): - """ Saving And Loading e to e test """ - backend = BasicAer.get_backend('statevector_simulator') - num_qubits = self.algo_input.qubit_op.num_qubits - init_state = Zero(num_qubits) - var_form = RY(num_qubits, 1, initial_state=init_state) - optimizer = L_BFGS_B(maxiter=10) - algo = VQE(self.algo_input.qubit_op, var_form, optimizer) - - with tempfile.NamedTemporaryFile(suffix='.inp', delete=True) as cache_tmp_file: - cache_tmp_file_name = cache_tmp_file.name - quantum_instance_caching = QuantumInstance(backend, - circuit_caching=True, - cache_file=cache_tmp_file_name, - skip_qobj_deepcopy=True, - skip_qobj_validation=True, - seed_simulator=self.seed, - seed_transpiler=self.seed) - algo.run(quantum_instance_caching) - self.assertLessEqual(quantum_instance_caching.circuit_cache.misses, 0) - - is_file_exist = os.path.exists(cache_tmp_file_name) - self.assertTrue(is_file_exist, "Does not store content successfully.") - - circuit_cache_new = CircuitCache(skip_qobj_deepcopy=True, - cache_file=cache_tmp_file_name) - self.assertEqual(quantum_instance_caching.circuit_cache.mappings, - circuit_cache_new.mappings) - self.assertLessEqual(circuit_cache_new.misses, 0) - - def test_saving_and_loading_one_circ(self): - """ Saving and Loading one Circ test """ - with tempfile.NamedTemporaryFile(suffix='.inp', delete=True) as cache_tmp_file: - cache_tmp_file_name = cache_tmp_file.name - var_form = RYRZ(num_qubits=4, depth=5) - backend = BasicAer.get_backend('statevector_simulator') - - params0 = aqua_globals.random.random_sample(var_form.num_parameters) - circ0 = var_form.construct_circuit(params0) - - qi0 = QuantumInstance(backend, - circuit_caching=True, - cache_file=cache_tmp_file_name, - skip_qobj_deepcopy=True, - skip_qobj_validation=True, - seed_simulator=self.seed, - seed_transpiler=self.seed) - - _ = qi0.execute([circ0]) - with open(cache_tmp_file_name, "rb") as cache_handler: - saved_cache = pickle.load(cache_handler, encoding="ASCII") - self.assertIn('qobjs', saved_cache) - self.assertIn('mappings', saved_cache) - qobjs = [Qobj.from_dict(qob) for qob in saved_cache['qobjs']] - self.assertTrue(isinstance(qobjs[0], Qobj)) - self.assertGreaterEqual(len(saved_cache['mappings'][0][0]), 50) - - qi1 = QuantumInstance(backend, - circuit_caching=True, - cache_file=cache_tmp_file_name, - skip_qobj_deepcopy=True, - skip_qobj_validation=True, - seed_simulator=self.seed, - seed_transpiler=self.seed) - - params1 = aqua_globals.random.random_sample(var_form.num_parameters) - circ1 = var_form.construct_circuit(params1) - - qobj1 = qi1.circuit_cache.load_qobj_from_cache([circ1], - 0, - run_config=qi1.run_config) - self.assertTrue(isinstance(qobj1, Qobj)) - _ = qi1.execute([circ1]) - - self.assertEqual(qi0.circuit_cache.mappings, qi1.circuit_cache.mappings) - self.assertLessEqual(qi1.circuit_cache.misses, 0) - - -if __name__ == '__main__': - unittest.main() diff --git a/test/aqua/test_qaoa.py b/test/aqua/test_qaoa.py index 5b007bc315..06b732a7ad 100644 --- a/test/aqua/test_qaoa.py +++ b/test/aqua/test_qaoa.py @@ -15,7 +15,6 @@ """ Test QAOA """ import unittest -import os from test.aqua.common import QiskitAquaTestCase import numpy as np @@ -65,7 +64,6 @@ def test_qaoa(self, w, prob, m, solutions): """ QAOA test """ seed = 0 aqua_globals.random_seed = seed - os.environ.pop('QISKIT_AQUA_CIRCUIT_CACHE', None) self.log.debug('Testing %s-step QAOA with MaxCut on graph\n%s', prob, w) backend = BasicAer.get_backend('statevector_simulator') @@ -73,9 +71,7 @@ def test_qaoa(self, w, prob, m, solutions): qubit_op, offset = max_cut.get_qubit_op(w) qaoa = QAOA(qubit_op, optimizer, prob, mixer=m) - # TODO: cache fails for QAOA since we construct the evolution circuit via instruction - quantum_instance = QuantumInstance(backend, circuit_caching=False, - seed_simulator=seed, seed_transpiler=seed) + quantum_instance = QuantumInstance(backend, seed_simulator=seed, seed_transpiler=seed) result = qaoa.run(quantum_instance) x = sample_most_likely(result['eigvecs'][0]) @@ -86,8 +82,6 @@ def test_qaoa(self, w, prob, m, solutions): self.log.debug('solution: %s', graph_solution) self.log.debug('solution objective: %s', max_cut.max_cut_value(x, w)) self.assertIn(''.join([str(int(i)) for i in graph_solution]), solutions) - if quantum_instance.has_circuit_caching: - self.assertLess(quantum_instance._circuit_cache.misses, 3) if __name__ == '__main__': diff --git a/test/aqua/test_qgan.py b/test/aqua/test_qgan.py index ebea5b4b33..b216d93bc7 100644 --- a/test/aqua/test_qgan.py +++ b/test/aqua/test_qgan.py @@ -16,7 +16,6 @@ """ Test QGAN """ import unittest -import os from test.aqua.common import QiskitAquaTestCase from qiskit import QuantumCircuit, QuantumRegister from qiskit.aqua.components.uncertainty_models import (UniformDistribution, @@ -33,7 +32,6 @@ class TestQGAN(QiskitAquaTestCase): """ Test QGAN """ def setUp(self): super().setUp() - os.environ.pop('QISKIT_AQUA_CIRCUIT_CACHE', None) self.seed = 7 aqua_globals.random_seed = self.seed # Number training data samples @@ -94,12 +92,10 @@ def setUp(self): self.qgan.seed = 7 # Set quantum instance to run the quantum generator self.qi_statevector = QuantumInstance(backend=BasicAer.get_backend('statevector_simulator'), - circuit_caching=False, seed_simulator=2, seed_transpiler=2) self.qi_qasm = QuantumInstance(backend=BasicAer.get_backend('qasm_simulator'), shots=1000, - circuit_caching=False, seed_simulator=2, seed_transpiler=2) # Set entangler map diff --git a/test/aqua/test_skip_qobj_validation.py b/test/aqua/test_skip_qobj_validation.py index 2ca9d6d776..69fffe1119 100644 --- a/test/aqua/test_skip_qobj_validation.py +++ b/test/aqua/test_skip_qobj_validation.py @@ -15,7 +15,6 @@ """ Test Skip Qobj Validation """ import unittest -import os from test.aqua.common import QiskitAquaTestCase from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister from qiskit import BasicAer @@ -56,12 +55,10 @@ def setUp(self): def test_wo_backend_options(self): """ without backend options test """ - os.environ.pop('QISKIT_AQUA_CIRCUIT_CACHE', None) quantum_instance = QuantumInstance(self.backend, seed_transpiler=self.random_seed, seed_simulator=self.random_seed, - shots=1024, - circuit_caching=False) + shots=1024) # run without backend_options and without noise res_wo_bo = quantum_instance.execute(self.qc).get_counts(self.qc) @@ -72,12 +69,10 @@ def test_wo_backend_options(self): def test_w_backend_options(self): """ with backend options test """ # run with backend_options - os.environ.pop('QISKIT_AQUA_CIRCUIT_CACHE', None) quantum_instance = QuantumInstance(self.backend, seed_transpiler=self.random_seed, seed_simulator=self.random_seed, shots=1024, backend_options={ - 'initial_statevector': [.5, .5, .5, .5]}, - circuit_caching=False) + 'initial_statevector': [.5, .5, .5, .5]}) res_w_bo = quantum_instance.execute(self.qc).get_counts(self.qc) quantum_instance.skip_qobj_validation = True res_w_bo_skip_validation = quantum_instance.execute(self.qc).get_counts(self.qc) @@ -95,7 +90,6 @@ def test_w_noise(self): self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return - os.environ.pop('QISKIT_AQUA_CIRCUIT_CACHE', None) probs_given0 = [0.9, 0.1] probs_given1 = [0.3, 0.7] noise_model = NoiseModel() @@ -103,8 +97,7 @@ def test_w_noise(self): quantum_instance = QuantumInstance(self.backend, seed_transpiler=self.random_seed, seed_simulator=self.random_seed, shots=1024, - noise_model=noise_model, - circuit_caching=False) + noise_model=noise_model) res_w_noise = quantum_instance.execute(self.qc).get_counts(self.qc) quantum_instance.skip_qobj_validation = True diff --git a/test/aqua/test_vqc.py b/test/aqua/test_vqc.py index c1073655a7..ebe3ae7685 100644 --- a/test/aqua/test_vqc.py +++ b/test/aqua/test_vqc.py @@ -97,10 +97,7 @@ def test_vqc_statevector_via_run_algorithm(self): params = { 'problem': {'name': 'classification', 'random_seed': 10598, - 'circuit_caching': True, - 'skip_qobj_deepcopy': True, - 'skip_qobj_validation': True, - 'circuit_cache_file': None, + 'skip_qobj_validation': True }, 'algorithm': {'name': 'VQC'}, 'backend': {'provider': 'qiskit.BasicAer', 'name': 'statevector_simulator'}, @@ -202,8 +199,6 @@ def test_save_and_load_model(self): self.ref_prediction_a_probs, decimal=8) np.testing.assert_array_equal(predicted_labels, self.ref_prediction_a_label) - if quantum_instance.has_circuit_caching: - self.assertLess(quantum_instance._circuit_cache.misses, 3) if os.path.exists(file_path): try: @@ -276,11 +271,7 @@ def test_vqc_on_wine(self): params = { 'problem': {'name': 'classification', 'random_seed': self.seed, - 'circuit_caching': True, - 'skip_qobj_deepcopy': True, - 'skip_qobj_validation': True, - 'circuit_cache_file': None, - 'circuit_optimization_level': 0 + 'skip_qobj_validation': True }, 'algorithm': {'name': 'VQC'}, 'backend': {'provider': 'qiskit.BasicAer', 'name': 'statevector_simulator'}, @@ -307,11 +298,7 @@ def test_vqc_with_raw_feature_vector_on_wine(self): params = { 'problem': {'name': 'classification', 'random_seed': self.seed, - 'circuit_caching': True, - 'skip_qobj_deepcopy': True, - 'skip_qobj_validation': True, - 'circuit_cache_file': None, - 'circuit_optimization_level': 0 + 'skip_qobj_validation': True }, 'algorithm': {'name': 'VQC'}, 'backend': {'provider': 'qiskit.BasicAer', 'name': 'statevector_simulator'}, From 2088b3a197a426395e342aa2faf903f9f7f039fd Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Thu, 19 Sep 2019 00:33:04 -0400 Subject: [PATCH 08/21] update change log --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c410cdd10..50e775e60e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,17 +18,29 @@ Changelog](http://keepachangelog.com/en/1.0.0/). [UNRELEASED](https://github.com/Qiskit/qiskit-aqua/compare/0.6.0...HEAD) ======================================================================== +Changed +----- + +- `VQE` and `VQC` now use parameterized circuits to save transpilation time. + Added ----- - Ability to create a `CustomCircuitOracle` object with a callback for `evaluate_classically`, which a `Grover` object will now check for, upon initialization, on its provided oracle. +- Add `Parameter` in `VariationalForm`, `FeatureMap` and `evolution_instruction` to allow generate + parameterized circuit/instruction. Fixed ------- - A bug where `UCCSD` might generate an empty operator and try to evolve it. (#680) +Removed +------- + +- The `CircuitCache` class is removed, use parameterized circuits as an alternative. + [0.6.0](https://github.com/Qiskit/qiskit-aqua/compare/0.5.5...0.6.0) - 2019-08-22 ================================================================================= From c73fdef5ae3d9a84bc1b8549e19c8bff2b398142 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Thu, 19 Sep 2019 15:01:38 -0400 Subject: [PATCH 09/21] update change log --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50e775e60e..6f19671460 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,15 +21,15 @@ Changelog](http://keepachangelog.com/en/1.0.0/). Changed ----- -- `VQE` and `VQC` now use parameterized circuits to save transpilation time. +- `VQE` and `VQC` now use parameterized circuits if it is available to save transpilation time. Added ----- - Ability to create a `CustomCircuitOracle` object with a callback for `evaluate_classically`, which a `Grover` object will now check for, upon initialization, on its provided oracle. -- Add `Parameter` in `VariationalForm`, `FeatureMap` and `evolution_instruction` to allow generate - parameterized circuit/instruction. +- Add `Parameter` to `VariationalForm`, `FeatureMap` and `evolution_instruction` to allow + generate parameterized circuit/instruction; hence, algorithm can save time in compilation. Fixed ------- @@ -60,7 +60,7 @@ Added shell support - Chemistry: UHF open-shell support - Supported for all drivers: Gaussian16, PyQuante, PySCF and PSI4 - - QMolecule extended to include integrals, coeffiecients etc for separate beta + - QMolecule extended to include integrals, coefficients etc for separate beta - Chemistry: QMolecule extended with integrals in atomic orbital basis to facilitate common access to these for experimentation - Supported for all drivers: Gaussian16, PyQuante, PySCF and PSI4 From 854fad5f3901215ef5427a32657b72500c35adc8 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Tue, 24 Sep 2019 16:25:13 -0400 Subject: [PATCH 10/21] put parameter in algo and use parametervector --- qiskit/aqua/algorithms/adaptive/vqc/vqc.py | 17 +++++++++-------- qiskit/aqua/algorithms/adaptive/vqe/vqe.py | 9 ++++++--- .../aqua/components/feature_maps/feature_map.py | 6 ------ .../components/feature_maps/pauli_expansion.py | 1 - .../feature_maps/raw_feature_vector.py | 6 +----- qiskit/aqua/components/variational_forms/ry.py | 4 +--- .../aqua/components/variational_forms/ryrz.py | 5 ++--- .../aqua/components/variational_forms/swaprz.py | 4 +--- .../variational_forms/variational_form.py | 10 ---------- .../components/variational_forms/uccsd.py | 5 +---- 10 files changed, 21 insertions(+), 46 deletions(-) diff --git a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py index eccd1bc02d..0ffa917d50 100644 --- a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py +++ b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py @@ -20,6 +20,8 @@ from sklearn.utils import shuffle from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister +from qiskit.circuit import ParameterVector + from qiskit.aqua import Pluggable, PluggableType, get_pluggable_class, AquaError from qiskit.aqua.utils import get_feature_dimension from qiskit.aqua.utils import map_label_to_class_name @@ -265,6 +267,8 @@ def __init__( self._ret = {} self._feature_map = feature_map self._num_qubits = feature_map.num_qubits + self._var_form_params = ParameterVector('θ', self._var_form.num_parameters) + self._feature_map_params = ParameterVector('x', self._feature_map.feature_dimension) self._parameterized_circuits = None @classmethod @@ -350,7 +354,7 @@ def _build_parameterized_circuits(): self._parameterized_circuits is None: parameterized_circuits = self.construct_circuit( - self._feature_map.parameters, self._var_form.parameters, + self._feature_map_params, self._var_form_params, measurement=not self._quantum_instance.is_statevector) self._parameterized_circuits = \ self._quantum_instance.transpile(parameterized_circuits)[0] @@ -360,19 +364,16 @@ def _build_parameterized_circuits(): for datum in data: if self._var_form.is_parameterized_circuit and \ self._feature_map.is_parameterized_circuit: - curr_params = {p: datum[i] for i, p in enumerate(self._feature_map.parameters)} - curr_params.update({p: thet[i] for i, p in - enumerate(self._var_form.parameters)}) - circuit = self._parameterized_circuits - circuit = circuit.bind_parameters(curr_params) + curr_params = {self._feature_map_params: datum, + self._var_form_params: thet} + circuit = self._parameterized_circuits.bind_parameters(curr_params) else: circuit = self.construct_circuit( datum, thet, measurement=not self._quantum_instance.is_statevector) circuits.append(circuit) results = self._quantum_instance.execute( - circuits, had_transpiled=self._var_form.is_parameterized_circuit and - self._feature_map.is_parameterized_circuit) + circuits, had_transpiled=self._parameterized_circuits is not None) circuit_id = 0 predicted_probs = [] diff --git a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py index e35357f815..02b55ac910 100644 --- a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py +++ b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py @@ -23,6 +23,7 @@ import numpy as np from qiskit import ClassicalRegister, QuantumCircuit +from qiskit.circuit import ParameterVector from qiskit.aqua.algorithms.adaptive.vq_algorithm import VQAlgorithm from qiskit.aqua import AquaError, Pluggable, PluggableType, get_pluggable_class @@ -127,6 +128,8 @@ def __init__(self, operator, var_form, optimizer, self._aux_operators.append(aux_op) self._auto_conversion = auto_conversion logger.info(self.print_settings()) + self._var_form_params = ParameterVector('θ', self._var_form.num_parameters) + self._parameterized_circuits = None @classmethod @@ -377,7 +380,7 @@ def _energy_evaluation(self, parameters): def _build_parameterized_circuits(): if self._var_form.is_parameterized_circuit and self._parameterized_circuits is None: parameterized_circuits = self.construct_circuit( - self._var_form.parameters, + self._var_form_params, statevector_mode=self._quantum_instance.is_statevector, use_simulator_operator_mode=self._use_simulator_operator_mode) @@ -389,7 +392,7 @@ def _build_parameterized_circuits(): # binding parameters here since the circuits had been transpiled if self._var_form.is_parameterized_circuit: for idx, parameter in enumerate(parameter_sets): - curr_param = {p: parameter[i] for i, p in enumerate(self._var_form.parameters)} + curr_param = {self._var_form_params: parameter} for qc in self._parameterized_circuits: tmp = qc.bind_parameters(curr_param) tmp.name = str(idx) + tmp.name @@ -417,7 +420,7 @@ def _build_parameterized_circuits(): extra_args = {} result = self._quantum_instance.execute(to_be_simulated_circuits, - self._var_form.is_parameterized_circuit, + self._parameterized_circuits is not None, **extra_args) for idx, _ in enumerate(parameter_sets): diff --git a/qiskit/aqua/components/feature_maps/feature_map.py b/qiskit/aqua/components/feature_maps/feature_map.py index 05bbd5b27c..b32893a625 100644 --- a/qiskit/aqua/components/feature_maps/feature_map.py +++ b/qiskit/aqua/components/feature_maps/feature_map.py @@ -80,12 +80,6 @@ def feature_dimension(self): def num_qubits(self): """ returns number of qubits """ return self._num_qubits - - @property - def parameters(self): - """ returns Parameters """ - return self._parameters - @property def is_parameterized_circuit(self): """ returns whether or not the sub-class support parameterized circuit """ diff --git a/qiskit/aqua/components/feature_maps/pauli_expansion.py b/qiskit/aqua/components/feature_maps/pauli_expansion.py index a5c62514b1..ac54cb1110 100644 --- a/qiskit/aqua/components/feature_maps/pauli_expansion.py +++ b/qiskit/aqua/components/feature_maps/pauli_expansion.py @@ -105,7 +105,6 @@ def __init__(self, feature_dimension, depth=2, entangler_map=None, self._pauli_strings = self._build_subset_paulis_string(paulis) self._data_map_func = data_map_func - self._parameters = [Parameter('f{}'.format(i)) for i in range(self._feature_dimension)] self._is_parameterized_circuit = True def _build_subset_paulis_string(self, paulis): diff --git a/qiskit/aqua/components/feature_maps/raw_feature_vector.py b/qiskit/aqua/components/feature_maps/raw_feature_vector.py index abc9e8e52a..c236985dfa 100644 --- a/qiskit/aqua/components/feature_maps/raw_feature_vector.py +++ b/qiskit/aqua/components/feature_maps/raw_feature_vector.py @@ -77,11 +77,7 @@ def construct_circuit(self, x, qr=None, inverse=False): TypeError: invalid input ValueError: invalid input """ - if not isinstance(x, np.ndarray): - raise TypeError("x must be numpy array.") - if x.ndim != 1: - raise ValueError("x must be 1-D array.") - if x.shape[0] != self._feature_dimension: + if len(x) != self._feature_dimension: raise ValueError("Unexpected feature vector dimension.") state_vector = np.pad(x, (0, (1 << self.num_qubits) - len(x)), 'constant') diff --git a/qiskit/aqua/components/variational_forms/ry.py b/qiskit/aqua/components/variational_forms/ry.py index 808b4b0168..f5127779d3 100644 --- a/qiskit/aqua/components/variational_forms/ry.py +++ b/qiskit/aqua/components/variational_forms/ry.py @@ -16,7 +16,6 @@ import numpy as np from qiskit import QuantumRegister, QuantumCircuit -from qiskit.circuit import Parameter from qiskit.aqua.components.variational_forms import VariationalForm @@ -125,7 +124,6 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, self._num_parameters += len(self._entangler_map) * depth self._bounds = [(-np.pi, np.pi)] * self._num_parameters - self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] self._is_parameterized_circuit = True def construct_circuit(self, parameters, q=None): @@ -133,7 +131,7 @@ def construct_circuit(self, parameters, q=None): Construct the variational form, given its parameters. Args: - parameters (numpy.ndarray): circuit parameters. + parameters (Union(numpy.ndarray, list[Parameter], ParameterVector)): circuit parameters. q (QuantumRegister): Quantum Register for the circuit. Returns: diff --git a/qiskit/aqua/components/variational_forms/ryrz.py b/qiskit/aqua/components/variational_forms/ryrz.py index c8f7301533..ef4aeae366 100644 --- a/qiskit/aqua/components/variational_forms/ryrz.py +++ b/qiskit/aqua/components/variational_forms/ryrz.py @@ -16,7 +16,7 @@ import numpy as np from qiskit import QuantumRegister, QuantumCircuit -from qiskit.circuit import Parameter + from qiskit.aqua.components.variational_forms import VariationalForm @@ -107,7 +107,6 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, # for repeated block self._num_parameters += len(self._entangled_qubits) * depth * 2 self._bounds = [(-np.pi, np.pi)] * self._num_parameters - self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] self._is_paramterized_circuit = True def construct_circuit(self, parameters, q=None): @@ -115,7 +114,7 @@ def construct_circuit(self, parameters, q=None): Construct the variational form, given its parameters. Args: - parameters (numpy.ndarray): circuit parameters + parameters (Union(numpy.ndarray, list[Parameter], ParameterVector)): circuit parameters q (QuantumRegister): Quantum Register for the circuit. Returns: diff --git a/qiskit/aqua/components/variational_forms/swaprz.py b/qiskit/aqua/components/variational_forms/swaprz.py index c15f3d3a1f..3a805398c6 100644 --- a/qiskit/aqua/components/variational_forms/swaprz.py +++ b/qiskit/aqua/components/variational_forms/swaprz.py @@ -16,7 +16,6 @@ import numpy as np from qiskit import QuantumRegister, QuantumCircuit -from qiskit.circuit import Parameter from qiskit.aqua.components.variational_forms import VariationalForm @@ -101,7 +100,6 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, # for repeated block self._num_parameters += (len(self._entangled_qubits) + len(self._entangler_map)) * depth self._bounds = [(-np.pi, np.pi)] * self._num_parameters - self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] self._is_parameterized_circuit = True def construct_circuit(self, parameters, q=None): @@ -109,7 +107,7 @@ def construct_circuit(self, parameters, q=None): Construct the variational form, given its parameters. Args: - parameters (numpy.ndarray): circuit parameters + parameters (Union(numpy.ndarray, list[Parameter], ParameterVector)): circuit parameters q (QuantumRegister): Quantum Register for the circuit. Returns: diff --git a/qiskit/aqua/components/variational_forms/variational_form.py b/qiskit/aqua/components/variational_forms/variational_form.py index 2fa03cd37e..6087d36a6a 100644 --- a/qiskit/aqua/components/variational_forms/variational_form.py +++ b/qiskit/aqua/components/variational_forms/variational_form.py @@ -38,7 +38,6 @@ def __init__(self): self._num_qubits = 0 self._bounds = list() self._is_parameterized_circuit = False - self._parameters = None pass @classmethod @@ -77,15 +76,6 @@ def num_parameters(self): """ return self._num_parameters - @property - def parameters(self): - """Parameters of the variational form. - - Returns: - list[Parameter]: the Parameter for the variational form. - """ - return self._parameters - @property def is_parameterized_circuit(self): """ Whether or not the sub-class support parameterized circuit. diff --git a/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py b/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py index 0c761f4f2b..0bed6a2194 100644 --- a/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py +++ b/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py @@ -22,7 +22,6 @@ import numpy as np from qiskit import QuantumRegister, QuantumCircuit -from qiskit.circuit import Parameter from qiskit.tools import parallel_map from qiskit.tools.events import TextProgressBar @@ -169,8 +168,6 @@ def __init__(self, num_qubits, depth, num_orbitals, num_particles, self._bounds = [(-np.pi, np.pi) for _ in range(self._num_parameters)] self._logging_construct_circuit = True - self._parameters = [Parameter('x{}'.format(i)) for i in range(self._num_parameters)] - self._is_parameterized_circuit = True @property def single_excitations(self): @@ -256,7 +253,7 @@ def construct_circuit(self, parameters, q=None): Construct the variational form, given its parameters. Args: - parameters (numpy.ndarray): circuit parameters + parameters (Union(numpy.ndarray, list[Parameter], ParameterVector)): circuit parameters q (QuantumRegister, optional): Quantum Register for the circuit. Returns: From f69abb60ac51d33bf323129135d6437d95a7aa21 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Tue, 24 Sep 2019 23:29:37 -0400 Subject: [PATCH 11/21] support parameterized circuits in qsvm, and change property name --- .../aqua/algorithms/adaptive/qaoa/var_form.py | 4 +- qiskit/aqua/algorithms/adaptive/vqc/vqc.py | 9 +- qiskit/aqua/algorithms/adaptive/vqe/vqe.py | 6 +- .../aqua/algorithms/many_sample/qsvm/qsvm.py | 88 +++++++++++++------ .../components/feature_maps/feature_map.py | 13 +-- .../feature_maps/pauli_expansion.py | 3 +- .../aqua/components/variational_forms/ry.py | 2 +- .../aqua/components/variational_forms/ryrz.py | 2 +- .../components/variational_forms/swaprz.py | 2 +- .../variational_forms/variational_form.py | 12 +-- 10 files changed, 88 insertions(+), 53 deletions(-) diff --git a/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py b/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py index db14541a01..0d44fa7f33 100644 --- a/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py +++ b/qiskit/aqua/algorithms/adaptive/qaoa/var_form.py @@ -18,7 +18,6 @@ import numpy as np from qiskit import QuantumRegister, QuantumCircuit -from qiskit.circuit import Parameter from qiskit.quantum_info import Pauli from qiskit.aqua.operators import WeightedPauliOperator, op_converter @@ -72,8 +71,7 @@ def __init__(self, cost_operator, p, initial_state=None, mixer_operator=None): raise TypeError('The mixer should be a qiskit.aqua.operators.WeightedPauliOperator ' + 'object, found {} instead'.format(type(mixer_operator))) self._mixer_operator = mixer_operator - self.parameters = [Parameter('x{}'.format(i)) for i in range(self.num_parameters)] - self.is_parameterized_circuit = True + self.support_parameterized_circuit = True def construct_circuit(self, angles): """ construct circuit """ diff --git a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py index 0ffa917d50..005872ed1e 100644 --- a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py +++ b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py @@ -349,8 +349,8 @@ def _get_prediction(self, data, theta): theta_sets = np.split(theta, num_theta_sets) def _build_parameterized_circuits(): - if self._var_form.is_parameterized_circuit and \ - self._feature_map.is_parameterized_circuit and \ + if self._var_form.support_parameterized_circuit and \ + self._feature_map.support_parameterized_circuit and \ self._parameterized_circuits is None: parameterized_circuits = self.construct_circuit( @@ -362,8 +362,9 @@ def _build_parameterized_circuits(): _build_parameterized_circuits() for thet in theta_sets: for datum in data: - if self._var_form.is_parameterized_circuit and \ - self._feature_map.is_parameterized_circuit: + if self._var_form.support_parameterized_circuit and \ + self._feature_map.support_parameterized_circuit and \ + self._parameterized_circuits is not None: curr_params = {self._feature_map_params: datum, self._var_form_params: thet} circuit = self._parameterized_circuits.bind_parameters(curr_params) diff --git a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py index 02b55ac910..2ba1f03d96 100644 --- a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py +++ b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py @@ -378,7 +378,8 @@ def _energy_evaluation(self, parameters): std_energy = [] def _build_parameterized_circuits(): - if self._var_form.is_parameterized_circuit and self._parameterized_circuits is None: + if self._var_form.support_parameterized_circuit and \ + self._parameterized_circuits is None: parameterized_circuits = self.construct_circuit( self._var_form_params, statevector_mode=self._quantum_instance.is_statevector, @@ -386,11 +387,10 @@ def _build_parameterized_circuits(): self._parameterized_circuits = \ self._quantum_instance.transpile(parameterized_circuits) - _build_parameterized_circuits() circuits = [] # binding parameters here since the circuits had been transpiled - if self._var_form.is_parameterized_circuit: + if self._var_form.support_parameterized_circuit: for idx, parameter in enumerate(parameter_sets): curr_param = {self._var_form_params: parameter} for qc in self._parameterized_circuits: diff --git a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py index 254b6913ed..a55eec25b1 100644 --- a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py +++ b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py @@ -21,6 +21,8 @@ from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister from qiskit.tools import parallel_map from qiskit.tools.events import TextProgressBar +from qiskit.circuit import ParameterVector + from qiskit.aqua import aqua_globals from qiskit.aqua.algorithms import QuantumAlgorithm from qiskit.aqua import AquaError, Pluggable, PluggableType, get_pluggable_class @@ -69,7 +71,7 @@ class QSVM(QuantumAlgorithm): BATCH_SIZE = 1000 def __init__(self, feature_map, training_dataset=None, test_dataset=None, datapoints=None, - multiclass_extension=None): + multiclass_extension=None, use_parameterized_circuits=True): """Constructor. Args: @@ -79,6 +81,8 @@ def __init__(self, feature_map, training_dataset=None, test_dataset=None, datapo datapoints (numpy.ndarray, optional): prediction dataset. multiclass_extension (MultiExtension, optional): if number of classes > 2 then a multiclass scheme is needed. + use_parameterized_circuits (bool): whether or not use parameterized circuits to avoid + for transpile circuits with identical circuit topology. Raises: AquaError: use binary classifer for classes > 3 @@ -116,6 +120,11 @@ def __init__(self, feature_map, training_dataset=None, test_dataset=None, datapo qsvm_instance = _QSVM_Multiclass(self, multiclass_extension) self.instance = qsvm_instance + if self.feature_map.support_parameterized_circuit: + self._use_parameterized_circuits = use_parameterized_circuits + else: + # overwrite it to false if feature map does not support parameterized circuit + self._use_parameterized_circuits = False @classmethod def init_params(cls, params, algo_input): @@ -148,7 +157,7 @@ def _construct_circuit(x, feature_map, measurement, is_statevector_sim=False): Psi(x2)^dagger Psi(x1)|0>. """ x1, x2 = x - if x1.shape[0] != x2.shape[0]: + if len(x1) != len(x2): raise ValueError("x1 and x2 must be the same dimension.") q = QuantumRegister(feature_map.num_qubits, 'q') @@ -195,7 +204,8 @@ def construct_circuit(self, x1, x2, measurement=False): return QSVM._construct_circuit((x1, x2), self.feature_map, measurement) @staticmethod - def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None): + def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None, + use_parameterized_circuits=False): """ Construct kernel matrix, if x2_vec is None, self-innerproduct is conducted. @@ -214,10 +224,11 @@ def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None): D is the feature dimension x2_vec (numpy.ndarray): data points, 2-D array, N2xD, where N2 is the number of data, D is the feature dimension + use_parameterized_circuits (bool): whether or not use parameterized circuits to avoid + for transpile circuits with identical circuit topology. Returns: numpy.ndarray: 2-D matrix, N1xN2 """ - if x2_vec is None: is_symmetric = True x2_vec = x1_vec @@ -244,18 +255,28 @@ def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None): else: to_be_computed_data = np.concatenate((x1_vec, x2_vec)) - # the second x is redundant - to_be_computed_data_pair = [(x, x) for x in to_be_computed_data] - - if logger.isEnabledFor(logging.DEBUG): - logger.debug("Building circuits:") - TextProgressBar(sys.stderr) - circuits = parallel_map(QSVM._construct_circuit, - to_be_computed_data_pair, - task_args=(feature_map, measurement, is_statevector_sim), - num_processes=aqua_globals.num_processes) - - results = quantum_instance.execute(circuits) + if not use_parameterized_circuits: + # the second x is redundant + to_be_computed_data_pair = [(x, x) for x in to_be_computed_data] + if logger.isEnabledFor(logging.DEBUG): + logger.debug("Building circuits:") + TextProgressBar(sys.stderr) + circuits = parallel_map(QSVM._construct_circuit, + to_be_computed_data_pair, + task_args=(feature_map, measurement, is_statevector_sim), + num_processes=aqua_globals.num_processes) + else: + # build parameterized circuits, it could be slower for building circuit + # but overall it should be faster since it only transpile one circuit + feature_map_params = ParameterVector('x', feature_map.feature_dimension) + parameterized_circuit = QSVM._construct_circuit( + (feature_map_params, feature_map_params), feature_map, measurement, + is_statevector_sim=is_statevector_sim) + parameterized_circuit = quantum_instance.transpile(parameterized_circuit)[0] + circuits = [parameterized_circuit.bind_parameters({feature_map_params: x}) + for x in to_be_computed_data] + results = quantum_instance.execute(circuits, + had_transpiled=use_parameterized_circuits) if logger.isEnabledFor(logging.DEBUG): logger.debug("Calculating overlap:") @@ -284,15 +305,29 @@ def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None): to_be_computed_data_pair.append((x1, x2)) to_be_computed_index.append((i, j)) - if logger.isEnabledFor(logging.DEBUG): - logger.debug("Building circuits:") - TextProgressBar(sys.stderr) - circuits = parallel_map(QSVM._construct_circuit, - to_be_computed_data_pair, - task_args=(feature_map, measurement), - num_processes=aqua_globals.num_processes) - - results = quantum_instance.execute(circuits) + if use_parameterized_circuits: + # build parameterized circuits, it could be slower for building circuit + # but overall it should be faster since it only transpile one circuit + feature_map_params_x = ParameterVector('x', feature_map.feature_dimension) + feature_map_params_y = ParameterVector('y', feature_map.feature_dimension) + parameterized_circuit = QSVM._construct_circuit( + (feature_map_params_x, feature_map_params_y), feature_map, measurement, + is_statevector_sim=is_statevector_sim) + parameterized_circuit = quantum_instance.transpile(parameterized_circuit)[0] + circuits = [parameterized_circuit.bind_parameters({feature_map_params_x: x, + feature_map_params_y: y}) + for x, y in to_be_computed_data_pair] + else: + if logger.isEnabledFor(logging.DEBUG): + logger.debug("Building circuits:") + TextProgressBar(sys.stderr) + circuits = parallel_map(QSVM._construct_circuit, + to_be_computed_data_pair, + task_args=(feature_map, measurement), + num_processes=aqua_globals.num_processes) + + results = quantum_instance.execute(circuits, + had_transpiled=use_parameterized_circuits) if logger.isEnabledFor(logging.DEBUG): logger.debug("Calculating overlap:") @@ -339,7 +374,8 @@ def construct_kernel_matrix(self, x1_vec, x2_vec=None, quantum_instance=None): if self._quantum_instance is None: raise AquaError("Either setup quantum instance or provide it in the parameter.") - return QSVM.get_kernel_matrix(self._quantum_instance, self.feature_map, x1_vec, x2_vec) + return QSVM.get_kernel_matrix(self._quantum_instance, self.feature_map, x1_vec, x2_vec, + use_parameterized_circuits=self._use_parameterized_circuits) def train(self, data, labels, quantum_instance=None): """ diff --git a/qiskit/aqua/components/feature_maps/feature_map.py b/qiskit/aqua/components/feature_maps/feature_map.py index b32893a625..75f17870ec 100644 --- a/qiskit/aqua/components/feature_maps/feature_map.py +++ b/qiskit/aqua/components/feature_maps/feature_map.py @@ -36,7 +36,7 @@ def __init__(self): super().__init__() self._num_qubits = 0 self._feature_dimension = 0 - self._is_parameterized_circuit = False + self._support_parameterized_circuit = False self._parameters = None @classmethod @@ -80,12 +80,13 @@ def feature_dimension(self): def num_qubits(self): """ returns number of qubits """ return self._num_qubits + @property - def is_parameterized_circuit(self): + def support_parameterized_circuit(self): """ returns whether or not the sub-class support parameterized circuit """ - return self._is_parameterized_circuit + return self._support_parameterized_circuit - @is_parameterized_circuit.setter - def is_parameterized_circuit(self, new_value): + @support_parameterized_circuit.setter + def support_parameterized_circuit(self, new_value): """ set whether or not the sub-class support parameterized circuit """ - self._is_parameterized_circuit = new_value + self._support_parameterized_circuit = new_value diff --git a/qiskit/aqua/components/feature_maps/pauli_expansion.py b/qiskit/aqua/components/feature_maps/pauli_expansion.py index ac54cb1110..608975a4e1 100644 --- a/qiskit/aqua/components/feature_maps/pauli_expansion.py +++ b/qiskit/aqua/components/feature_maps/pauli_expansion.py @@ -24,7 +24,6 @@ from qiskit import QuantumCircuit, QuantumRegister from qiskit.quantum_info import Pauli from qiskit.qasm import pi -from qiskit.circuit import Parameter from qiskit.aqua.operators import evolution_instruction from qiskit.aqua.components.feature_maps import FeatureMap, self_product @@ -105,7 +104,7 @@ def __init__(self, feature_dimension, depth=2, entangler_map=None, self._pauli_strings = self._build_subset_paulis_string(paulis) self._data_map_func = data_map_func - self._is_parameterized_circuit = True + self._support_parameterized_circuit = True def _build_subset_paulis_string(self, paulis): # fill out the paulis to the number of qubits diff --git a/qiskit/aqua/components/variational_forms/ry.py b/qiskit/aqua/components/variational_forms/ry.py index f5127779d3..4ba4511de7 100644 --- a/qiskit/aqua/components/variational_forms/ry.py +++ b/qiskit/aqua/components/variational_forms/ry.py @@ -124,7 +124,7 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, self._num_parameters += len(self._entangler_map) * depth self._bounds = [(-np.pi, np.pi)] * self._num_parameters - self._is_parameterized_circuit = True + self._support_parameterized_circuit = True def construct_circuit(self, parameters, q=None): """ diff --git a/qiskit/aqua/components/variational_forms/ryrz.py b/qiskit/aqua/components/variational_forms/ryrz.py index ef4aeae366..ef299bb8b1 100644 --- a/qiskit/aqua/components/variational_forms/ryrz.py +++ b/qiskit/aqua/components/variational_forms/ryrz.py @@ -107,7 +107,7 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, # for repeated block self._num_parameters += len(self._entangled_qubits) * depth * 2 self._bounds = [(-np.pi, np.pi)] * self._num_parameters - self._is_paramterized_circuit = True + self._support_parameterized_circuit = True def construct_circuit(self, parameters, q=None): """ diff --git a/qiskit/aqua/components/variational_forms/swaprz.py b/qiskit/aqua/components/variational_forms/swaprz.py index 3a805398c6..4e7789b546 100644 --- a/qiskit/aqua/components/variational_forms/swaprz.py +++ b/qiskit/aqua/components/variational_forms/swaprz.py @@ -100,7 +100,7 @@ def __init__(self, num_qubits, depth=3, entangler_map=None, # for repeated block self._num_parameters += (len(self._entangled_qubits) + len(self._entangler_map)) * depth self._bounds = [(-np.pi, np.pi)] * self._num_parameters - self._is_parameterized_circuit = True + self._support_parameterized_circuit = True def construct_circuit(self, parameters, q=None): """ diff --git a/qiskit/aqua/components/variational_forms/variational_form.py b/qiskit/aqua/components/variational_forms/variational_form.py index 6087d36a6a..6b35f7c172 100644 --- a/qiskit/aqua/components/variational_forms/variational_form.py +++ b/qiskit/aqua/components/variational_forms/variational_form.py @@ -37,7 +37,7 @@ def __init__(self): self._num_parameters = 0 self._num_qubits = 0 self._bounds = list() - self._is_parameterized_circuit = False + self._support_parameterized_circuit = False pass @classmethod @@ -77,18 +77,18 @@ def num_parameters(self): return self._num_parameters @property - def is_parameterized_circuit(self): + def support_parameterized_circuit(self): """ Whether or not the sub-class support parameterized circuit. Returns: boolean: indicate the sub-class support parameterized circuit """ - return self._is_parameterized_circuit + return self._support_parameterized_circuit - @is_parameterized_circuit.setter - def is_parameterized_circuit(self, new_value): + @support_parameterized_circuit.setter + def support_parameterized_circuit(self, new_value): """ set whether or not the sub-class support parameterized circuit """ - self._is_parameterized_circuit = new_value + self._support_parameterized_circuit = new_value @property def num_qubits(self): From ab4deb1dd0878875d1f903465eceb90f0941369a Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 25 Sep 2019 11:43:34 -0400 Subject: [PATCH 12/21] update test --- test/aqua/test_vqc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/aqua/test_vqc.py b/test/aqua/test_vqc.py index ebe3ae7685..59a37b7b9c 100644 --- a/test/aqua/test_vqc.py +++ b/test/aqua/test_vqc.py @@ -122,14 +122,14 @@ def test_vqc_minibatching_no_gradient_support(self): n=n_dim, gap=0.3) backend = BasicAer.get_backend('statevector_simulator') num_qubits = n_dim - optimizer = COBYLA(maxiter=300) + optimizer = COBYLA(maxiter=40) feature_map = SecondOrderExpansion(feature_dimension=num_qubits, depth=2) var_form = RYRZ(num_qubits=num_qubits, depth=3) vqc = VQC(optimizer, feature_map, var_form, training_input, test_input, minibatch_size=2) quantum_instance = QuantumInstance(backend, seed_simulator=seed, seed_transpiler=seed, optimization_level=0) result = vqc.run(quantum_instance) - vqc_accuracy = 0.833333 + vqc_accuracy = 0.666 self.log.debug(result['testing_accuracy']) self.assertGreaterEqual(result['testing_accuracy'], vqc_accuracy) From 465cfe8b58a31180c3e200a54e5bcb70f5e039ce Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 25 Sep 2019 11:53:56 -0400 Subject: [PATCH 13/21] update docstring and change log --- CHANGELOG.md | 8 +++++--- qiskit/aqua/components/feature_maps/pauli_expansion.py | 2 +- qiskit/aqua/operators/common.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f19671460..a6e4b2199c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,15 +21,17 @@ Changelog](http://keepachangelog.com/en/1.0.0/). Changed ----- -- `VQE` and `VQC` now use parameterized circuits if it is available to save transpilation time. +- `VQE`, `VQC` and `QSVM` now use parameterized circuits if it is available to save time + in transpilation. Added ----- - Ability to create a `CustomCircuitOracle` object with a callback for `evaluate_classically`, which a `Grover` object will now check for, upon initialization, on its provided oracle. -- Add `Parameter` to `VariationalForm`, `FeatureMap` and `evolution_instruction` to allow - generate parameterized circuit/instruction; hence, algorithm can save time in compilation. +- `VariationalForm` and `FeatureMap` has a new property on `support_parameterized_circuit`, which + implies whether or not can be built with `Parameter` (or `ParameterVector`). Furthermore, + the `evolution_instruction` method support `Parameter` as time parameter. Fixed ------- diff --git a/qiskit/aqua/components/feature_maps/pauli_expansion.py b/qiskit/aqua/components/feature_maps/pauli_expansion.py index 608975a4e1..2185986116 100644 --- a/qiskit/aqua/components/feature_maps/pauli_expansion.py +++ b/qiskit/aqua/components/feature_maps/pauli_expansion.py @@ -146,7 +146,7 @@ def construct_circuit(self, x, qr=None, inverse=False): Construct the second order expansion based on given data. Args: - x (numpy.ndarray): 1-D to-be-transformed data. + x (Union(numpy.ndarray, list[Parameter], ParameterVector)): 1-D to-be-transformed data. qr (QauntumRegister, optional): the QuantumRegister object for the circuit, if None, generate new registers with name q. inverse (bool, optional): whether or not inverse the circuit diff --git a/qiskit/aqua/operators/common.py b/qiskit/aqua/operators/common.py index 54ff5e08fe..41d59ec696 100644 --- a/qiskit/aqua/operators/common.py +++ b/qiskit/aqua/operators/common.py @@ -235,7 +235,7 @@ def evolution_instruction(pauli_list, evo_time, num_time_slices, Args: pauli_list (list([[complex, Pauli]])): The list of pauli terms corresponding to a single time slice to be evolved - evo_time (Union(complex, float, Parameter)): The evolution time + evo_time (Union(complex, float, Parameter, ParameterExpression)): The evolution time num_time_slices (int): The number of time slices for the expansion controlled (bool, optional): Controlled circuit or not power (int, optional): The power to which the unitary operator is to be raised From 764b5016337a9dea37811e4052034960331781ce Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 25 Sep 2019 11:58:05 -0400 Subject: [PATCH 14/21] change default to use paramterized cirucits --- .../aqua/algorithms/many_sample/qsvm/qsvm.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py index a55eec25b1..7b2c462bd3 100644 --- a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py +++ b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py @@ -205,7 +205,7 @@ def construct_circuit(self, x1, x2, measurement=False): @staticmethod def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None, - use_parameterized_circuits=False): + use_parameterized_circuits=True): """ Construct kernel matrix, if x2_vec is None, self-innerproduct is conducted. @@ -255,17 +255,7 @@ def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None, else: to_be_computed_data = np.concatenate((x1_vec, x2_vec)) - if not use_parameterized_circuits: - # the second x is redundant - to_be_computed_data_pair = [(x, x) for x in to_be_computed_data] - if logger.isEnabledFor(logging.DEBUG): - logger.debug("Building circuits:") - TextProgressBar(sys.stderr) - circuits = parallel_map(QSVM._construct_circuit, - to_be_computed_data_pair, - task_args=(feature_map, measurement, is_statevector_sim), - num_processes=aqua_globals.num_processes) - else: + if use_parameterized_circuits: # build parameterized circuits, it could be slower for building circuit # but overall it should be faster since it only transpile one circuit feature_map_params = ParameterVector('x', feature_map.feature_dimension) @@ -275,6 +265,17 @@ def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None, parameterized_circuit = quantum_instance.transpile(parameterized_circuit)[0] circuits = [parameterized_circuit.bind_parameters({feature_map_params: x}) for x in to_be_computed_data] + else: + # the second x is redundant + to_be_computed_data_pair = [(x, x) for x in to_be_computed_data] + if logger.isEnabledFor(logging.DEBUG): + logger.debug("Building circuits:") + TextProgressBar(sys.stderr) + circuits = parallel_map(QSVM._construct_circuit, + to_be_computed_data_pair, + task_args=(feature_map, measurement, is_statevector_sim), + num_processes=aqua_globals.num_processes) + results = quantum_instance.execute(circuits, had_transpiled=use_parameterized_circuits) From 65177ed3b26db348e7737c49fdf098c002f2b523 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 25 Sep 2019 13:32:56 -0400 Subject: [PATCH 15/21] add PR number --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a6e4b2199c..133a0316ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,16 +22,16 @@ Changed ----- - `VQE`, `VQC` and `QSVM` now use parameterized circuits if it is available to save time - in transpilation. + in transpilation. (#693) Added ----- - Ability to create a `CustomCircuitOracle` object with a callback for `evaluate_classically`, - which a `Grover` object will now check for, upon initialization, on its provided oracle. + which a `Grover` object will now check for, upon initialization, on its provided oracle. (#681) - `VariationalForm` and `FeatureMap` has a new property on `support_parameterized_circuit`, which implies whether or not can be built with `Parameter` (or `ParameterVector`). Furthermore, - the `evolution_instruction` method support `Parameter` as time parameter. + the `evolution_instruction` method support `Parameter` as time parameter. (#693) Fixed ------- @@ -41,7 +41,7 @@ Fixed Removed ------- -- The `CircuitCache` class is removed, use parameterized circuits as an alternative. +- The `CircuitCache` class is removed, use parameterized circuits as an alternative. (#693) [0.6.0](https://github.com/Qiskit/qiskit-aqua/compare/0.5.5...0.6.0) - 2019-08-22 ================================================================================= From 817d715767b674af15beff38b7aab39250d98101 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 25 Sep 2019 14:13:03 -0400 Subject: [PATCH 16/21] remove the argument and add todo --- qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py | 11 +++-------- qiskit/aqua/components/feature_maps/feature_map.py | 1 - .../components/variational_forms/uccsd.py | 3 ++- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py index 7b2c462bd3..f53f4a73d8 100644 --- a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py +++ b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py @@ -71,7 +71,7 @@ class QSVM(QuantumAlgorithm): BATCH_SIZE = 1000 def __init__(self, feature_map, training_dataset=None, test_dataset=None, datapoints=None, - multiclass_extension=None, use_parameterized_circuits=True): + multiclass_extension=None): """Constructor. Args: @@ -81,8 +81,6 @@ def __init__(self, feature_map, training_dataset=None, test_dataset=None, datapo datapoints (numpy.ndarray, optional): prediction dataset. multiclass_extension (MultiExtension, optional): if number of classes > 2 then a multiclass scheme is needed. - use_parameterized_circuits (bool): whether or not use parameterized circuits to avoid - for transpile circuits with identical circuit topology. Raises: AquaError: use binary classifer for classes > 3 @@ -120,11 +118,8 @@ def __init__(self, feature_map, training_dataset=None, test_dataset=None, datapo qsvm_instance = _QSVM_Multiclass(self, multiclass_extension) self.instance = qsvm_instance - if self.feature_map.support_parameterized_circuit: - self._use_parameterized_circuits = use_parameterized_circuits - else: - # overwrite it to false if feature map does not support parameterized circuit - self._use_parameterized_circuits = False + self._use_parameterized_circuits = True if self.feature_map.support_parameterized_circuit \ + else False @classmethod def init_params(cls, params, algo_input): diff --git a/qiskit/aqua/components/feature_maps/feature_map.py b/qiskit/aqua/components/feature_maps/feature_map.py index 75f17870ec..2294a582f2 100644 --- a/qiskit/aqua/components/feature_maps/feature_map.py +++ b/qiskit/aqua/components/feature_maps/feature_map.py @@ -37,7 +37,6 @@ def __init__(self): self._num_qubits = 0 self._feature_dimension = 0 self._support_parameterized_circuit = False - self._parameters = None @classmethod def init_params(cls, params): diff --git a/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py b/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py index a0cb5cf184..a56a6209da 100644 --- a/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py +++ b/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py @@ -294,7 +294,8 @@ def construct_circuit(self, parameters, q=None): @staticmethod def _construct_circuit_for_one_excited_operator(qubit_op_and_param, qr, num_time_slices): qubit_op, param = qubit_op_and_param - # need to put -1j in the coeff of pauli since the Parameter does not support complex number + # TODO: need to put -1j in the coeff of pauli since the Parameter + # does not support complex number, but it can be removed if Parameter supports complex qubit_op = qubit_op * -1j qc = qubit_op.evolve(state_in=None, evo_time=param, num_time_slices=num_time_slices, From f8ec58d9507ca4c1221a9508bacd0b873e3cae9b Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 25 Sep 2019 14:17:59 -0400 Subject: [PATCH 17/21] access and clean parameterized circuit for vq algorihm --- .../aqua/algorithms/adaptive/vq_algorithm.py | 9 + qiskit/aqua/utils/circuit_cache.py | 243 ------------------ 2 files changed, 9 insertions(+), 243 deletions(-) delete mode 100644 qiskit/aqua/utils/circuit_cache.py diff --git a/qiskit/aqua/algorithms/adaptive/vq_algorithm.py b/qiskit/aqua/algorithms/adaptive/vq_algorithm.py index a420feadf9..df0776f03e 100644 --- a/qiskit/aqua/algorithms/adaptive/vq_algorithm.py +++ b/qiskit/aqua/algorithms/adaptive/vq_algorithm.py @@ -196,3 +196,12 @@ def var_form(self, new_value): def optimizer(self): """ returns optimizer """ return self._optimizer + + @property + def parameterized_circuits(self): + """ return parameterized circuits """ + return self._parameterized_circuits + + def cleanup_parameterized_circuits(self): + """ set parameterized circuits to None """ + self._parameterized_circuits = None diff --git a/qiskit/aqua/utils/circuit_cache.py b/qiskit/aqua/utils/circuit_cache.py deleted file mode 100644 index 9528c1bcb4..0000000000 --- a/qiskit/aqua/utils/circuit_cache.py +++ /dev/null @@ -1,243 +0,0 @@ -# -*- coding: utf-8 -*- - -# This code is part of Qiskit. -# -# (C) Copyright IBM 2018, 2019. -# -# This code is licensed under the Apache License, Version 2.0. You may -# obtain a copy of this license in the LICENSE.txt file in the root directory -# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. -# -# Any modifications or derivative works of this code must retain this -# copyright notice, and modified files need to carry a notice indicating -# that they have been altered from the originals. - -""" A utility for caching and reparameterizing circuits, rather than compiling from scratch -with each iteration. Note that if the circuit is transpiled aggressively such that rotation -parameters -cannot be easily mapped from the uncompiled to compiled circuit, caching will fail gracefully to -standard compilation. This will be noted by multiple cache misses in the DEBUG log. It is -generally safer to skip the transpiler (aqua_dict['backend']['skip_transpiler'] = True) -when using caching. - -Caching is controlled via the aqua_dict['problem']['circuit_caching'] parameter. -Setting skip_qobj_deepcopy = True -reuses the same qobj object over and over to avoid deepcopying. It is controlled -via the aqua_dict['problem']['skip_qobj_deepcopy'] parameter. - -You may also specify a filename into which to store the cache as a pickle file, for circuits which -are expensive to compile even the first time. The filename is set in -aqua_dict['problem']['circuit_cache_file']. -If a filename is present, the system will attempt to load from the file. - -In the event of an error, the system will fail gracefully, compile from scratch, and cache the new -compiled qobj and mapping in the file location in pickled form. It will fail over 5 -times before deciding -that caching should be disabled.""" - -import copy -import pickle -import logging -import numpy as np - -from qiskit import QuantumRegister -from qiskit.circuit import Qubit -from qiskit.assembler.run_config import RunConfig -from qiskit.qobj import Qobj, QasmQobjConfig - -from qiskit.aqua.aqua_error import AquaError - -logger = logging.getLogger(__name__) - - -class CircuitCache: - """ Circuit Cache """ - def __init__(self, - skip_qobj_deepcopy=False, - cache_file=None, - allowed_misses=3): - self.skip_qobj_deepcopy = skip_qobj_deepcopy - self.cache_file = cache_file - self.misses = 0 - self.qobjs = [] - self.mappings = [] - self.cache_transpiled_circuits = False - self.try_reusing_qobjs = True - self.allowed_misses = allowed_misses - try: - self.try_loading_cache_from_file() - except(EOFError, FileNotFoundError) as ex: - logger.warning("Error loading cache from file %s: %s", self.cache_file, repr(ex)) - - def cache_circuit(self, qobj, circuits, chunk): - """ - A method for caching compiled qobjs by storing the compiled qobj - and constructing a mapping array from the uncompiled operations in the circuit - to the instructions in the qobj. Note that the "qobjs" list in the cache dict is a - list of the cached chunks, each element of which contains a single qobj with as - many experiments as is allowed by the execution backend. E.g. if the backend allows - 300 experiments per job and the user wants to run 500 circuits, - len(circuit_cache['qobjs']) == 2, - len(circuit_cache['qobjs'][0].experiments) == 300, and - len(circuit_cache['qobjs'][1].experiments) == 200. - - This feature is only applied if 'circuit_caching' is True in the 'problem' Aqua - dictionary section. - - Args: - qobj (Qobj): A compiled qobj to be saved - circuits (list): The original uncompiled QuantumCircuits - chunk (int): If a larger list of circuits was broken into chunks by run_algorithm - for separate runs, - which chunk number `circuits` represents - Raises: - AquaError: Invalid circuit shape - """ - - self.qobjs.insert(chunk, copy.deepcopy(qobj)) - - self.mappings.insert(chunk, [{} for i in range(len(circuits))]) - for circ_num, input_circuit in enumerate(circuits): - - qreg_sizes = [reg.size for reg in input_circuit.qregs - if isinstance(reg, QuantumRegister)] - qreg_indeces = {reg.name: sum(qreg_sizes[0:i]) - for i, reg in enumerate(input_circuit.qregs)} - op_graph = {} - - # Unroll circuit in case of composite gates - raw_gates = [] - for gate in input_circuit.data: - raw_gates += [gate] - - for i, (uncompiled_gate, regs, _) in enumerate(raw_gates): - # pylint: disable=len-as-condition - if not hasattr(uncompiled_gate, 'params') or len(uncompiled_gate.params) < 1: - continue - if uncompiled_gate.name == 'snapshot': - continue - qubits = [bit.index + qreg_indeces[bit.register.name] - for bit in regs if isinstance(bit, Qubit)] - gate_type = uncompiled_gate.name - type_and_qubits = gate_type + qubits.__str__() - op_graph[type_and_qubits] = \ - op_graph.get(type_and_qubits, []) + [i] - mapping = {} - for compiled_gate_index, compiled_gate in \ - enumerate(qobj.experiments[circ_num].instructions): - # pylint: disable=len-as-condition - if not hasattr(compiled_gate, 'params') or len(compiled_gate.params) < 1: - continue - if compiled_gate.name == 'snapshot': - continue - type_and_qubits = compiled_gate.name + compiled_gate.qubits.__str__() - if len(op_graph[type_and_qubits]) > 0: - uncompiled_gate_index = op_graph[type_and_qubits].pop(0) - (uncompiled_gate, regs, _) = raw_gates[uncompiled_gate_index] - qubits = [bit.index + qreg_indeces[bit.register.name] for bit in regs - if isinstance(bit, Qubit)] - if (compiled_gate.name == uncompiled_gate.name) and \ - (compiled_gate.qubits.__str__() == qubits.__str__()): - mapping[compiled_gate_index] = uncompiled_gate_index - else: - raise AquaError( - "Circuit shape does not match qobj, found extra {} " - "instruction in qobj".format(type_and_qubits)) - self.mappings[chunk][circ_num] = mapping - for type_and_qubits, ops in op_graph.items(): - if ops: - raise AquaError( - "Circuit shape does not match qobj, found extra {} in circuit".format( - type_and_qubits)) - if self.cache_file is not None and self.cache_file: - with open(self.cache_file, 'wb') as cache_handler: - qobj_dicts = [qob.to_dict() for qob in self.qobjs] - pickle.dump({'qobjs': qobj_dicts, - 'mappings': self.mappings, - 'transpile': self.cache_transpiled_circuits}, - cache_handler, - protocol=pickle.HIGHEST_PROTOCOL) - logger.debug("Circuit cache saved to file: %s", self.cache_file) - - def try_loading_cache_from_file(self): - """ load cache from file """ - if not self.qobjs and self.cache_file: - with open(self.cache_file, "rb") as cache_handler: - try: - cache = pickle.load(cache_handler, encoding="ASCII") - except EOFError: - logger.debug("No cache found in file: %s", self.cache_file) - return - self.qobjs = [Qobj.from_dict(qob) for qob in cache['qobjs']] - self.mappings = cache['mappings'] - self.cache_transpiled_circuits = cache['transpile'] - logger.debug("Circuit cache loaded from file: %s", self.cache_file) - - # Note that this function overwrites the previous cached qobj for speed - def load_qobj_from_cache(self, circuits, chunk, run_config=None): - """ load qobj from cache """ - self.try_loading_cache_from_file() - - if self.try_reusing_qobjs and self.qobjs and len(self.qobjs) <= chunk: - self.mappings.insert(chunk, self.mappings[0]) - self.qobjs.insert(chunk, copy.deepcopy(self.qobjs[0])) - - for circ_num, input_circuit in enumerate(circuits): - - # If there are too few experiments in the cache, try reusing the first experiment. - # Only do this for the first chunk. Subsequent chunks should rely on these copies - # through the deepcopy above. - if self.try_reusing_qobjs and chunk == 0 and circ_num > 0 and \ - len(self.qobjs[chunk].experiments) <= circ_num: - self.qobjs[0].experiments.insert(circ_num, - copy.deepcopy(self.qobjs[0].experiments[0])) - self.mappings[0].insert(circ_num, self.mappings[0][0]) - - # Unroll circuit in case of composite gates - raw_gates = [] - for gate in input_circuit.data: - raw_gates += [gate] - self.qobjs[chunk].experiments[circ_num].header.name = input_circuit.name - for gate_num, compiled_gate in \ - enumerate(self.qobjs[chunk].experiments[circ_num].instructions): - # pylint: disable=len-as-condition - if not hasattr(compiled_gate, 'params') or len(compiled_gate.params) < 1: - continue - if compiled_gate.name == 'snapshot': - continue - cache_index = self.mappings[chunk][circ_num][gate_num] - (uncompiled_gate, _, _) = raw_gates[cache_index] - - # Need the 'getattr' wrapper because measure has no 'params' field and breaks this. - if not len(getattr(compiled_gate, 'params', [])) == \ - len(getattr(uncompiled_gate, 'params', [])) or \ - not compiled_gate.name == uncompiled_gate.name: - raise AquaError('Gate mismatch at gate {0} ({1}, {2} ' - 'params) of circuit against gate {3} ({4}, ' - '{5} params) of cached qobj'.format(cache_index, - uncompiled_gate.name, - len(uncompiled_gate.params), - gate_num, - compiled_gate.name, - len(compiled_gate.params))) - compiled_gate.params = np.array(uncompiled_gate.params, dtype=float).tolist() - exec_qobj = copy.copy(self.qobjs[chunk]) - if self.skip_qobj_deepcopy: - exec_qobj.experiments = self.qobjs[chunk].experiments[0:len(circuits)] - else: - exec_qobj.experiments = copy.deepcopy(self.qobjs[chunk].experiments[0:len(circuits)]) - - if run_config is None: - run_config = RunConfig(shots=1024, max_credits=10, memory=False) - exec_qobj.config = QasmQobjConfig(**run_config.to_dict()) - exec_qobj.config.memory_slots = \ - max(experiment.config.memory_slots for experiment in exec_qobj.experiments) - exec_qobj.config.n_qubits = \ - max(experiment.config.n_qubits for experiment in exec_qobj.experiments) - return exec_qobj - - def clear_cache(self): - """ clear cache """ - self.qobjs = [] - self.mappings = [] - self.try_reusing_qobjs = True From 43c363000fc0b9e26f7f8c91d9414f01f1672a43 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 25 Sep 2019 16:12:58 -0400 Subject: [PATCH 18/21] clean up parameterized circuit after _run --- qiskit/aqua/algorithms/adaptive/vq_algorithm.py | 7 ++----- qiskit/aqua/algorithms/adaptive/vqc/vqc.py | 1 + qiskit/aqua/algorithms/adaptive/vqe/vqe.py | 2 ++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/qiskit/aqua/algorithms/adaptive/vq_algorithm.py b/qiskit/aqua/algorithms/adaptive/vq_algorithm.py index df0776f03e..f84e9544ae 100644 --- a/qiskit/aqua/algorithms/adaptive/vq_algorithm.py +++ b/qiskit/aqua/algorithms/adaptive/vq_algorithm.py @@ -60,6 +60,8 @@ def __init__(self, self._cost_fn = cost_fn self._initial_point = initial_point + self._parameterized_circuits = None + @abstractmethod def get_optimal_cost(self): """ get optimal cost """ @@ -197,11 +199,6 @@ def optimizer(self): """ returns optimizer """ return self._optimizer - @property - def parameterized_circuits(self): - """ return parameterized circuits """ - return self._parameterized_circuits - def cleanup_parameterized_circuits(self): """ set parameterized circuits to None """ self._parameterized_circuits = None diff --git a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py index 005872ed1e..e3e3fdca6c 100644 --- a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py +++ b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py @@ -611,6 +611,7 @@ def _run(self): _, predicted_labels = self.predict(self._datapoints) self._ret['predicted_classes'] = map_label_to_class_name(predicted_labels, self._label_to_class) + self.cleanup_parameterized_circuits() return self._ret def get_optimal_cost(self): diff --git a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py index 2ba1f03d96..d01352499c 100644 --- a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py +++ b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py @@ -359,6 +359,8 @@ def _run(self): self._ret['eigvals'] = np.asarray([self.get_optimal_cost()]) self._ret['eigvecs'] = np.asarray([self.get_optimal_vector()]) self._eval_aux_ops() + + self.cleanup_parameterized_circuits() return self._ret # This is the objective function to be passed to the optimizer that is uses for evaluation From 0a7f26b13ba04a7808ffee5a4b1b84975c0982b0 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 25 Sep 2019 17:14:11 -0400 Subject: [PATCH 19/21] Fix lint error and decrease travis job time --- .travis.yml | 6 +++--- qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6d88cf777a..fdbd99aec8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,11 +30,11 @@ matrix: - name: "Lint and Style check and Test Chemistry" env: TEST_DIR=chemistry - name: "Test Aqua 1" - env: TEST_DIR=aqua TEST_PARAMS="-end 30" + env: TEST_DIR=aqua TEST_PARAMS="-end 29" - name: "Test Aqua 2" - env: TEST_DIR=aqua TEST_PARAMS="-start 30 -end 47" + env: TEST_DIR=aqua TEST_PARAMS="-start 29 -end 46" - name: "Test Aqua 3" - env: TEST_DIR=aqua TEST_PARAMS="-start 47" + env: TEST_DIR=aqua TEST_PARAMS="-start 46" - if: tag IS present python: 3.6 env: diff --git a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py index f53f4a73d8..5005653b58 100644 --- a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py +++ b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py @@ -118,8 +118,7 @@ def __init__(self, feature_map, training_dataset=None, test_dataset=None, datapo qsvm_instance = _QSVM_Multiclass(self, multiclass_extension) self.instance = qsvm_instance - self._use_parameterized_circuits = True if self.feature_map.support_parameterized_circuit \ - else False + self._use_parameterized_circuits = bool(self.feature_map.support_parameterized_circuit) @classmethod def init_params(cls, params, algo_input): From 2ff9c4b8c4fe17f6ea0ae154bd784feac6e6e34f Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 25 Sep 2019 22:33:42 -0400 Subject: [PATCH 20/21] remove option of using parameterized circuits in qsvm, it determines based on the availability --- qiskit/aqua/algorithms/adaptive/vqc/vqc.py | 4 +--- qiskit/aqua/algorithms/adaptive/vqe/vqe.py | 2 +- qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py | 12 +++++------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py index e3e3fdca6c..0981eb8694 100644 --- a/qiskit/aqua/algorithms/adaptive/vqc/vqc.py +++ b/qiskit/aqua/algorithms/adaptive/vqc/vqc.py @@ -362,9 +362,7 @@ def _build_parameterized_circuits(): _build_parameterized_circuits() for thet in theta_sets: for datum in data: - if self._var_form.support_parameterized_circuit and \ - self._feature_map.support_parameterized_circuit and \ - self._parameterized_circuits is not None: + if self._parameterized_circuits is not None: curr_params = {self._feature_map_params: datum, self._var_form_params: thet} circuit = self._parameterized_circuits.bind_parameters(curr_params) diff --git a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py index d01352499c..42ae948dcd 100644 --- a/qiskit/aqua/algorithms/adaptive/vqe/vqe.py +++ b/qiskit/aqua/algorithms/adaptive/vqe/vqe.py @@ -392,7 +392,7 @@ def _build_parameterized_circuits(): _build_parameterized_circuits() circuits = [] # binding parameters here since the circuits had been transpiled - if self._var_form.support_parameterized_circuit: + if self._parameterized_circuits is not None: for idx, parameter in enumerate(parameter_sets): curr_param = {self._var_form_params: parameter} for qc in self._parameterized_circuits: diff --git a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py index 5005653b58..bc3a316459 100644 --- a/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py +++ b/qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py @@ -118,7 +118,6 @@ def __init__(self, feature_map, training_dataset=None, test_dataset=None, datapo qsvm_instance = _QSVM_Multiclass(self, multiclass_extension) self.instance = qsvm_instance - self._use_parameterized_circuits = bool(self.feature_map.support_parameterized_circuit) @classmethod def init_params(cls, params, algo_input): @@ -198,8 +197,7 @@ def construct_circuit(self, x1, x2, measurement=False): return QSVM._construct_circuit((x1, x2), self.feature_map, measurement) @staticmethod - def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None, - use_parameterized_circuits=True): + def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None): """ Construct kernel matrix, if x2_vec is None, self-innerproduct is conducted. @@ -218,11 +216,12 @@ def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None, D is the feature dimension x2_vec (numpy.ndarray): data points, 2-D array, N2xD, where N2 is the number of data, D is the feature dimension - use_parameterized_circuits (bool): whether or not use parameterized circuits to avoid - for transpile circuits with identical circuit topology. Returns: numpy.ndarray: 2-D matrix, N1xN2 """ + + use_parameterized_circuits = feature_map.support_parameterized_circuit + if x2_vec is None: is_symmetric = True x2_vec = x1_vec @@ -369,8 +368,7 @@ def construct_kernel_matrix(self, x1_vec, x2_vec=None, quantum_instance=None): if self._quantum_instance is None: raise AquaError("Either setup quantum instance or provide it in the parameter.") - return QSVM.get_kernel_matrix(self._quantum_instance, self.feature_map, x1_vec, x2_vec, - use_parameterized_circuits=self._use_parameterized_circuits) + return QSVM.get_kernel_matrix(self._quantum_instance, self.feature_map, x1_vec, x2_vec) def train(self, data, labels, quantum_instance=None): """ From c4a76db2128bc6dd55868611b811aad97ddfe7d9 Mon Sep 17 00:00:00 2001 From: Richard Chen Date: Wed, 25 Sep 2019 23:45:44 -0400 Subject: [PATCH 21/21] add the flag in uccsd to support parameterized circuits --- .../aqua_extensions/components/variational_forms/uccsd.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py b/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py index a56a6209da..da332229d9 100644 --- a/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py +++ b/qiskit/chemistry/aqua_extensions/components/variational_forms/uccsd.py @@ -168,6 +168,7 @@ def __init__(self, num_qubits, depth, num_orbitals, num_particles, self._bounds = [(-np.pi, np.pi) for _ in range(self._num_parameters)] self._logging_construct_circuit = True + self._support_parameterized_circuit = True @property def single_excitations(self):