OPX simulator ============= The simulator is capable of prediciting the actual output timing of the OPX for a given QUA program. The simulator supports input to measurement commands via the loopback interface. The simulator returns two type of objects: 1. `simulated analog wavefoms` and `simulated digital waveforms`, which is a dictionary that contains the names, timing values, frequencies and phases of all waveforms that were played during a program. These can be retreived based on DAC ports or quantum elements. .. note:: The samples of the waveforms are the raw envelope values, before modulation by the carrier, scaling with the ``amp()`` function, frame rotation and mixer correction. To obtain the actual values that are output to the DAC, see ``get_simulated_samples()`` method of ``QmJob``. 2. The actual analog samples that were played to the DAC, using the ``get_simulated_samples()`` method of ``QmJob``. Usage ----- By calling the ``simulate()`` function of a ``QuantumMachine`` (see Quantum Machine API). This function should receive a ``SimulationConfig`` object, whose API is specified in the following section. An example for the a call to ``simulate`` can be found in its API documentation. ***************** LoopbackInterface ***************** It is possible to simulate a connection from an output of an OPX to one of the two inputs. This is done by using the ``LoopbackInterface`` which is passed to the ``simulate`` method of the QM object. For example, the following code generates a virtual connection from output 1 to input 2. A list of two such tuples can be passed to the LoopbackInterface (as there are two inputs to the OPX). .. code-block:: python QM.simulate(prog,SimulationConfig(duration, LoopbackInterface([("con1",1,"con1",2)])) ``LoopbackInterface`` can also simulate signal propagation delay and noise using the ``noisePower`` and ``latency`` keywords. ``noisePower`` adds gaussian noise with zero mean and a variance (in units [V^2]) set by this parameter. ``latency`` adds a signal propagation delay in [ns] set by this parameter The following adds noise with variance 1 V^2 and 100 ns latency. All (up to 2) tuples in the loopback interface will have the same noise figure and same latency. .. code-block:: python QM.simulate(prog,SimulationConfig(duration, LoopbackInterface([("con1",1,"con1",2)],noisePower=1,latency=100)) ***************** Raw ADC interface ***************** It is possible to explicitly specify the signal passed into the OPX in the simulation. This is quite similar to the LoopbackInterface described above and supports the same options. For example, the code snippet below playes a 1 microsecond signal to input 1 of the device called "con1". .. code-block:: python signal=np.linspace(0,0.2,1000).tolist() QM.simulate(prog,SimulationConfig(duration, RawInterface([("con1",1,signal)],noisePower=1,latency=100)) API --- .. autoclass:: qm._SimulationConfig.SimulationConfig :members: .. autoclass:: qm._SimulationConfig.LoopbackInterface :members: Examples -------- .. code-block:: python from qm.QuantumMachinesManager import QuantumMachinesManager from qm import SimulationConfig from qm import LoopbackInterface from qm.qua import * from examples._config import config import matplotlib.pyplot as plt qmm = QuantumMachinesManager(host='127.0.0.1') qm = qmm.open_qm(config) ## # ## Example 1 - No inputs with program() as prog1: a = declare(fixed) b = declare(fixed, value=0.2) c = declare(fixed, value=0.4) play("measurement", "qe1") play("marker", "qeDig") with for_(a, 0.2, a < 0.9, a + 0.1): play("measurement" * amp(a), "qe1") assign(c, c + b*1.5) save(c, "c") # simulate program job = qm.simulate(prog1, SimulationConfig( duration=1000, # duration of simulation in units of 4ns include_analog_waveforms=True, # include analog waveform names include_digital_waveforms=True # include digital waveform names )) # get DAC and digital samples samples = job.get_simulated_samples() # plot all ports: samples.con1.plot() # another way, plot analog output 1 and digital output 9: # plt.figure() # plt.plot(samples.con1.analog["1"]) # plt.plot(samples.con1.digital["9"]) # plt.legend(("analog 1", "digital 9")) # plt.xlabel("Time [ns]") # plt.ylabel("Signal [V]") # print waveform names and times print(job.simulated_analog_waveforms()) print(job.simulated_digital_waveforms()) # get results res = job.get_results() c = res.variable_results.c.values print(c) ## # ## Example 2 - Loopback inputs with program() as prog2: I = declare(fixed) Q = declare(fixed) f = declare(int) a = declare(fixed) with for_(a, 0.1, a < 0.4, a + 0.1): with for_(f, 50e6, f < 92e6, f + 1e6): update_frequency("qe1", f) measure("measurement"*amp(a), "qe1", "adc", demod.full('integW1', I), demod.full('integW2', Q)) save(I, "I") save(Q, "Q") # simulate program job = qm.simulate(prog2, SimulationConfig( duration=40000, simulation_interface=LoopbackInterface([("con1", 1, "con1", 1)]) # loopback from output 1 to input 1 )) # get results res = job.get_results() adc = res.raw_results.input1.values ts = res.raw_results.input1.ts_nsec I = res.variable_results.I.values Q = res.variable_results.Q.values plt.figure() plt.plot(ts, adc) plt.xlabel("Time [ns]") plt.ylabel("ADC") plt.figure() plt.plot(I, Q, '.') plt.xlabel("I") plt.xlabel("Q") ## # ## Example 3: with program() as prog3: d = declare(int) with for_(d, 10, d <= 100, d + 10): play("measurement", "qe1") play("measurement", "qe2", duration=d) wait(50, "qe1") # simulate program job = qm.simulate(prog3, SimulationConfig( duration=1700, # duration of simulation in units of 4ns )) # get DAC and digital samples samples = job.get_simulated_samples() # plot analog ports 1 and 3: samples.con1.plot(analog_ports={'1', '3'}, digital_ports={}) # another way: # plt.figure() # plt.plot(samples.con1.analog["1"], "-") # plt.plot(samples.con1.analog["3"], "--") # plt.legend(("analog 1", "analog 3")) # plt.xlabel("Time [ns]") # plt.ylabel("Signal [V]")