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

Update gradient logic for Qiskit Rust circuit data implementation #188

Merged
merged 6 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
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
23 changes: 20 additions & 3 deletions qiskit_algorithms/gradients/reverse/derive_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,18 @@ def derive_circuit(
if parameter not in circuit.parameters:
raise ValueError(f"The parameter {parameter} is not in this circuit.")

if len(circuit._parameter_table[parameter]) > 1:
raise NotImplementedError("No product rule support yet, circuit parameters must be unique.")
if hasattr(circuit, "_parameter_table"):
if len(circuit._parameter_table[parameter]) > 1:
raise NotImplementedError(
"Product rule is not supported, circuit parameters must be unique."
)
else:
# Qiskit is moving circuit functionality to Rust and with the updated logic the former attribute
# which was used no longer exists.
if circuit._data._get_entry_count(parameter) > 1:
raise NotImplementedError(
"Product rule is not supported, circuit parameters must be unique."
)

summands, op_context = [], []
for i, op in enumerate(circuit.data):
Expand All @@ -151,7 +161,14 @@ def derive_circuit(
c = complex(1)
for i, term in enumerate(product_rule_term):
c *= term[0]
summand_circuit.data.append([term[1], *op_context[i]])
# Qiskit changed the format of the stored value. The newer Qiskit has this internal
# method to go from the older (legacy) format to new. This logic may need updating
# at some point if this internal method goes away.
if hasattr(summand_circuit.data, "_resolve_legacy_value"):
value = summand_circuit.data._resolve_legacy_value(term[1], *op_context[i])
summand_circuit.data.append(value)
else:
summand_circuit.data.append([term[1], *op_context[i]])
gradient += [(c, summand_circuit.copy())]

return gradient
9 changes: 8 additions & 1 deletion qiskit_algorithms/gradients/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of a Qiskit project.
#
# (C) Copyright IBM 2022, 2023.
# (C) Copyright IBM 2022, 2024.
#
# 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 @@ -137,6 +137,13 @@ def _make_lin_comb_gradient_circuit(
lin_comb_circuit.h(qr_aux)
if add_measurement:
lin_comb_circuit.measure(qr_aux, cr_aux)
# This next line which assigns data is a workaround otherwise the
# circuit parameters may not be properly recognized as data is now
# managed in Rust and changing things may break parameters - making a
# copy of itself by assignment sorts things out.
# See https://github.com/Qiskit/qiskit/blob/main/releasenotes/notes
# /circuit-gates-rust-5c6ab6c58f7fd2c9.yaml#L47-L79
lin_comb_circuit.data = lin_comb_circuit.data
Cryoris marked this conversation as resolved.
Show resolved Hide resolved
lin_comb_circuits[p] = lin_comb_circuit

return lin_comb_circuits
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
other:
- |
Aspects of the gradients internal implementation, which manipulate circuits more
directly, have been updated now that circuit data is being handled by Rust so it's
compatible with the former Python way as well as the new Qiskit Rust implementation.
12 changes: 12 additions & 0 deletions test/gradients/test_estimator_gradient.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,18 @@ def operations_callback(op):
with self.subTest(msg="assert result is correct"):
self.assertAlmostEqual(result.gradients[0].item(), expect, places=5)

def test_product_rule_check(self):
"""Test product rule check."""
p = Parameter("p")
qc = QuantumCircuit(1)
qc.rx(p, 0)
qc.ry(p, 0)

from qiskit_algorithms.gradients.reverse.derive_circuit import derive_circuit

with self.assertRaises(NotImplementedError):
_ = derive_circuit(qc, p)


if __name__ == "__main__":
unittest.main()
Loading