Qiskit / qiskit

Qiskit is an open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
https://www.ibm.com/quantum/qiskit
Apache License 2.0
5.1k stars 2.34k forks source link

Help to transpile a parameterized circuit with more than 3 controls #13192

Open Augusto12 opened 1 week ago

Augusto12 commented 1 week ago

Environment

What is happening?

I can't transpile a parameterized circuit with more than 3 controls

How can we reproduce the issue?

from qiskit.compiler import transpile
from qiskit.circuit import QuantumCircuit, ParameterVector

p = ParameterVector('p',4)

pqc = QuantumCircuit(4)
for i in range(4):
    pqc.ry(p[i],i)

pqc = pqc.control(4,0,annotated=True)
pqc = transpile(pqc, basis_gates=['u1', 'u2', 'u3', 'cx'], optimization_level=3)

What should happen?

I got the error: QiskitError: 'Cannot synthesize MCRY with unbound parameter: p[0].'

Any suggestions?

No response

ACE07-Sev commented 1 week ago

Apparently, it's because they haven't implemented the q_ancillae in add_control() method of qiskit.circuit.add_control.py.

def control(
    operation: Gate | ControlledGate,
    num_ctrl_qubits: int | None = 1,
    label: str | None = None,
    ctrl_state: str | int | None = None,
) -> ControlledGate:
    """Return controlled version of gate using controlled rotations. This function
    first checks the name of the operation to see if it knows of a method from which
    to generate a controlled version. Currently, these are ``x``, ``rx``, ``ry``, and ``rz``.
    If a method is not directly known, it calls the unroller to convert to `u1`, `u3`,
    and `cx` gates.

    Args:
        operation: The gate used to create the ControlledGate.
        num_ctrl_qubits: The number of controls to add to gate (default=1).
        label: An optional gate label.
        ctrl_state: The control state in decimal or as
            a bitstring (e.g. '111'). If specified as a bitstring the length
            must equal num_ctrl_qubits, MSB on left. If None, use
            2**num_ctrl_qubits-1.

    Returns:
        Controlled version of gate.

    Raises:
        CircuitError: gate contains non-gate in definition
    """
    from math import pi

    # pylint: disable=cyclic-import
    from qiskit.circuit import controlledgate

    ctrl_state = _ctrl_state_to_int(ctrl_state, num_ctrl_qubits)

    q_control = QuantumRegister(num_ctrl_qubits, name="control")
    q_target = QuantumRegister(operation.num_qubits, name="target")
    q_ancillae = None  # TODO: add

Which causes the q_ancillae to be [] when mcry is called. Since it's empty, the length of it is 0, which means that

if mode is None:
        # if enough ancillary qubits are provided, use the 'v-chain' method
        additional_vchain = MCXGate.get_num_ancilla_qubits(len(control_qubits), "v-chain")
        if len(ancillary_qubits) >= additional_vchain:
            mode = "basic"
        else:
            mode = "noancilla"

Will always choose "noancilla" given MCXGate.get_num_ancilla_qubits(4, "v-chain") will evaluate to 2. So, the method uses "noancilla" and because you have 4 control qubits:

elif mode == "noancilla":
        n_c = len(control_qubits)
        if n_c == 1:  # cu
            _apply_cu(
                self, theta, 0, 0, control_qubits[0], target_qubit, use_basis_gates=use_basis_gates
            )
        elif n_c < 4:
            theta_step = theta * (1 / (2 ** (n_c - 1)))
            _apply_mcu_graycode(
                self,
                theta_step,
                0,
                0,
                control_qubits,
                target_qubit,
                use_basis_gates=use_basis_gates,
            )
        else:
            if isinstance(theta, ParameterExpression):
                raise QiskitError(f"Cannot synthesize MCRY with unbound parameter: {theta}.")

            cgate = _mcsu2_real_diagonal(
                RYGate(theta).to_matrix(),
                num_controls=len(control_qubits),
                use_basis_gates=use_basis_gates,
            )
            self.compose(cgate, control_qubits + [target_qubit], inplace=True)

It will go to the else block, and since you're using a ParameterExpression it raises the error. I think you have to wait until they finish adding the TODO part.

alexanderivrii commented 1 week ago

Indeed, we currently do not have methods to transpile parameterized MCRY gates with unbound parameters, regardless of the number of ancillas, @Cryoris and @ShellyGarion please correct me if I am wrong. For instance, the function __mcsu2_realdiagonal, used in the code snippet above, requires all of the parameters to be floats and does not work with general parameter expressions.

What you can do it to call _assignparameters prior to transpilation (this was recently added in #12869), e.g. the following code does work:

from qiskit.compiler import transpile
from qiskit.circuit import QuantumCircuit, ParameterVector

p = ParameterVector('p',4)

pqc = QuantumCircuit(4)
for i in range(4):
    pqc.ry(p[i],i)

pqc = pqc.control(4,0,annotated=True)
pqc = pqc.assign_parameters([1, 2, 3, 4])  # <------ BINDING PARAMETERS
pqc = transpile(pqc, basis_gates=['u1', 'u2', 'u3', 'cx'], optimization_level=3)

Also note that by default assign_parameters returns a new circuit, but you can also (alternatively) use pqc.assign_parameters([1, 2, 3, 4], inplace=True)