Export a description of a custom circuit

Hi pennylane team,
I would like to share my circuit with a third party so that they could run it or at least open and study it using standard pennylane tools.

The problem is that I can’t pickle the circuit because it would also require custom libraries that I can’t share atm.

I was hoping there was a simple way to export json-like description of a circuit so that it could be easily rebuild using default pennylane tools.

I couldn’t find anything in the docs.

Hi @Einar_Gabbassov, I’m not sure if I understand your problem.

You can’t share the Python code used to generate the circuit?

Maybe you could create a custom template. You can find how to do this here at the end of the page.

Please let me know if this is what you were looking for. I hope this helps.

Hi @CatalinaAlbornoz
Yes, I can’t share the code that builds the circuit. Even if I could, there is too much stuff involved to build the circuit. The circuit structure also depends on the input data. So the whole thing becomes hard to manage.

Usually, one can describe a circuit’s gates and gate parameters in some structured file (e.g. json) or plain text with instruction how to recreate the circuit.

I guess some example will be helpful here.
Let’s say I have a 1 qubit device with a single RZ(theta) gate. In order to compute theta, I develop a big complicated program that estimates the correct theta and creates the RZ(theta) gate.
Now I want to share the circuit. Why share the entire complicated program? I have already estimated theta and can just pass on the description of the final circuit.
I could just write a text file which says: hook RZ gate and set theta=x.
This description is platform agnostic and can be shared easily.

It is a pretty common thing in many fields.

Thanks for showing me the template webpage, but this is not exactly what I need.

I found this in Circ
https://quantumai.google/cirq/interop

Hey @Einar_Gabbassov! Circuit serialization is something we are working on, so it would be super helpful to hear what aspects you would like to serialize :slightly_smiling_face:

  • Are you looking to serialize just the internal circuit (so operations, which operations have trainable parameters, and the measurement statistics)?

  • Or, alternatively, are you looking to serialize the full QNode, including metadata such as the device, number of shots, differentiation methods?

Finally, would serialization of the optimization loop itself be something you are interested in, or is it more the quantum parts of your model you would like to serialize?

hi @josh,
So far I would like to serialize the internal circuit so that whoever has the description file can rebuild the circuit.
It is likely that a third party will use pennylane as-well, however they might use their custom simulator device.

Ideally, I would like to share a jupyter notebook with some example code where I setup a device (subject to be changed by the third party), rebuild the final circuit from the description file and sample the device using that circuit.

At the moment, I’m thinking about some really dirty quick workaround for this. Something like saving all the gates and associated attributes such as wires, parameters into an ordered dictionary. And then have a shared jupyter notebook which rebuilds the circuit. This is not ideal of course.

Please let me know if you have anything like that readily available.

Hi @Einar_Gabbassov, there is a parameter of qnodes called specs.

When you execute a circuit you create a quantum tape and you can then find the specifications of that tape.

For example, for the following code

import pennylane as qml
dev = qml.device("lightning.qubit",wires=1)

@qml.qnode(dev)
def circuit(x):
    qml.RX(x,wires=0)
    return qml.expval(qml.PauliZ(0))

circuit(0.5)
print(circuit.specs)

you would get the following specs.
{‘gate_sizes’: defaultdict(<class ‘int’>, {1: 1}), ‘gate_types’: defaultdict(<class ‘int’>, {‘RX’: 1}), ‘num_operations’: 1, ‘num_observables’: 1, ‘num_diagonalizing_gates’: 0, ‘num_used_wires’: 1, ‘depth’: 1, ‘num_trainable_params’: 1, ‘num_parameter_shift_executions’: 3, ‘num_device_wires’: 1, ‘device_name’: ‘lightning.qubit’, ‘diff_method’: ‘parameter-shift’}

I know this is not perfect but it may be helpful to share the specs and then reconstruct the circuit from them.

What do you think?

Hi @CatalinaAlbornoz, this is definitely a useful bit of information! Thank you.
In the end, I ended up writing my own add-hoc solution which pickles a circuit description file and then creates a circuit class that rebuilds all the gates and parameters on an object initialization.

Hi @Einar_Gabbassov, it’s great that you managed to create a solution.

Please let me know if we can help in any other way.

Enjoy using PennyLane!

Hi @Einar_Gabbassov, just in case it still helps you can use to_openqasm and from_qasm to do what you need.

Here’s an example of how to use it

import pennylane as qml

dev = qml.device("lightning.qubit",wires=1)

@qml.qnode(dev)
def circuit(x):
    qml.RX(x,wires=0)
    return qml.expval(qml.PauliZ(0))

circuit(0.5)
# Convert to QASM (str)
t = circuit.qtape.to_openqasm()
print(t)

# Remove the measurement
t_cut = t[:-23]
print(t_cut) 

This gives you a string that you can send to someone. The other person can then use the following code:

my_circuit = qml.from_qasm(t_cut) # Create a template

@qml.qnode(dev)
def new_circuit(y):
    # You can add new things to the circuit
    qml.RY(y, wires=0)
    # Add your old circuit
    my_circuit(wires=[0])
    return qml.expval(qml.PauliZ(0))

#Draw
drawer = qml.draw(new_circuit)
print(drawer(y = 0.3))

I hope this helps.

1 Like

hi @CatalinaAlbornoz! This looks great!
When doing export using to_openqasm() is it necessary to call the circuit? In your example, you define a circuit and then call it. My circuit is too large to be executed on a device. So if I do

circuit(params)
# Convert to QASM (str)
t = circuit.qtape.to_openqasm()

it will take forever to run the first line. Is there a way to get the tape without actually executing the circuit?

Hi @Einar_Gabbassov! Yes, there is a way of ‘constructing’ the circuit without executing it:

circuit.construct([params], {})
# Convert to QASM (str)
t = circuit.qtape.to_openqasm()

Here, construct accepts the list of QNode arguments, and a dictionary of keyword arguments.

We should make this more easily accessible, perhaps via qml.to_openqasm(circuit)!

2 Likes

thanks a lot @josh This was super helpful!
I wonder why all this is not searchable in the Docs?

Unfortunately this is very much an implementation detail of PennyLane, and was not designed at the time to be part of the public API! Something we would like to fix though :slightly_smiling_face: