• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

qiskit-community / qiskit-algorithms / 15802199099

13 Jun 2025 01:21PM CUT coverage: 90.448%. Remained the same
15802199099

push

github

web-flow
Fix getter/setter type mismatch (#232)

1 of 1 new or added line in 1 file covered. (100.0%)

6401 of 7077 relevant lines covered (90.45%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.53
/qiskit_algorithms/gradients/reverse/derive_circuit.py
1
# This code is part of a Qiskit project.
2
#
3
# (C) Copyright IBM 2022, 2024.
4
#
5
# This code is licensed under the Apache License, Version 2.0. You may
6
# obtain a copy of this license in the LICENSE.txt file in the root directory
7
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
#
9
# Any modifications or derivative works of this code must retain this
10
# copyright notice, and modified files need to carry a notice indicating
11
# that they have been altered from the originals.
12

13
"""Split a circuit into subcircuits, each containing a single parameterized gate."""
14

15
from __future__ import annotations
1✔
16
import itertools
1✔
17
from collections.abc import Sequence
1✔
18

19
from qiskit.circuit import QuantumCircuit, Parameter, Gate, ParameterExpression
1✔
20
from qiskit.circuit.library import RXGate, RYGate, RZGate, CRXGate, CRYGate, CRZGate
1✔
21

22

23
def gradient_lookup(gate: Gate) -> list[tuple[complex, QuantumCircuit]]:
1✔
24
    """Returns a circuit implementing the gradient of the input gate.
25

26
    Args:
27
        gate: The gate whose derivative is returned.
28

29
    Returns:
30
        The derivative of the input gate as list of ``(coeff, circuit)`` pairs,
31
        where the sum of all ``coeff * circuit`` elements describes the full derivative.
32
        The circuit is the unitary part of the derivative with a potential separate ``coeff``.
33
        The output is a list as derivatives of e.g. controlled gates can only be described
34
        as a sum of ``coeff * circuit`` pairs.
35

36
    Raises:
37
        NotImplementedError: If the derivative of ``gate`` is not implemented.
38
    """
39

40
    param = gate.params[0]
1✔
41
    if isinstance(gate, RXGate):
1✔
42
        derivative = QuantumCircuit(gate.num_qubits)
1✔
43
        derivative.rx(param, 0)
1✔
44
        derivative.x(0)
1✔
45
        return [(-0.5j, derivative)]
1✔
46
    if isinstance(gate, RYGate):
1✔
47
        derivative = QuantumCircuit(gate.num_qubits)
1✔
48
        derivative.ry(param, 0)
1✔
49
        derivative.y(0)
1✔
50
        return [(-0.5j, derivative)]
1✔
51
    if isinstance(gate, RZGate):
1✔
52
        derivative = QuantumCircuit(gate.num_qubits)
1✔
53
        derivative.rz(param, 0)
1✔
54
        derivative.z(0)
1✔
55
        return [(-0.5j, derivative)]
1✔
56
    if isinstance(gate, CRXGate):
×
57
        proj1 = QuantumCircuit(gate.num_qubits)
×
58
        proj1.rx(param, 1)
×
59
        proj1.x(1)
×
60

61
        proj2 = QuantumCircuit(gate.num_qubits)
×
62
        proj2.z(0)
×
63
        proj2.rx(param, 1)
×
64
        proj2.x(1)
×
65

66
        return [(-0.25j, proj1), (0.25j, proj2)]
×
67
    if isinstance(gate, CRYGate):
×
68
        proj1 = QuantumCircuit(gate.num_qubits)
×
69
        proj1.ry(param, 1)
×
70
        proj1.y(1)
×
71

72
        proj2 = QuantumCircuit(gate.num_qubits)
×
73
        proj2.z(0)
×
74
        proj2.ry(param, 1)
×
75
        proj2.y(1)
×
76

77
        return [(-0.25j, proj1), (0.25j, proj2)]
×
78
    if isinstance(gate, CRZGate):
×
79
        proj1 = QuantumCircuit(gate.num_qubits)
×
80
        proj1.rz(param, 1)
×
81
        proj1.z(1)
×
82

83
        proj2 = QuantumCircuit(gate.num_qubits)
×
84
        proj2.z(0)
×
85
        proj2.rz(param, 1)
×
86
        proj2.z(1)
×
87

88
        return [(-0.25j, proj1), (0.25j, proj2)]
×
89
    raise NotImplementedError("Cannot implement gradient for", gate)
×
90

91

92
def derive_circuit(
1✔
93
    circuit: QuantumCircuit, parameter: Parameter, check: bool = True
94
) -> Sequence[tuple[complex, QuantumCircuit]]:
95
    """Return the analytic gradient expression of the input circuit wrt. a single parameter.
96

97
    Returns a list of ``(coeff, gradient_circuit)`` tuples, where the derivative of the circuit is
98
    given by the sum of the gradient circuits multiplied by their coefficient.
99

100
    For example, the circuit::
101

102
           ┌───┐┌───────┐┌─────┐
103
        q: ┤ H ├┤ Rx(x) ├┤ Sdg ├
104
           └───┘└───────┘└─────┘
105

106
    returns the coefficient `-0.5j` and the circuit equivalent to::
107

108
           ┌───┐┌───────┐┌───┐┌─────┐
109
        q: ┤ H ├┤ Rx(x) ├┤ X ├┤ Sdg ├
110
           └───┘└───────┘└───┘└─────┘
111

112
    as the derivative of `Rx(x)` is `-0.5j Rx(x) X`.
113

114
    Args:
115
        circuit: The quantum circuit to derive.
116
        parameter: The parameter with respect to which we derive.
117
        check: If ``True`` (default) check that the parameter is valid and that no product
118
            rule is required.
119

120
    Returns:
121
        A list of ``(coeff, gradient_circuit)`` tuples.
122

123
    Raises:
124
        ValueError: If ``parameter`` is of the wrong type.
125
        ValueError: If ``parameter`` is not in this circuit.
126
        NotImplementedError: If a non-unique parameter is added, as the product rule is not yet
127
            supported in this function.
128
    """
129
    if check:
1✔
130
        # this is added as useful user-warning, since sometimes ``ParameterExpression``s are
131
        # passed around instead of ``Parameter``s
132
        if not isinstance(parameter, Parameter):
1✔
133
            raise ValueError(f"parameter must be of type Parameter, not {type(parameter)}.")
×
134

135
        if parameter not in circuit.parameters:
1✔
136
            raise ValueError(f"The parameter {parameter} is not in this circuit.")
×
137

138
        # check uniqueness
139
        seen_parameters: set[Parameter] = set()
1✔
140
        for instruction in circuit.data:
1✔
141
            # get parameters in the current operation
142
            new_parameters = set()
1✔
143
            for p in instruction.operation.params:
1✔
144
                if isinstance(p, ParameterExpression):
1✔
145
                    new_parameters.update(p.parameters)
1✔
146

147
            if duplicates := seen_parameters.intersection(new_parameters):
1✔
148
                raise NotImplementedError(
1✔
149
                    "Product rule is not supported, circuit parameters must be unique, but "
150
                    f"{duplicates} are duplicated."
151
                )
152

153
            seen_parameters.update(new_parameters)
1✔
154

155
    summands, op_context = [], []
1✔
156
    for i, op in enumerate(circuit.data):
1✔
157
        gate = op.operation
1✔
158
        op_context.append((op.qubits, op.clbits))
1✔
159
        if parameter in gate.params:
1✔
160
            coeffs_and_grads = gradient_lookup(gate)
1✔
161
            summands += [coeffs_and_grads]
1✔
162
        else:
163
            summands += [[(1, gate)]]
1✔
164

165
    gradient = []
1✔
166
    for product_rule_term in itertools.product(*summands):
1✔
167
        summand_circuit = QuantumCircuit(*circuit.qregs)
1✔
168
        c = complex(1)
1✔
169
        for i, term in enumerate(product_rule_term):
1✔
170
            c *= term[0]
1✔
171
            # Qiskit changed the format of the stored value. The newer Qiskit has this internal
172
            # method to go from the older (legacy) format to new. This logic may need updating
173
            # at some point if this internal method goes away.
174
            if hasattr(summand_circuit.data, "_resolve_legacy_value"):
1✔
175
                value = summand_circuit.data._resolve_legacy_value(term[1], *op_context[i])
1✔
176
                summand_circuit.data.append(value)
1✔
177
            else:
178
                summand_circuit.data.append([term[1], *op_context[i]])
×
179
        gradient += [(c, summand_circuit.copy())]
1✔
180

181
    return gradient
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc