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 parameter handling for NLocal(..., flatten=True) and standard gates #13482

Merged
merged 4 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
25 changes: 12 additions & 13 deletions qiskit/circuit/library/n_local/n_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -1109,11 +1109,11 @@ def _build_rotation_layer(self, circuit, param_iter, i):
if k not in skipped_blocks
)
for qubits in all_qubits:
instr = CircuitInstruction(
simple_block.gate(*itertools.islice(param_iter, simple_block.num_params)),
circuit._append_standard_gate(
simple_block.gate,
qubits,
[next(param_iter) for _ in range(simple_block.num_params)],
)
circuit._append(instr)
else:
block_indices = [
list(range(k * block.num_qubits, (k + 1) * block.num_qubits))
Expand All @@ -1139,11 +1139,12 @@ def _build_entanglement_layer(self, circuit, param_iter, i):
# It's actually nontrivially faster to use a listcomp and pass that to `tuple`
# than to pass a generator expression directly.
# pylint: disable=consider-using-generator
instr = CircuitInstruction(
simple_block.gate(*itertools.islice(param_iter, simple_block.num_params)),
tuple([target_qubits[i] for i in indices]),
qubits = tuple([target_qubits[i] for i in indices])
circuit._append_standard_gate(
simple_block.gate,
qubits,
[next(param_iter) for _ in range(simple_block.num_params)],
)
circuit._append(instr)
else:
# apply the operations in the layer
for indices in entangler_map:
Expand Down Expand Up @@ -1277,7 +1278,6 @@ def get_entangler_map(


_StdlibGateResult = collections.namedtuple("_StdlibGateResult", ("gate", "num_params"))
_STANDARD_GATE_MAPPING = get_standard_gate_name_mapping()


def _stdlib_gate_from_simple_block(block: QuantumCircuit) -> _StdlibGateResult | None:
Expand All @@ -1291,14 +1291,13 @@ def _stdlib_gate_from_simple_block(block: QuantumCircuit) -> _StdlibGateResult |
if (
instruction.clbits
or tuple(instruction.qubits) != tuple(block.qubits)
or (
getattr(_STANDARD_GATE_MAPPING.get(instruction.operation.name), "base_class", None)
is not instruction.operation.base_class
)
or getattr(instruction.operation, "_standard_gate", None) is None
or tuple(instruction.operation.params) != tuple(block.parameters)
):
return None
return _StdlibGateResult(instruction.operation.base_class, len(instruction.operation.params))
return _StdlibGateResult(
instruction.operation._standard_gate, len(instruction.operation.params)
)


def _normalize_entanglement(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
fixes:
- |
Fixed a bug in :class:`.NLocal` and derivatives, occurring when ``flatten=True`` was set
and standard gates backed by Rust were used. When binding parameters in-place to such a
circuit, the new parameter values were not correctly propagated to the circuit instructions.

The circuit could still be compiled and used as expected, however, inspecting the individual
operations would still show the old parameter values. For example::

from qiskit.circuit.library import EfficientSU2

circuit = EfficientSU2(2, flatten=True)
circuit.assign_parameters([1.25] * circuit.num_parameters, inplace=True)

print(circuit.data[0].operation.params) # would print θ[0] instead of 1.25

Fixed `#13478 <https://github.com/Qiskit/qiskit/issues/13478>`__.
15 changes: 15 additions & 0 deletions test/python/circuit/library/test_nlocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,21 @@ def test_initial_state_as_circuit_object(self):

self.assertCircuitEqual(ref, expected)

def test_inplace_assignment_with_cache(self):
"""Test parameters are correctly re-bound in the cached gates.

This test requires building with the Rust feature "cache_pygates" enabled, otherwise
it does not test what it is supposed to.

Regression test of #13478.
"""
qc = EfficientSU2(2, flatten=True)
binds = [1.25] * qc.num_parameters

qc.assign_parameters(binds, inplace=True)
bound_op = qc.data[0].operation
self.assertAlmostEqual(bound_op.params[0], binds[0])


@ddt
class TestNLocalFunction(QiskitTestCase):
Expand Down
Loading