Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix VQC training with warm_start=True #312

Merged
merged 11 commits into from
Feb 12, 2022
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()