-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Fixed ZZFeatureMap not accepting a list of entanglement #12767
Changes from 7 commits
85dfd3f
a4254cc
e6db70b
80bb7f6
2970842
0fbc472
67a2908
adc63c6
d9827f7
0fb66c4
3c94135
79b71d9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,8 +11,7 @@ | |
# that they have been altered from the originals. | ||
|
||
"""The Pauli expansion circuit module.""" | ||
|
||
from typing import Optional, Callable, List, Union | ||
from typing import Optional, Callable, List, Union, Sequence, Dict, Tuple | ||
from functools import reduce | ||
import numpy as np | ||
|
||
|
@@ -116,7 +115,7 @@ def __init__( | |
self, | ||
feature_dimension: Optional[int] = None, | ||
reps: int = 2, | ||
entanglement: Union[str, List[List[int]], Callable[[int], List[int]]] = "full", | ||
entanglement: Union[str, Dict[int, List[Tuple[int]]], Dict[int, List[List[int]]]] = "full", | ||
alpha: float = 2.0, | ||
paulis: Optional[List[str]] = None, | ||
data_map_func: Optional[Callable[[np.ndarray], float]] = None, | ||
|
@@ -129,8 +128,11 @@ def __init__( | |
Args: | ||
feature_dimension: Number of qubits in the circuit. | ||
reps: The number of repeated circuits. | ||
entanglement: Specifies the entanglement structure. Refer to | ||
:class:`~qiskit.circuit.library.NLocal` for detail. | ||
entanglement: Specifies the entanglement structure. Can be a string (``'full'``, | ||
``'linear'``, ``'reverse_linear'``, ``'circular'`` or ``'sca'``) or can be a | ||
dictionary where the keys represent the number of qubits and the values are list | ||
of integer-pairs specifying the indices of qubits that are entangled with one | ||
another. For example: ``{1: [(0,), (2,)], 2: [(0,1), (2,0)]}`` | ||
alpha: The Pauli rotation factor, multiplicative to the pauli rotations | ||
paulis: A list of strings for to-be-used paulis. If None are provided, ``['Z', 'ZZ']`` | ||
will be used. | ||
|
@@ -156,6 +158,7 @@ def __init__( | |
self._data_map_func = data_map_func or self_product | ||
self._paulis = paulis or ["Z", "ZZ"] | ||
self._alpha = alpha | ||
self.entanglement = entanglement | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary? This attribute should already be accessible via |
||
|
||
def _parameter_generator( | ||
self, rep: int, block: int, indices: List[int] | ||
|
@@ -281,6 +284,40 @@ def cx_chain(circuit, inverse=False): | |
basis_change(evo, inverse=True) | ||
return evo | ||
|
||
def get_entangler_map( | ||
self, rep_num: int, block_num: int, num_block_qubits: int | ||
) -> Sequence[Sequence[int]]: | ||
|
||
# entanglement is Dict[int, List[List[int]]] | ||
if isinstance(self.entanglement, dict): | ||
if all( | ||
isinstance(e2, (int, np.int32, np.int64)) | ||
for key in self.entanglement.keys() | ||
for en in self.entanglement[key] | ||
for e2 in en | ||
): | ||
for qb, ent in self.entanglement.items(): | ||
for ind, en in enumerate(ent): | ||
if len(en) != qb: | ||
raise ValueError( | ||
f"For num_qubits = {qb}, entanglement must be a " | ||
f"tuple of length {qb}. You specified {en}." | ||
) | ||
self.entanglement[qb][ind] = tuple(map(int, en)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure we need this; users should already provide integers (as per the type hint) and I think the code works with both tuples and lists. |
||
|
||
# Check if the entanglement is specified for all the pauli blocks being used | ||
for pauli in self.paulis: | ||
if len(pauli) not in self.entanglement.keys(): | ||
raise ValueError(f"No entanglement specified for {pauli} pauli.") | ||
|
||
return self.entanglement[num_block_qubits] | ||
|
||
else: | ||
# if the entanglement is not Dict[int, List[List[int]]] or | ||
# Dict[int, List[Tuple[int]]] then we fall back on the original | ||
# `get_entangler_map()` method from NLocal | ||
return super().get_entangler_map(rep_num, block_num, num_block_qubits) | ||
|
||
|
||
def self_product(x: np.ndarray) -> float: | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
--- | ||
features_circuits: | ||
- | | ||
The entanglement argument in `PauliFeatureMap` and `ZZFeatureMap` will no longer support List[int] or List[List[int]]. Instead, | ||
the entanglement structure in `PauliFeatureMap` and `ZZFeatureMap` can now be specified as a dictionary where the keys represent | ||
the number of qubits, and the values are lists of integer tuples that define which qubits are entangled with one another. This | ||
allows for more flexibility in constructing feature maps tailored to specific quantum algorithms. | ||
Example usage:: | ||
from qiskit.circuit.library import PauliFeatureMap | ||
|
||
entanglement = { | ||
1: [(0,), (2,)], | ||
2: [(0, 1), (1, 2)], | ||
3: [(0, 1, 2)], | ||
} | ||
params=[0, 1, 3.14] | ||
qc = PauliFeatureMap(3, reps=2, paulis=['Z', 'ZZ', 'ZZZ'], entanglement=entanglement, insert_barriers=True) | ||
qc.decompose().draw('mpl') | ||
|
||
|
||
|
||
ElePT marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should still accept a callable, which can return an entanglement specific for a repetition. So the signature should be:
Union[str, Dict[...], Callable[[int], Union[str | Dict[...]]
.