Skip to content

Commit

Permalink
Fix VQC training with warm_start=True (#312)
Browse files Browse the repository at this point in the history
* fixes #296 warm_start with vqc

extract the initial_point in TrainableModel from the final point of the
minimization in the existing OptimizerResult object.

* update copyright

* add units tests to check vqc warm_start

* fix grammar

* fix grammar

* initialise -> initialize

* fix typo in test docstring

* add a reno for fix of #296

* fix quotes in reno report

Co-authored-by: Anton Dekusar <62334182+adekusar-drl@users.noreply.github.com>
(cherry picked from commit 5a02c76)
  • Loading branch information
declanmillar authored and mergify-bot committed Feb 12, 2022
1 parent 912439d commit cdd1450
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 2 deletions.
4 changes: 2 additions & 2 deletions qiskit_machine_learning/algorithms/trainable_model.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2021.
# (C) Copyright IBM 2021, 2022.
#
# 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
Expand Down Expand Up @@ -201,7 +201,7 @@ def _choose_initial_point(self) -> np.ndarray:
An array as an initial point
"""
if self._warm_start and self._fit_result is not None:
self._initial_point = self._fit_result[0]
self._initial_point = self._fit_result.x
elif self._initial_point is None:
self._initial_point = algorithm_globals.random.random(self._neural_network.num_weights)
return self._initial_point
Expand Down
11 changes: 11 additions & 0 deletions releasenotes/notes/fix-vqc-warm-start-1c15d3f5742c5e84.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
fixes:
- |
Fixes an issue where ``VQC`` would fail with ``warm_start=True``.
The extraction of the ``initial_point`` in ``TrainableModel`` from the final
point of the minimization had not been updated to reflect the refactor
of optimizers in ``qiskit-terra``; the old ``optimize`` method, that returned
a tuple was deprecated and new method ``minimize`` was created that returns
an ``OptimizerResult`` object. We now correctly recover the final point of
the minimization from previous fits to use for a warm start in subsequent
fits.
64 changes: 64 additions & 0 deletions test/algorithms/classifiers/test_vqc.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,70 @@ def test_multiclass(self, config):
score = classifier.score(X, y)
self.assertGreater(score, 1 / num_classes)

@data(
# optimizer, quantum instance
("cobyla", "statevector"),
("cobyla", "qasm"),
("bfgs", "statevector"),
("bfgs", "qasm"),
(None, "statevector"),
(None, "qasm"),
)
def test_warm_start(self, config):
"""Test VQC with warm_start=True."""
opt, q_i = config

if q_i == "statevector":
quantum_instance = self.sv_quantum_instance
else:
quantum_instance = self.qasm_quantum_instance

if opt == "bfgs":
optimizer = L_BFGS_B(maxiter=5)
elif opt == "cobyla":
optimizer = COBYLA(maxiter=25)
else:
optimizer = None

num_inputs = 2
feature_map = ZZFeatureMap(num_inputs)
ansatz = RealAmplitudes(num_inputs, reps=1)

# Construct the data.
num_samples = 10
# pylint: disable=invalid-name
X = algorithm_globals.random.random((num_samples, num_inputs))
y = 1.0 * (np.sum(X, axis=1) <= 1)
while len(np.unique(y)) == 1:
X = algorithm_globals.random.random((num_samples, num_inputs))
y = 1.0 * (np.sum(X, axis=1) <= 1)
y = np.array([y, 1 - y]).transpose() # VQC requires one-hot encoded input.

# Initialize the VQC.
classifier = VQC(
feature_map=feature_map,
ansatz=ansatz,
optimizer=optimizer,
warm_start=True,
quantum_instance=quantum_instance,
)

# Fit the VQC to the first half of the data.
num_start = num_samples // 2
classifier.fit(X[:num_start, :], y[:num_start])
first_fit_final_point = classifier._fit_result.x

# Fit the VQC to the second half of the data with a warm start.
classifier.fit(X[num_start:, :], y[num_start:])
second_fit_initial_point = classifier._initial_point

# Check the final optimization point from the first fit was used to start the second fit.
np.testing.assert_allclose(first_fit_final_point, second_fit_initial_point)

# Check score.
score = classifier.score(X, y)
self.assertGreater(score, 0.5)


if __name__ == "__main__":
unittest.main()

0 comments on commit cdd1450

Please sign in to comment.