Quantum Virtual Machine

View on QuantumAI Run in Google Colab View source on GitHub Download notebook

The quantum virtual machine is a virtual Google quantum processor that you can run circuits on by using the virtual engine interface. Behind this interface, it uses simulation with noise data to mimic Google quantum hardware processors with high accuracy: In internal tests, the virtual and actual hardware are within experimental error of each other. Additionally, it supports internal use of the high-performance qsim simulator, for fast execution of larger circuits. The QVM should be used as a preparation step before running on Google hardware, and as a substitute for Google hardware when it is not available.

If you just want to use the QVM for realistic noisy simulation, you can copy and build upon the QVM Creation Template, which provides a concise and portable way to instantiate an Engine class that you can realistically simulate circuit runs with. After doing so, skip forward to the How to use a QVM section. If you're interested in how the QVM is prepared for use, continue on to the following section.

Setup

How to build a QVM

Choose a processor to virtualize

Currently, the necessary data is publicly accessible only for the Weber and Rainbow processors. Read more about Google's processors here.

# Choose a processor ("rainbow" or "weber")
processor_id = "weber"

Build a noisy simulator with a hardware noise model

  • Load median device noise data for the processor you have chosen. Learn more about device noise data here
  • Transform the median device noise data to a Cirq noise properties object
  • Create a noise model using your noise properties
  • Set up a qsim sampler which runs noisy simulations using your noise model. Learn more about noisy simulation with qsim here in the Noisy simulation with qsim page.
# Load the median device noise calibration for your selected processor.
cal = cirq_google.engine.load_median_device_calibration(processor_id)
# Create the noise properties object.
noise_props = cirq_google.noise_properties_from_calibration(cal)
# Create a noise model from the noise properties.
noise_model = cirq_google.NoiseModelFromGoogleNoiseProperties(noise_props)
# Prepare a qsim simulator using the noise model.
sim = qsimcirq.QSimSimulator(noise=noise_model)

The qsim documentation explains how simulation performance depends on choice of hardware. If you would like use a larger number of qubits on your virtual device (eg 25-32 qubits), parallelizing the simulation over multiple compute nodes is advised. You can do this using Google Cloud hardware as is described in the qsim Multinode Tutorial.

Set up the virtual engine with a virtual processor, packaging in the noisy simulator

To ensure that the workflow for using a virtual quantum processor is the same as the workflow for using a real quantum processor, a quantum virtual engine implements the same interface as the cirq.Engine for used Google's quantum hardware. Learn more about Google’s quantum engine in the Quantum Virtual Engine Interface page.

  • Create a device object. Learn more about the device object in Cirq here in the Devices page
  • Create a simulated processor object for the engine to consume (SimulatedLocalProcessor)
  • Create a virtual engine (SimulatedLocalEngine)
# Package the simulator and device in an Engine.
# The device object
device = cirq_google.engine.create_device_from_processor_id(processor_id)
# The simulated processor object
sim_processor = cirq_google.engine.SimulatedLocalProcessor(
    processor_id=processor_id, sampler=sim, device=device, calibrations={cal.timestamp // 1000: cal}
)
# The virtual engine
sim_engine = cirq_google.engine.SimulatedLocalEngine([sim_processor])
print(
    "Your quantum virtual machine",
    processor_id,
    "is ready, here is the qubit grid:",
    "\n========================\n",
)
print(sim_engine.get_processor(processor_id).get_device())
Your quantum virtual machine weber is ready, here is the qubit grid: 
========================

                                             (0, 5)───(0, 6)
                                             │        │
                                             │        │
                                    (1, 4)───(1, 5)───(1, 6)───(1, 7)
                                    │        │        │        │
                                    │        │        │        │
                                    (2, 4)───(2, 5)───(2, 6)───(2, 7)───(2, 8)
                                    │        │        │        │        │
                                    │        │        │        │        │
                  (3, 2)───(3, 3)───(3, 4)───(3, 5)───(3, 6)───(3, 7)───(3, 8)───(3, 9)
                  │        │        │        │        │        │        │        │
                  │        │        │        │        │        │        │        │
         (4, 1)───(4, 2)───(4, 3)───(4, 4)───(4, 5)───(4, 6)───(4, 7)───(4, 8)───(4, 9)
         │        │        │        │        │        │        │        │
         │        │        │        │        │        │        │        │
(5, 0)───(5, 1)───(5, 2)───(5, 3)───(5, 4)───(5, 5)───(5, 6)───(5, 7)───(5, 8)
         │        │        │        │        │        │        │
         │        │        │        │        │        │        │
         (6, 1)───(6, 2)───(6, 3)───(6, 4)───(6, 5)───(6, 6)───(6, 7)
                  │        │        │        │        │
                  │        │        │        │        │
                  (7, 2)───(7, 3)───(7, 4)───(7, 5)───(7, 6)
                           │        │        │
                           │        │        │
                           (8, 3)───(8, 4)───(8, 5)
                                    │
                                    │
                                    (9, 4)

How to use a QVM

The following code runs a circuit on your QVM by using the run function of a sampler from the simulated engine:

q0 = cirq.GridQubit(4, 4)
q1 = cirq.GridQubit(4, 5)
circuit = cirq.Circuit(cirq.X(q0), cirq.SQRT_ISWAP(q0, q1), cirq.measure([q0, q1], key="measure"))

results = sim_engine.get_sampler(processor_id).run(circuit, repetitions=3000)

print(results.histogram(key="measure"))
Counter({2: 1449, 1: 1264, 0: 248, 3: 39})

As in the example linked above, your circuit needs to be “device ready”. This means that:

  • The gates in the circuit need to be in the set of legal gates on the device
  • The circuit needs to operate on qubits available on the virtual device.
  • The topology of your circuit must correspond to the topology of the device (i.e., 2-qubit gates must act on adjacent qubits).

For a hands-on example of the steps necessary to prepare a circuit to be run on the QVM, see the QVM Basic Example page.

The steps necessary to make a circuit device-ready are summarized here:

  1. Transform your circuit to use the correct gate set with cirq.optimize_for_target_gateset. Read Transformers for more on how to modify circuits.
  2. Choose qubits on the virtual device for your circuit to run on. The connectivity required by your circuit must be supported by the connectivity present in your chosen qubit set. See Qubit Picking for more advice.
  3. Map your transformed circuit to those qubits with cirq.Circuit's transform_qubits function. This may require some careful planning depending on your particular circuit.

You also need to decide on the number of repetitions your circuit will be used in the trajectory simulation. This number determines how accurately the quantum virtual machine will simulate the true quantum state of your circuit. For more details on this see this paper. We recommend using 10,000+ repetitions for research simulations, and 3,000 repetitions for learning simulations. If you are just getting a feel for the tools you can set the number of repetitions lower temporarily (eg 1 to 10) to speed things up.