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

Add QuantumKernelTrainer and KernelLoss classes #244

Merged
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
b6399c7
Add QuantumKernelTrainer class, KernelLoss class, and refactor QSVC t…
caleb-johnson Oct 18, 2021
a4803b2
Refactor to unconfuse mypy
caleb-johnson Oct 18, 2021
7344b90
Add runtime client for QKA
caleb-johnson Oct 18, 2021
7a990cb
Remove qka program info from __init__
caleb-johnson Oct 18, 2021
f86f844
Remove vqe language from qka client
caleb-johnson Oct 19, 2021
4b58eb2
Merge branch 'main' into qka-runtime
manoelmarques Oct 29, 2021
33b0d39
Merge branch 'main' into quantum-kernel-trainer
manoelmarques Oct 29, 2021
a7ae58f
Merge branch 'main' of github.com:calebj15/qiskit-machine-learning in…
caleb-johnson Nov 10, 2021
2b524f2
Retract changes to QSVC
caleb-johnson Nov 10, 2021
2bee96e
Address peer review comments. Remove kernels.algorithms package. Make…
caleb-johnson Nov 10, 2021
72b4905
Change SVCAlignment class to SVCLoss and update KernelLoss doc strings.
caleb-johnson Nov 10, 2021
f6202c8
Fix equation in SVCLoss docstring
caleb-johnson Nov 10, 2021
bad47a9
Docstring improvements
caleb-johnson Nov 10, 2021
5949970
Add quantum kernel trainer runtime client and unit tests.
caleb-johnson Nov 10, 2021
6833223
Fix spelling errors
caleb-johnson Nov 10, 2021
47f0d3e
Fix spelling errors
caleb-johnson Nov 10, 2021
1126bbd
Clean up docstring for SVCLoss
caleb-johnson Nov 10, 2021
859ad4f
Clean up docstring for SVCLoss
caleb-johnson Nov 10, 2021
d2323d8
Fix spelling errors
caleb-johnson Nov 10, 2021
45e2aa4
Fix equation formatting in docstring
caleb-johnson Nov 11, 2021
d40345e
Fix equation formatting in docstring
caleb-johnson Nov 11, 2021
6a3e042
New spelling errors
caleb-johnson Nov 11, 2021
2973ca4
New spelling errors
caleb-johnson Nov 11, 2021
67dbc22
Fix spelling errors. Add 'runtime' to pylint dict
caleb-johnson Nov 11, 2021
afa7232
Fix mypy errors
caleb-johnson Nov 11, 2021
eb95125
Fix mypy errors
caleb-johnson Nov 11, 2021
7640bfb
style error
caleb-johnson Nov 11, 2021
f0506dc
style error
caleb-johnson Nov 11, 2021
7989965
spelling error in docstring
caleb-johnson Nov 11, 2021
1e468ee
Fix circular imports due to typing.
caleb-johnson Nov 11, 2021
c7b191e
Fix circular imports due to typing.
caleb-johnson Nov 11, 2021
fe34bfb
Make QuantumKernel class serializable by runtime.
caleb-johnson Nov 16, 2021
47267f1
Update quantum_kernel_trainer.py
caleb-johnson Nov 17, 2021
30d4ec8
Update quantum_kernel_trainer.py
caleb-johnson Nov 17, 2021
6921254
Update quantum_kernel_trainer.py
caleb-johnson Nov 17, 2021
7e51267
Make KernelLoss.get_variational_callable a base method
caleb-johnson Nov 17, 2021
bd417d1
Make SVCLoss more efficient by only calculating kernel once
caleb-johnson Nov 19, 2021
33972a9
Create kernels.algorithms package for QuantumKernelTrainer. Fix all C…
caleb-johnson Nov 22, 2021
c946418
spelling errors
caleb-johnson Nov 22, 2021
83b2330
Clean up kernel loss docstring
caleb-johnson Nov 22, 2021
5af811e
Create kernels.algorithms package for quantum kernel trainer. Clean u…
caleb-johnson Nov 22, 2021
b57d1a4
spelling errors
caleb-johnson Nov 22, 2021
84c38f3
spelling errors
caleb-johnson Nov 22, 2021
676816b
spelling errors
caleb-johnson Nov 22, 2021
a4e4203
Create separate file for kernel loss classes. Fix spelling errors.
caleb-johnson Nov 22, 2021
2ec50d5
add runtime to pylint dict
caleb-johnson Nov 22, 2021
30395f2
add vqe to pylint dict
caleb-johnson Nov 22, 2021
c9ec0e3
Merge branch 'main' into quantum-kernel-trainer
caleb-johnson Nov 22, 2021
eb90863
Merge branch 'main' into qka-runtime
manoelmarques Nov 26, 2021
6214143
Merge branch 'main' into quantum-kernel-trainer
manoelmarques Nov 26, 2021
1f49459
Address peer feedback
caleb-johnson Dec 1, 2021
66f04b2
Merge branch 'qka-runtime' of github.com:calebj15/qiskit-machine-lear…
caleb-johnson Dec 1, 2021
52c2e61
Address peer review comments
caleb-johnson Dec 1, 2021
3849e60
Merge branch 'main' into quantum-kernel-trainer
manoelmarques Dec 2, 2021
a24a563
Remove qkt runtime code from qkt branch
caleb-johnson Dec 6, 2021
199e3be
html errors
caleb-johnson Dec 6, 2021
7131b7c
Merge branch 'main' into quantum-kernel-trainer
manoelmarques Dec 7, 2021
08de1b8
Merge branch 'main' into quantum-kernel-trainer
manoelmarques Dec 7, 2021
44e0c0e
Merge branch 'main' of github.com:calebj15/qiskit-machine-learning in…
caleb-johnson Dec 8, 2021
67c4125
Merge branch 'quantum-kernel-trainer' of github.com:calebj15/qiskit-m…
caleb-johnson Dec 8, 2021
309dd52
Address peer review comments
caleb-johnson Dec 8, 2021
0874a93
Fix mypy errors
caleb-johnson Dec 8, 2021
dd49cd3
Add a quantum kernel training tutorial
caleb-johnson Dec 12, 2021
28589ae
Print results of kernel fitting in tutorial
caleb-johnson Dec 12, 2021
09c75bb
Modify QKT tutorial. Clean up QKT interface.
caleb-johnson Dec 13, 2021
4f18589
Rename QKT tutorial. Fix bad assertion in qkt test.
caleb-johnson Dec 13, 2021
000f7b4
Make QKT tests deterministic. Add to pylintdict
caleb-johnson Dec 13, 2021
fc99b56
Address minor comments in qkt tutorial
caleb-johnson Dec 13, 2021
d6bd020
Fix spelling errors in QKT tutorial
caleb-johnson Dec 13, 2021
d5c8bff
Clean up docstring for qkt
caleb-johnson Dec 13, 2021
4ed685a
Fix comment in qkt tutorial. Remove unnecessary type checks from QKT …
caleb-johnson Dec 14, 2021
5505b61
Clean up docstring in QKT
caleb-johnson Dec 14, 2021
a6625d4
Fix mypy error in qkt
caleb-johnson Dec 14, 2021
c7d03c1
Fix mypy errors in qkt
caleb-johnson Dec 14, 2021
93f8cbb
Fix mypy errors in qkt
caleb-johnson Dec 14, 2021
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
5 changes: 4 additions & 1 deletion qiskit_machine_learning/kernels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
:nosignatures:

