qiskit-community / qiskit-braket-provider

Qiskit-Braket provider to execute Qiskit programs on quantum computing hardware devices through Amazon Braket.
https://qiskit-community.github.io/qiskit-braket-provider/
Apache License 2.0
57 stars 45 forks source link

Create Qiskit `QuantumCircuit` from Braket `Circuit` objects #88

Closed kshitijc closed 5 months ago

kshitijc commented 1 year ago

Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.

What is the expected behavior?

Currently, the Qiskit Braket Provider allows you to convert Qiskit QuantumCircuit objects to Braket Circuit objects. It would be useful to be able to convert Braket Circuit objects to Qiskit QuantumCircuit objects, allowing users to make use of any of the utilities available in Qiskit to visualize or transform the QuantumCircuit before optionally translating it back to a Braket Circuit if needed.

The method:

def from_braket_circuit(circuit: Circuit) -> QuantumCircuit:
   ...

could potentially leverage the existing from_qasm_str method available in the QuantumCircuit class or operate directly on the Braket Circuit objects, and could also be potentially exposed on the Quantum circuit class to have a similar usage pattern as the from_qasm_str method:

QuantumCircuit.from_braket_circuit = from_braket_circuit

Acceptance criteria

ogunsegun commented 1 year ago

I will like to Contributor all the code should have a method, like unit tests

ogunsegun commented 1 year ago

https://github.com/ogunsegun/Rebulid-of-Create-Qiskit-QuantumCircuit-from-Braket-Circuit-objects.git

JordanAWS commented 1 year ago

@ogunsegun Can you submit a pull request on the main repo using a feature branch?

ogunsegun commented 1 year ago

plz how am my going to do that @JordanAWS because the is in my github

SimoneGasperini commented 1 year ago

@ogunsegun: take a look here to learn how to properly submit pull requests and start contributing to open-source projects.

By the way, I'm also interested in working on this. @kshitijc @JordanAWS: can you please add me as an assignee? Quick question: is it ok to implement the from_braket_circuit(circuit: Circuit) -> QuantumCircuit function in the qiskit_braket_provider/providers/adapter.py module? It sounds reasonable to me because the reverse function convert_qiskit_to_braket_circuit(circuit: QuantumCircuit) -> Circuit is there as well.

ogunsegun commented 1 year ago

thanks @SimoneGasperini

ogunsegun commented 1 year ago

@kshitijc I have put my code on my github but am unable put it here

JordanAWS commented 1 year ago

@kshitijc I have put my code on my github but am unable put it here

@ogunsegun Did you read the link that Kshitij provided on how to submit pull requests?

ogunsegun commented 1 year ago

@kshitijc @JordanAWS have submit my code on pull requests, please is that the way I should have submit it. Create Qiskit QuantumCircuit from Braket Circuit objects! #101

SimoneGasperini commented 1 year ago

@kshitijc @JordanAWS I'm working on this but I encountered several problems that I'm not sure how to handle. I start recalling the criteria to fulfill in the implementation of the function from_braket_circuit:

The implementation should be able to handle the Braket circuit containing the following appropriately:

The first bullet about the supported gates conversion is the minimal requirement and I already have something working including parametric gates. This is straightforward because each quantum gate in Braket has a corresponding gate in Qiskit which performs exactly the same unitary operation on a single or a specific subset of qubits.

Concerning the second bullet, we are already in troubles because the way Braket and Qiskit implement standard (computational basis) measurements, or access the final state_vector/density_matrix, or measure observables expectation values, is different by design. The function convert_qiskit_to_braket_circuit is implemented so that each QuantumCircuit.measure Qiskit instruction is translated into a Circuit.probability call even if the two operations don't have exactly the same underlying logic. Nevertheless, in this case one can assume that the best Braket -> Qiskit approach for standard measurements could be the reverse Circuit.probability -> QuantumCircuit.measure conversion. But what about accessing for instance the final quantum state_vector/density_matrix (or any other kind of object defined in the result_types module)? In Bracket this is done by calling the appropriate method on the circuit which modifies the Circuit instance itself:

braket_circuit.state_vector()

While in Qiskit there is a separate module to do this and the operation doesn't affect the QuantumCircuit instance at all:

from qiskit.quantum_info import Statevector
sv = Statevector(qiskit_circuit)

Similar problem is there also for measuring observables expectation values. In Braket we act in-place directly on the circuit:

from braket.circuits.observable import Observable

obs = Observable.X()
braket_circuit.sample(obs)

While in Qiskit we use the Sampler primitive passing the observable defined by using again the quantum_info module.

Converting noise instructions Braket -> Qiskit turns out to be even more complicated because, while the former allows you to do something like Circuit.bit_flip(target, probability), the latter, as far as I know, only lets you to create a NoiseModel and attach it to the backed used for running the circuit (again, not directly to the circuit itself).

Finally, regarding the Verbatim blocks, I guess that the only way to convert them from Braket to Qiskit would be to put a barrier between each operation performed from the beginning to the end of the block, forcing Qiskit to skip any possible transpilation. However, this could be misleading because in the conversion Qiskit -> Braket all the barriers are simply ignored.

Can you please help me or maybe give me some feedback? Isn't this the case of loosening the criteria for implementing such a function from_braket_circuit?

kshitijc commented 1 year ago

Thank you for these detailed questions @SimoneGasperini! Essentially, we wish to enable users to be able to convert Braket circuits to Qiskit circuits, perform transformations/visualize, and be able to convert these back to Braket circuits. So we want to ensure that the converted Qiskit circuit is reasonable to work with/visualize, and can be translated back to the Braket circuit. For components of the Braket circuit which aren't a component of the Qiskit circuit, you could look at storing them in the metadata property of the Qiskit circuit and then ensuring that these are accounted for when converting to a Braket circuit. Additionally, we could add a notebook demonstrating how to work with these transformations here and display info messages where relevant. These would be useful in scenarios where there is no direct mapping between Braket and Qiskit circuits.

Also note that if the Circuit object has no result types specified, then by default this has the implied meaning of measure on all qubits used.

Please let us know if you need more clarifications!

SimoneGasperini commented 1 year ago

Thank you very much @kshitijc for the clarifications! If I got it well what we would like to have is a function to make braket_circuit == convert_qiskit_to_braket_circuit(from_braket_circuit(braket_circuit)) evaluate to True, right?

I already submitted the PR #102 and will keep working on it but I'm not sure to understand how can I handle Braket features which have no direct mapping to Qiskit (i.e. all the ones in my comment above). I know that Qiskit QuantumCircuit has a metadata dict attribute to store free-form data which could then be used for converting the circuit back to Braket. The problem is that I don't see this metadata used in the already available function convert_qiskit_to_braket_circuit so I believe that maybe also that function would need to be accordingly updated to really enable users to do what you mean.

kshitijc commented 1 year ago

braket_circuit == convert_qiskit_to_braket_circuit(from_braket_circuit(braket_circuit)) evaluate to True, right?

Yes, that's right!

maybe also that function would need to be accordingly updated to really enable users to do what you mean

Yes, that's correct

SimoneGasperini commented 1 year ago

@kshitijc Is there a function to generate random Braket circuits? It could be very useful for testing purposes. If not available, can you provide it just as a reference for testing? It should possibly include all the Braket features we would like to support in the Braket Circuit conversion to Qiskit QuantumCircuit: gate instructions, result types, observables, noise instructions, verbatim blocks.

JordanAWS commented 1 year ago

@SimoneGasperini we don't have a random circuit generator currently available in Braket, but we will take your suggestion into consideration for our roadmap :)

If you're looking for same circuits to run, a good set of test cases with be the Braket examples repo:

denvitko commented 1 year ago

Greetings, I created a PR for this issue. Submissions for UnitaryHack close now, but I would like to continue working on it. So far, I am able to get braket == convert_qiskit_to_braket_circuit(from_braket_circuit(braket)) as True for certain circuits.

