qiskit-community / qiskit-machine-learning

Quantum Machine Learning
https://qiskit-community.github.io/qiskit-machine-learning/
Apache License 2.0
647 stars 316 forks source link

QuantumKernel doesn't work with RawFeatureVector. #205

Closed vbelis closed 2 years ago

vbelis commented 3 years ago

Information

The behavior has been firstly reproduced also locally:

What is the current behavior?

QiskitError: 'Cannot define a ParameterizedInitialize with unbound parameters' occurs when trying to train a QSVM classifier on the breast_cancer data set, using RawFeatureVector as a feature map for the kernel. Same error has occurred with other data sets as well: adhoc data set, random data generated using numpy in the interval [0,1).

 Traceback (most recent call last):
  File "<ipython-input-2-5b15e6dd39e3>", line 24, in <module>
    main()
  File "<ipython-input-2-5b15e6dd39e3>", line 18, in main
    adhoc_svc.fit(train_features, train_labels)
  File "/opt/conda/lib/python3.8/site-packages/sklearn/svm/_base.py", line 226, in fit
    fit(X, y, sample_weight, solver_type, kernel, random_seed=seed)
  File "/opt/conda/lib/python3.8/site-packages/sklearn/svm/_base.py", line 266, in _dense_fit
    X = self._compute_kernel(X)
  File "/opt/conda/lib/python3.8/site-packages/sklearn/svm/_base.py", line 396, in _compute_kernel
    kernel = self.kernel(X, self.__Xfit)
  File "/opt/conda/lib/python3.8/site-packages/qiskit_machine_learning/kernels/quantum_kernel.py", line 306, in evaluate
    parameterized_circuit = self.construct_circuit(
  File "/opt/conda/lib/python3.8/site-packages/qiskit_machine_learning/kernels/quantum_kernel.py", line 149, in construct_circuit
    qc.append(psi_y_dag.to_instruction().inverse(), qc.qubits)
  File "/opt/conda/lib/python3.8/site-packages/qiskit/circuit/instruction.py", line 389, in inverse
    inverse_gate.definition._data = [
  File "/opt/conda/lib/python3.8/site-packages/qiskit/circuit/instruction.py", line 390, in <listcomp>
    (inst.inverse(), qargs, cargs) for inst, qargs, cargs in reversed(self._definition)
  File "/opt/conda/lib/python3.8/site-packages/qiskit/circuit/instruction.py", line 364, in inverse
    if self.definition is None:
  File "/opt/conda/lib/python3.8/site-packages/qiskit/circuit/instruction.py", line 221, in definition
    self._define()
  File "/opt/conda/lib/python3.8/site-packages/qiskit_machine_learning/circuit/library/raw_feature_vector.py", line 170, in _define
    raise QiskitError("Cannot define a ParameterizedInitialize with unbound parameters")
QiskitError: 'Cannot define a ParameterizedInitialize with unbound parameters'

Steps to reproduce the problem

import matplotlib.pyplot as plt
import numpy as np

from sklearn.svm import SVC

from qiskit import Aer
from qiskit.circuit.library import ZZFeatureMap
from qiskit.utils import QuantumInstance, algorithm_globals
from qiskit_machine_learning.algorithms import QSVC
from qiskit_machine_learning.kernels import QuantumKernel
from qiskit_machine_learning.datasets import breast_cancer
from qiskit_machine_learning.circuit.library import RawFeatureVector

seed = 12345
algorithm_globals.random_seed = seed

def main():
    n_features = 2
    train_features, train_labels, test_features, test_labels = breast_cancer(
    training_size=20,test_size=5, n=n_features,one_hot = False
)

    feature_map = RawFeatureVector(n_features)
    backend = QuantumInstance(Aer.get_backend('qasm_simulator'), shots=1024,
                                    seed_simulator=seed, seed_transpiler=seed)

    kernel = QuantumKernel(feature_map=feature_map, quantum_instance=backend)

    qsvm = SVC(kernel=kernel.evaluate) #pass custom kernel as callable function
    qsvm.fit(train_features, train_labels)

    score = qsvm.score(test_features, test_labels)

    print(f'Callable kernel classification test score: {adhoc_score}')

if __name__ == '__main__':
    main()

What is the expected behavior?

The model is expected to train.

Suggested solutions

No clue. I hope these might be helpful: the code runs properly if ZZFeatureMap is used for the construction of the kernel. In the code tests I checked that in the QuantumKernel test the ZZFeatureMap is used, whereas in the RawFeatureVector test the RawFeatureVector circuit is used with a VQC.

adekusar-drl commented 3 years ago

When a quantum kernel is being computed, an inverse circuit is constructed to be re-used with training data. The problem is that RawFeatureVector does not support inverse operation which is required to compute a quantum kernel. From the RawFeatureVector documentation: "The circuit contains a placeholder instruction that can only be synthesized/defined when all parameters are bound." Basically RawFeatureVector is not suitable for QuantumKernel right now.

Let me know if you have questions.

vbelis commented 3 years ago

Thank you for the info and answer. I would like to understand better some points from the RawFeatureVector documentation.

adekusar-drl commented 3 years ago

Thank you for the info and answer. I would like to understand better some points from the RawFeatureVector documentation.

* How does the statement "The circuit contains a placeholder instruction that can only be synthesized/defined when all parameters are bound." connect to the fact that there is no inverse operation? In this context what does it mean to have "bound parameters"?

"bound parameters" means that all parameters of a circuit have an actual value bound, not a parameter placeholder, e.g. instead of x we have a value of, say, 2. In case of RawFeatureVector it is a bit more complicated. A circuit that represents RawFeatureVectordoes not exist until all parameters are known and bound. In other words, this is what is stated in the cited documentation.

In QuantumKernel a feature map is inverted first and only then parameters are bound to their corresponding placeholders and then the kernel circuit is executed. That's why inverse operation failed, parameter values are not yet known, and that's why you see this exception.

* In the documentation it states: "Since initialization is implemented via a `QuantumCircuit.initialize()` call, this circuit
  can't be used with gradient based optimizers, one can see a warning that gradients can't be computed.". Does this mean that the `RawFeatureVector` cannot be used with `VQC` when using gradient descent for the training?

Yes, that's right. You can use gradient free optimizers, e.g. COBYLA, in this case. Please take a look at the example in the readme file: https://github.com/adekusar-drl/qiskit-machine-learning/blob/main/README.md

* I would really appreciate if you could inform me if you are planning to implement the `RawFeatureVector` compatibility with`QuantumKernel` and/or the posibility of using it with gradient based optimizers?

We don't have plans to enhance RawFeatureVector. May be @stefan-woerner has some ideas here.

vbelis commented 3 years ago

Thank you once again for the detailed and prompt reply.

stefan-woerner commented 3 years ago

@vbelis using RawFeatureVector (RFV) as feature map in the QuantumKernel is actually the same as just computing a quadratic classical kernel: Suppose two state |phi> and |psi> coming from a feature map applied to 2 data points, then, the quantum kernel computes |<phi|psi>|^2. Since RFV maps a classical vector x to \sum_{i=0}^{2^n-1} x_i |i> the quantum kernel with RFV will just do |x^T y|^2, i.e., a classical inner product of your data points. You can get the behavior you are looking for by just using sklearn.SVC with a quadratic kernel: svc = SVC(kernel='poly', degree=2).

vbelis commented 2 years ago

@stefan-woerner thank you for the message and explanation. It is clear that an amplitude encoding circuit as a feature map, i.e., RawFeatureVector, reproduces the quadratic kernel. My goal was to extend the RawFeatureVector circuit by attaching additional circuits achieving different feature maps for the QuantumKernel, some of which could also have trainable parameters. If I have understood correctly from this thread, this won't be possible with the current version of the RawFeatureVector and QuantumKernel classes. However, this was possible in the corresponding older versions of these classes (before the deprecation of Aqua, e.g., 0.23.2).

adekusar-drl commented 2 years ago

There is a PR #219 where we will introduce trainable parameters in QuantumKernel. Maybe it is what you are looking for?

vbelis commented 2 years ago

Thanks for pointing this out, it seems that PR #219 addresses trainable parameters in QuantumKernel. However, I would really appreciate any information about the following. In older versions of qiskit, before the Aqua deprecation (e.g., qiskit version 0.23.2), it was possible to create a QSVM equipped with a kernel that is constructed from the RawFeatureVector. This would simply create an SVM with a quadratic kernel, as @stefan-woerner pointed out in the above comment. However, in the old versions it was also possible to attach circuits to the generated RawFeatureVector circuit, and use that extended circuit as a custom feature map for the kernel. In that case, the constructed kernel is not strictly equivalent to a simple classical one. Currently, this functionality is not provided by the QuantumKernel and RawFeatureVector implementations. Would it be possible to add this functionality back to the current versions of qiskit and qiskit-machine-learning?

adekusar-drl commented 2 years ago

@vbelis A lot of things have been changed in the transition from Aqua to dedicated applications modules. For now we don't have plans to enhance RawFeatureVector.

vbelis commented 2 years ago

@adekusar-drl thank you for the valuable information provided in this thread. Since I do not have currently any questions or remarks I will close the issue.