rigetti / pyquil

A Python library for quantum programming using Quil.
http://docs.rigetti.com
Apache License 2.0
1.4k stars 342 forks source link

Read classical registers while keeping the wavefunction #796

Open apozas opened 5 years ago

apozas commented 5 years ago

In pyQuil < 2.0, it was possible to simulate postselection when simulating wavefunctions, with codes of this style:

from pyquil import Program
from pyquil.api import QVMConnection
from pyquil.gates import *

state_prep = Program(H(0))
state_prep += I(1)
state_prep.measure(0, 0)
conditional_bit = 0

qvm = QVMConnection()
finished = False
while not finished:
    wf = qvm.wavefunction(program.if_then(conditional_bit, I(0)), [conditional_bit])
    finished = wf.classical_memory[conditional_bit]

# Here would come other operations with wf, which is now postselected on the first qubit being |1>.

I have not managed to reproduce this type of program with pyQuil 2.3. The most similar solution (that is quite specific) that I have found is the following:

from pyquil import Program
from pyquil.api import QVMConnection
from pyquil.gates import *

conditional_bit = 0

state_prep = Program(H(0))
c = cr.declare('ro', 'BIT', 1)
state_prep += MEASURE(0, c[conditional_bit])

qvm = QVMConnection()
finished = False
while not finished:
    wf = qvm.wavefunction(program.if_then(('ro', conditional_bit), I(0)))
    finished = (abs(wf.amplitudes[0]) < 1e-10)

# Here would come other operations with wf, which is now postselected on the first qubit being |1>

So, ultimately my question is, is there a standard way to access classical registers from outside a Quil program?

Thank you

mpharrigan commented 5 years ago

We changed the quil memory model so that classical registers have names. The second argument to qvm.wavefunction() would specify the addresses of classical memory to return, which used to just be numbers. Now it would have to be (perhaps) a list of tuples of variable names and offsets or we could do something like always return the full classical memory space of the qvm. As you have found out, we just removed the ability to inspect the classical memory with wavefunction methods.

As a workaround, you could use the PyQVM object, which implements a QVM in python. As such, you can peek and poke at classical memory however you want


from pyquil import Program
from pyquil.gates import *
from pyquil.pyqvm import PyQVM

qvm = PyQVM(n_qubits=1)
program = Program(H(0))
conditional_bit = program.declare('conditional_bit')
program.measure(0, conditional_bit[0])
program.if_then(conditional_bit, I(0))
print(program)

finished = False
while not finished:
    qvm.execute(program)
    print('wf', qvm.wf_simulator.wf.reshape(-1))
    print(qvm.ram['conditional_bit'])
    finished = bool(qvm.ram['conditional_bit'])

please note that PyQVM.execute() will not reset the state of the machine before a subsequent call (in contrast to QVMConnection.wavefunction()

apozas commented 5 years ago

Thank you for the information about the actual status and possible workarounds. Regarding this workaround, is it possible to perform noisy computations with it? A bit outside the question I posed, my actual intention was doing runs of the sorts of programs above, but in simulators where I could specify gate and measurement noise levels. Hence my attempts using QVMConnection instead of PyQVM.

mpharrigan commented 5 years ago

One important note that you might already know: QVMConnection's noise models stochastically evolve a wavefunction. Essentially, each time you call wavefunction it will randomly select whether to apply noise or not. Therefore, you must run a given program multiple times and gather statistics in order to draw conclusions about the performance of your circuit under noise.

PyQVM can use a density matrix simulator as a backend which currently has rudimentary support for noise models.

qvm = PyQVM(n_qubits=1, post_gate_noise_probabilities={'dephasing': 0.05})

This will apply a dephasing kraus map to each qubit after each gate, which is probably not what you want. But all the building blocks are there if you're willing to tinker with when/how noise operations get applied. Since it's a full density matrix simulation you won't have to be concerned with stochastic evolution, in contrast to the note above.

If you're willing to get your hands dirty, this is one avenue to pursue. I'd highly recommend reading through the code to figure out exactly what the noise model is and modifying it to suit your needs. We'd also love if you wanted to contribute changes to the density matrix simulation code to support better experimentation with noise models. Otherwise, it's on our roadmap to improve the interface for noisy density matrix simulations in the future.

apozas commented 5 years ago

Great, thank you for your comments. Indeed, a stochastic evolution is what I am initially looking for, but thank you for pointing it out. Also, I will start playing around with PyQVM, and see what I can get from it.