QuantumKernel
QuantumKernelTrainer
QuantumKernelTrainerResult

"""

from .quantum_kernel import QuantumKernel
from .quantum_kernel_trainer import QuantumKernelTrainer, QuantumKernelTrainerResult

__all__ = ["QuantumKernel"]
__all__ = ["QuantumKernel", "QuantumKernelTrainer", "QuantumKernelTrainerResult"]
117 changes: 87 additions & 30 deletions qiskit_machine_learning/kernels/quantum_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import numpy as np

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit import Parameter, ParameterVector
from qiskit.circuit import Parameter, ParameterVector, ParameterExpression
from qiskit.circuit.parameterexpression import ParameterValueType
from qiskit.circuit.library import ZZFeatureMap
from qiskit.providers import Backend, BaseBackend
Expand Down Expand Up @@ -77,7 +77,7 @@ def __init__(
self._quantum_instance = quantum_instance

# Setters
self.feature_map = feature_map if feature_map else ZZFeatureMap(2)
self.feature_map = feature_map if feature_map is not None else ZZFeatureMap(2)
if user_parameters is not None:
self.user_parameters = user_parameters

Expand Down Expand Up @@ -122,27 +122,25 @@ def quantum_instance(
@property
def user_parameters(self) -> Optional[Union[ParameterVector, Sequence[Parameter]]]:
"""Return the vector of user parameters."""
return self._user_parameters
return copy.copy(self._user_parameters)
adekusar-drl marked this conversation as resolved.
Show resolved Hide resolved

@user_parameters.setter
def user_parameters(self, user_params: Union[ParameterVector, Sequence[Parameter]]) -> None:
"""Sets the user parameters"""
self._user_param_binds = {user_params[i]: user_params[i] for i, _ in enumerate(user_params)}

self._user_parameters = user_params
self._user_parameters = copy.deepcopy(user_params)

def assign_user_parameters(
self, values: Union[Mapping[Parameter, ParameterValueType], Sequence[ParameterValueType]]
) -> None:
"""
Assign user parameters in the ``QuantumKernel`` feature map.

Args:
values (dict or iterable): Either a dictionary or iterable specifying the new
parameter values. If a dict, it specifies the mapping from ``current_parameter`` to
``new_parameter``, where ``new_parameter`` can be a parameter object or a
numeric value. If an iterable, the elements are assigned to the existing parameters
in the order of ``QuantumKernel.user_parameters``.
parameter values. If a dict, it specifies the mapping from ``current_parameter`` to
``new_parameter``, where ``new_parameter`` can be a parameter expression or a
numeric value. If an iterable, the elements are assigned to the existing parameters
in the order of ``QuantumKernel.user_parameters``.

Raises:
ValueError: Incompatible number of user parameters and values
Expand All @@ -156,14 +154,11 @@ def assign_user_parameters(
"""
adekusar-drl marked this conversation as resolved.
Show resolved Hide resolved
)

if isinstance(values, dict):
unknown_parameters = list(set(values.keys()) - set(self._user_parameters))
if len(unknown_parameters) > 0:
raise ValueError(
f"Cannot bind parameters ({unknown_parameters}) not tracked by the quantum kernel."
)
param_binds = values
else:
# Get the input parameters. These should remain unaffected by assigning of user parameters.
input_params = list(set(self._unbound_feature_map.parameters) - set(self._user_parameters))

# If iterable of values is passed, the length must match length of user_parameters field
if isinstance(values, (Sequence, np.ndarray)):
if len(values) != len(self._user_parameters):
raise ValueError(
f"""
Expand All @@ -172,29 +167,91 @@ def assign_user_parameters(
({len(self._user_parameters)}).
"""
)
param_binds = {param: values[i] for i, param in enumerate(self._user_parameters)}

if self._user_param_binds is None:
self._user_param_binds = param_binds
values = {p: values[i] for i, p in enumerate(self._user_parameters)}
else:
self._user_param_binds.update(param_binds)
if not isinstance(values, dict):
raise ValueError(
f"""
'values' must be of type Dict or Sequence.
Type {type(values)} is not supported.
"""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest indenting these three lines one additional level, to match the preceding line.

)

# All input keys must exist in the circuit
# This check actually catches some well defined assignments;
# however; we throw an error to be consistent with the behavior
# of QuantumCircuit's parameter binding.
unknown_parameters = list(set(values.keys()) - set(self._user_parameters))
if len(unknown_parameters) > 0:
raise ValueError(
f"Cannot bind parameters ({unknown_parameters}) not tracked by the quantum kernel."
)

# Because QuantumKernel supports parameter rebinding, entries of the `values` dictionary must
# be handled differently depending on whether they represent numerical assignments or parameter
# reassignments. However, re-ordering the values dictionary inherently changes the expected
# behavior of parameter binding, as entries in the values dict do not commute with one another
# in general. To resolve this issue, we handle each entry of the values dict one at a time.
for param, bind in values.items():
if isinstance(bind, ParameterExpression):
self._unbound_feature_map.assign_parameters({param: bind}, inplace=True)
adekusar-drl marked this conversation as resolved.
Show resolved Hide resolved

# User params are all non-input params in the unbound feature map
# This list comprehension ensures that self._user_parameters is ordered
# in a way that is consistent with self.feature_map.parameters
self._user_parameters = [
p for p in self._unbound_feature_map.parameters if (p not in input_params)
]

# Remove param if it was overwritten
if param not in self._user_parameters:
del self._user_param_binds[param]

# Add new parameters
for sub_param in bind.parameters:
if sub_param not in self._user_param_binds.keys():
self._user_param_binds[sub_param] = sub_param

# If parameter is being set to expression of itself, user_param_binds
# reflects a self-bind
if param in bind.parameters:
self._user_param_binds[param] = param

# If assignment is numerical, update the param_binds
elif isinstance(bind, numbers.Number):
self._user_param_binds[param] = bind

else:
raise ValueError(
f"""
Parameters can only be bound to numeric values,
Parameters, or ParameterExpressions. Type {type(bind)}
is not supported.
"""
)

# Reorder dict according to self._user_parameters
self._user_param_binds = {
param: self._user_param_binds[param] for param in self._user_parameters
}

# Update feature map with numerical parameter assignments
self._feature_map = self._unbound_feature_map.assign_parameters(self._user_param_binds)

@property
def user_param_binds(self) -> Optional[Mapping[Parameter, float]]:
"""Return a copy of the current user parameter mappings for the feature map circuit."""
return copy.deepcopy(self._user_param_binds)

def bind_user_parameters(self, values: Sequence[float]) -> None:
def bind_user_parameters(
self, values: Union[Mapping[Parameter, ParameterValueType], Sequence[ParameterValueType]]
) -> None:
"""
Alternate function signature for ``assign_user_parameters``

Args:
values (iterable): [value1, value2, ...]
"""
self.assign_user_parameters(values)

def get_unbound_parameters(self) -> List[Parameter]:
def get_unbound_user_parameters(self) -> List[Parameter]:
"""Returns a list of any unbound user parameters in the feature map circuit."""
unbound_user_params = []
if self._user_param_binds is not None:
Expand Down Expand Up @@ -236,7 +293,7 @@ def construct_circuit(
- unbound user parameters in the feature map circuit
"""
# Ensure all user parameters have been bound in the feature map circuit.
unbound_params = self.get_unbound_parameters()
unbound_params = self.get_unbound_user_parameters()
if unbound_params:
raise ValueError(
f"""
Expand Down Expand Up @@ -322,7 +379,7 @@ def evaluate(self, x_vec: np.ndarray, y_vec: np.ndarray = None) -> np.ndarray:
and feature map can not be modified to match.
"""
# Ensure all user parameters have been bound in the feature map circuit.
unbound_params = self.get_unbound_parameters()
unbound_params = self.get_unbound_user_parameters()
if unbound_params:
raise ValueError(
f"""
Expand Down
Loading