There will need to be done some modifications to the convert_qiskit_to_braket_circuit() function because it gives me errors for, e.g., a measurement on a different qubit and a classical bit.

JordanAWS commented 1 year ago

@denvitko @SimoneGasperini Thanks for your work so far on this issue! Since you both submitted a PR before the June 13 deadline, you will have until June 20th to get your code approved and merged. It looks like it will be a race to the finish line, unless the two of you would like to collaborate.

Please don't hesitate to reach out if you need support.

denvitko commented 1 year ago

@SimoneGasperini Are you still interested in working on this issue?

SimoneGasperini commented 1 year ago

Hi @denvitko! Yes I'm still interested. If you are too, we can collaborate.

denvitko commented 1 year ago

@JordanAWS I am facing some issues with the convert_qiskit_to_braket_circuit() function. I generate random Qiskit circuits (using Qiskit random_circuit() function), pass them to the converter, but it gives me errors (KeyErrors at the moment).

Could you maybe point out what are the most must-have features that should work? Because I am not sure if there is enough time to fix it all 🙃.

denvitko commented 1 year ago

Also @JordanAWS, do you plan to implement draw() method for Braket Circuit, I think that it would be nice. Just putting print(self) in the method would be enough.

SimoneGasperini commented 1 year ago

@JordanAWS I am facing some issues with the convert_qiskit_to_braket_circuit() function. I generate random Qiskit circuits (using Qiskit random_circuit() function), pass them to the converter, but it gives me errors (KeyErrors at the moment).

This is because not all the gates/instructions defined in Qiskit are also available in Braket: for example qiskit_gate_names_to_braket_gates["iswap"] will raise a KeyError. A workaround could be to generate your random Qiskit circuit and then transpile it by passing a list of basis_gates containing only the gates supported by the Braket adapter:

from qiskit import transpile
from qiskit_braket_provider.providers.adapter import (
    qiskit_to_braket_gate_names_mapping,
    convert_qiskit_to_braket_circuit
)

qiskit =  # random circuit
basis_gates = list(qiskit_to_braket_gate_names_mapping.keys())
qiskit = transpile(qiskit, basis_gates=basis_gates, optimization_level=0)

braket = convert_qiskit_to_braket_circuit(qiskit)

However, for this issue we are interested in the conversion from Braket to Qiskit so we don't have to worry too much about gates/instructions that Qiskit supports and Braket doesn't.


Also @JordanAWS, do you plan to implement draw() method for Braket Circuit, I think that it would be nice. Just putting print(self) in the method would be enough.

I think that if the only thing the method Circuit.draw() would do was to call print(self), then it wouldn't make any sense to define it at all (a user could simply use print(braket_circuit)).

denvitko commented 1 year ago

I am trying to add XX+YY gate to the adapter.py, most of it was pretty straightforward, the only line I was not sure about is this one: "xx_plus_yy": lambda theta, beta: [gates.XY(theta)],. It seems that Qiskit has two parameters for the XX+YY gate, but Braket XY gate takes only one.

@JordanAWS In case you have access to this, I believe there is a mistake in Braket docs, the comments for XX gate and XY gate are swapped. obrazek

JordanAWS commented 1 year ago

@JordanAWS In case you have access to this, I believe there is a mistake in Braket docs, the comments for XX gate and XY gate are swapped. obrazek Good catch! Have reported this to the docs team

JordanAWS commented 1 year ago

Yeah, currently we just use print(circuit) rather than having a separate circuit.draw() method: https://docs.aws.amazon.com/braket/latest/developerguide/braket-constructing-circuit.html

denvitko commented 1 year ago

Greetings, I am sorry I haven't done more. Despite facing some issues with Python, I am happy that I tried to tackle this problem! I posted my updates in the PR.

robotAstray commented 11 months ago

Hello @JordanAWS is this issue resolved?

kshitijc commented 10 months ago

This issue is still open. Happy to collaborate with you if you'd be interested in contributing.

jcjaskula-aws commented 5 months ago

closed by #136