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

Multicontrol Rz fix #9574

Closed

Conversation

AlexisRalli
Copy link

@AlexisRalli AlexisRalli commented Feb 13, 2023

Summary

I've currently written code to decompose multi control rotation gates properly (see issue 9202 ): QuantumCircuit.mcrz, QuantumCircuit.mcrx, QuantumCircuit.mcry

The update includes a new file in generalized_gates that for any given single qubit unitary matrix will generate a multicontrol quantum circuit of it using O(n^2) two qubit gates.

Details and comments

Currently there are problems with the unit tests. I think this could stem from the following:

from qiskit import QuantumCircuit, transpile
import numpy as np

angle = -np.pi/3
tt = QuantumCircuit(1)
tt.rz(angle, 0)

gg = tt.to_gate()

gg = gg.control(1, 'TEST', '1')

bb =  QuantumCircuit(2)
bb.append(gg, [0,1])



print(bb.decompose().decompose().draw())

>> 
                                      ┌────────────┐
q_0: ──────────────■───────────────■──┤ U(0,0,π/6) ├
     ┌──────────┐┌─┴─┐┌─────────┐┌─┴─┐└────────────┘
q_1: ┤ Rz(-π/6) ├┤ X ├┤ Rz(π/6) ├┤ X ├──────────────
     └──────────┘└───┘└─────────┘└───┘              

i.e. there is a relative phase in the gate decomposition that should NOT be there. I think the unit tests take this into account, when they should not. However, by correcting the codebase these tests then fail.

Note the updated code for QuantumCircuit.mcrz builds the correct quantum circuits:

angle = -np.pi/3
vv = QuantumCircuit(2)
vv.mcrz(angle, [0],1)
print(vv.decompose().draw())

                                      
q_0: ──────────────■───────────────■──
     ┌──────────┐┌─┴─┐┌─────────┐┌─┴─┐
q_1: ┤ Rz(-π/6) ├┤ X ├┤ Rz(π/6) ├┤ X ├
     └──────────┘└───┘└─────────┘└───┘

How should I proceed?

Thanks for the help!

(side note) The MCU2Gate class allows arbitrary control single qubit gates to be built as follows:

from qiskit import QuantumCircuit
from scipy.stats import unitary_group
from qiskit.circuit.library.generalized_gates.multicontrol_arb_gate import MCU2Gate

ctrl_state = '110'
n_c = len(ctrl_state)
U = unitary_group.rvs(2) # random unitary

test_gate = MCU2Gate(U,
                n_c,
                ctrl_state)

nq = test_gate.num_qubits
IBM_c = QuantumCircuit(nq)
IBM_c.append(test_gate, range(nq))
IBM_c.draw()
>>
q_0: ───■────
        │    
q_1: ───■────
        │    
q_2: ───o────
     ┌──┴───┐
q_3: ┤ U(2) ├
     └──────┘

@AlexisRalli AlexisRalli requested a review from a team as a code owner February 13, 2023 16:09
@qiskit-bot qiskit-bot added the Community PR PRs from contributors that are not 'members' of the Qiskit repo label Feb 13, 2023
@qiskit-bot
Copy link
Collaborator

Thank you for opening a new pull request.

Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient.

While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone.

One or more of the the following people are requested to review this:

@CLAassistant
Copy link

CLAassistant commented Feb 13, 2023

CLA assistant check
All committers have signed the CLA.

@AlexisRalli
Copy link
Author

@javabster @jakelishman @1ucian0 - Thanks for your help during the qiskit office hours. It turns out the code still fails some unit tests even without the mcx functionality.

@jakelishman
Copy link
Member

jakelishman commented Feb 13, 2023

@alexanderivrii, @Cryoris (and @ShellyGarion, I think?): please could you two (three if I'm right about Shelly too) have a look at this? Alexis came to the office hours earlier today and we talked about what he'd been working on, and in principle it all looks like something we're very interested in to me.

Sasha: for you, my questions are mostly about how you think it's best to organise the code in the short-term with other synthesis efforts going on. I suspect that it's mostly ok to mirror the current style, since our hope is to more completely overhaul MCX/MCU/etc synthesis later.

Julien and Shelly: I think Julien you did the original (or most recent) implementations of the MC synthesis routines, and Shelly you're definitely more up-to-date on the literature and other methods than I am.

@AlexisRalli
Copy link
Author

AlexisRalli commented Feb 13, 2023

@alexanderivrii, @Cryoris (and @ShellyGarion, I think?): please could you two have a look at this? Alexis came to the office hours earlier today and we talked about what he'd been working on, and in principle it all looks like something we're very interested in to me.

Sasha: for you, my questions are mostly about how you think it's best to organise the code in the short-term with other synthesis efforts going on. I suspect that it's mostly ok to mirror the current style, since our hope is to more completely overhaul MCX/MCU/etc synthesis later.

Julien and Shelly: I think Julien you did the original (or most recent) implementations of the MC synthesis routines, and Shelly you're definitely more up-to-date on the literature and other methods than I am.

Thanks @jakelishman - to reproduce the McX code run the following:

from qiskit.circuit.library.generalized_gates.multicontrol_arb_gate import MCU2Gate
from qiskit.quantum_info import Operator
from qiskit import QuantumCircuit, transpile
import numpy as np
from qiskit import QuantumCircuit

X = np.array([[0,1],[1,0]])

for n_controls in range(2,10): # < -- change range for more controls!
    cqbits = list(range(n_controls))
    targ = cqbits[-1]+1
    nc = max(*cqbits, targ)+1
    
    ###
    diag_circuit = QuantumCircuit(nc)
    diag_circuit.h(targ) # change from Z to X basis
    diag = np.ones(2**nc)
    diag[-1]=-1
    diag_circuit.diagonal(diag.tolist(), [*cqbits, targ])
    diag_circuit.h(targ) # change from Z to X basis
    diag_circuit = transpile(diag_circuit, basis_gates=['cx','u'])
    
    ###
    custom_mcX = QuantumCircuit(nc)
    custom_mcX_gate = MCU2Gate(X,
                        n_controls,
                       label='X')
    custom_mcX.append(custom_mcX_gate, [*cqbits, targ])
    custom_mcX = transpile(custom_mcX, basis_gates=['cx','u'])
    ##
    std_mcX = QuantumCircuit(nc)
    std_mcX.mcx(cqbits, targ, mode='noancilla')
    std_mcX = transpile(std_mcX, basis_gates=['cx','u'])
    
    assert(np.allclose(Operator(std_mcX).data,
                       Operator(diag_circuit).data))
    assert(np.allclose(Operator(std_mcX).data,
                   Operator(diag_circuit).data))
    ####
    print('N_controls:', n_controls)
    print('*'*10)
    print('DIAG :', diag_circuit.num_nonlocal_gates())
    print('NEW  :', custom_mcX.num_nonlocal_gates())
    print('OLD  :', std_mcX.num_nonlocal_gates())
    print()

This outputs the following (counts number of CNOT gates):

N_controls: 2
**********
DIAG : 6
NEW  : 10
OLD  : 6

N_controls: 3
**********
DIAG : 14
NEW  : 26
OLD  : 14

N_controls: 4
**********
DIAG : 30
NEW  : 50
OLD  : 36

N_controls: 5
**********
DIAG : 62
NEW  : 82
OLD  : 92

N_controls: 6
**********
DIAG : 126
NEW  : 122
OLD  : 188

N_controls: 7
**********
DIAG : 254
NEW  : 170
OLD  : 380

N_controls: 8
**********
DIAG : 510
NEW  : 226
OLD  : 764

N_controls: 9
**********
DIAG : 1022
NEW  : 290
OLD  : 1532

i.e. the diagonal approach should be used up to 5 controls and then the new decomposition after 6 controls



QuantumCircuit.mcrx = mcrx
QuantumCircuit.mcry = mcry
QuantumCircuit.mcrz = mcrz


class ControlRotationGate(ControlledGate):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @AlexisRalli, I think that ucry, ucrx and ucrz in this folder already implements Theorem 8 of https://arxiv.org/pdf/quant-ph/0406176.pdf.

if n_c == 1: # cu
_apply_cu(self, 0, 0, lam, control_qubits[0], target_qubit, use_basis_gates=use_basis_gates)
if n_c <= 6:
ncrz = ControlRotationGate(lam, n_c, axis="z")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can probably use 'QuantumCircuit.ucrz' with several id gates and one rz. Or maybe adapt ControlRotationGate to use ucrz. Nice observation that with a small number of qubits the multiplexer is a good approach.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that mcrz should be in another PR, because the mcrz issue.

use_basis_gates=use_basis_gates,
)
if n_c <= 6:
ncrx = ControlRotationGate(theta, n_c, axis="x")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use ucrx here?

rygate = (
numpy.cos(theta / 2) * numpy.eye(2) - 1j * numpy.sin(theta / 2) * YGate().__array__()
)
ncry = MCU2Gate(rygate, n_c, label=f"Ry({theta:0.3f})")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use ucry here?

from qiskit.extensions.quantum_initializer.squ import SingleQubitUnitary


class MCU2Gate(ControlledGate):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion is to create a different PR with the MCU2Gate because this class is for "any multicontrol version of any single qubit unitary matrix (gate)" and this PR is about multicontrolled rotation gates.

@adjs
Copy link
Contributor

adjs commented Mar 17, 2023

Hi @AlexisRalli. I'm not on the qiskit team, but I left some suggestions. The main one is that this PR could be separeted in two. One for the MCU2Gate class and another for multicontrolled rotations with few control bits.

@ShellyGarion
Copy link
Member

can we close this PR as #9836 has been merged?

@1ucian0
Copy link
Member

1ucian0 commented May 25, 2023

thanks @adjs for the review and #9836 !

@AlexisRalli, if you think #9202 is still not fixed or that #9836 is somehow incomplete, please open a new issue. If this PR has elements that are not covered by #9836, you can also open a new PR from a branch of AlexisRalli:multicontrol_rot_only_fix.

thanks!

@1ucian0 1ucian0 closed this May 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Community PR PRs from contributors that are not 'members' of the Qiskit repo
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

8 participants