QPUs vs. QVMs
qc = get_qc("2q-qvm")
From a pyQuil perspective, changing from a QVM to a QPU (and vice versa) is usually as simple as changing the string within the
get_qc()function call. For example, to target the
Aspen-9QPU, you can change the line above to:
qc = get_qc("Aspen-9")
In both cases above, the
qcobject will be an instance of a pyQuil
QuantumComputeris comprised of:
- A quantum abstract machine, accessed via
- An abstract compiler, accessed via
If your code accesses either
qc.compiler, it's important to understand that the
AbstractCompilerimplementations differ between QPU and QVM use cases.
For instance, if your code targets a QVM,
qc.qamwill be a
qc.compilerwill be a
QVMCompilerinstance. However, if your code targets a QPU,
qc.qamwill be a
qc.compilerwill be a
While these subcomponents follow common interfaces, namely
AbstractCompiler, there may be some methods or properties that are accessible on the QPU-based instances but not on the QVM-based instances, and vice versa.
You can make your code robust by performing type checks on
qc.compiler. For example:
from pyquil import get_qc
from pyquil.api import QPUCompiler
qc = get_qc("Aspen-9") # or "2q-qvm"
if isinstance(qc.compiler, QPUCompiler):
# Working with a QPU - refresh calibrations
Without the type check on line 6, your code might run well when targeting a QPU but raise an error when changing to a QVM (because
get_calibration_program()is not available on
QVMCompiler). With the type check in place, your code will run seamlessly between both cases.
While QVMs typically run in your current environment and therefore don't have special access requirements, QPUs are remote resources hosted by QCS. As such, a QPU will only be accessible to you during a reservation window. You must also have network access to the QPU, which is most easily obtained by using your provisioned JupyterLab IDE.
When moving from a QVM to a QPU, it's important to consider qubit topology. Qubit topology describes which qubits can be used together in quantum operations. For a QVM, this is a fully-connected graph — meaning any qubit can be used with any other qubit. This is not necessarily the case for a QPU, in which only certain qubits can be used together.
On a QPU, qubits and qubit pairs also have differing fidelities for certain operations. You can view this information by clicking
Device Calibrationunder a QPU when making a reservation, as shown above.
Assume your quantum program includes an operation on qubits 0 and 1. On a QVM, your program may run well, but what about on a particular QPU? What if these qubits are not connected in the desired QPU's topology? Or perhaps they are connected but have poor fidelity for the operation in question. To run your program on the desired QPU, you can take advantage of compiler rewiring.
Rewiring is a step that the Quil compiler will perform to select optimal qubits for you. The qubits selected will be based on a QPU's topology and calibration information. Rewiring will happen by default, if needed, but rewiring behavior can also be explicitly overridden using a compiler directive.
Active reset — enacted by using the Quil
RESETinstruction at the beginning of a program — forces all qubits to their ground states. This is useful when running sequential programs against a QPU, as it reduces the time needed for qubits to return to their ground states between programs.
Active reset has no effect when running programs against a QVM, as a QVM will start with its qubits in their ground states regardless of active reset.
It may seem theoretically trivial to use active reset when targeting a QPU. However, its use can have side effects in practice, as active reset has its own associated fidelity (as seen in the figure under QPU Topology & Fidelities) — it's important to be aware of this when moving from a QVM to a QPU.