Skip to content
This repository has been archived by the owner on Dec 7, 2021. It is now read-only.

Use parameterized circuits to speed up some algorithms instead of using CircuitCache #693

Merged
merged 24 commits into from
Oct 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
18 changes: 16 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,31 @@ Changelog](http://keepachangelog.com/en/1.0.0/).
[UNRELEASED](https://github.com/Qiskit/qiskit-aqua/compare/0.6.0...HEAD)
========================================================================

Changed
-----

- `VQE`, `VQC` and `QSVM` now use parameterized circuits if it is available to save time
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. (#693)

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. (#693)

[0.6.0](https://github.com/Qiskit/qiskit-aqua/compare/0.5.5...0.6.0) - 2019-08-22
=================================================================================

Expand All @@ -48,7 +62,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
Expand Down
4 changes: 3 additions & 1 deletion qiskit/aqua/algorithms/adaptive/qaoa/var_form.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
"""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.quantum_info import Pauli

from qiskit.aqua.operators import WeightedPauliOperator, op_converter

# pylint: disable=invalid-name
Expand Down Expand Up @@ -70,6 +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.support_parameterized_circuit = True

def construct_circuit(self, angles):
""" construct circuit """
Expand Down
6 changes: 6 additions & 0 deletions qiskit/aqua/algorithms/adaptive/vq_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 """
Expand Down Expand Up @@ -196,3 +198,7 @@ def var_form(self, new_value):
def optimizer(self):
""" returns optimizer """
return self._optimizer

def cleanup_parameterized_circuits(self):
""" set parameterized circuits to None """
self._parameterized_circuits = None
45 changes: 30 additions & 15 deletions qiskit/aqua/algorithms/adaptive/vqc/vqc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -265,6 +267,9 @@ 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
def init_params(cls, params, algo_input):
Expand Down Expand Up @@ -338,27 +343,36 @@ 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.support_parameterized_circuit and \
self._feature_map.support_parameterized_circuit and \
self._parameterized_circuits is None:

parameterized_circuits = self.construct_circuit(
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]

_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._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)
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._parameterized_circuits is not None)

circuit_id = 0
predicted_probs = []
Expand All @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -595,6 +609,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):
Expand Down
54 changes: 40 additions & 14 deletions qiskit/aqua/algorithms/adaptive/vqe/vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -127,6 +128,9 @@ 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
def init_params(cls, params, algo_input):
Expand Down Expand Up @@ -355,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
Expand All @@ -369,21 +375,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.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,
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._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:
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':
Expand All @@ -394,7 +420,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._parameterized_circuits is not None,
**extra_args)

for idx, _ in enumerate(parameter_sets):
mean, std = self._operator.evaluate_with_result(
Expand Down Expand Up @@ -438,10 +467,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']

Expand Down
71 changes: 50 additions & 21 deletions qiskit/aqua/algorithms/many_sample/qsvm/qsvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -148,7 +150,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')
Expand Down Expand Up @@ -218,6 +220,8 @@ def get_kernel_matrix(quantum_instance, feature_map, x1_vec, x2_vec=None):
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
Expand All @@ -244,18 +248,29 @@ 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)
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)
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]
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)
results = quantum_instance.execute(circuits,
had_transpiled=use_parameterized_circuits)

if logger.isEnabledFor(logging.DEBUG):
logger.debug("Calculating overlap:")
Expand Down Expand Up @@ -284,15 +299,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:")
Expand Down
Loading