Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emulator readout module modification #1040

Open
wants to merge 19 commits into
base: emulator-readout
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
12f5387
created testing1 and testing2 files
dexonoir Sep 9, 2024
8ece268
added readout_phase.py
dexonoir Sep 10, 2024
cc71386
updated readout_phase.py to readout_example.py, pending changes to re…
dexonoir Sep 11, 2024
5ac0261
modified demodulation procedure, incorporated dressed frequency, desc…
dexonoir Sep 13, 2024
b6d8a2e
updated readout_example folder
dexonoir Sep 13, 2024
23e1b79
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 13, 2024
142fda7
provided additional descriptions, improved performance of the readout…
dexonoir Sep 14, 2024
3e72819
Merge branch 'emulator-readout' of https://github.com/dexonoir/qibola…
dexonoir Sep 14, 2024
9216b68
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 14, 2024
60c0f04
added LO frequency option, restored envelope waveform
dexonoir Sep 16, 2024
dfd4332
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Sep 16, 2024
86db307
integrated emulator readout module into pulse_simulator, to have read…
dexonoir Oct 8, 2024
122c517
provided notebook tutorials on both readout module and PulseSimulator…
dexonoir Oct 8, 2024
a2f40d8
made the requested changes
dexonoir Oct 9, 2024
b564e64
slight changes to account for LO_frequency in device configuration
dexonoir Oct 9, 2024
97faa40
handles multiple resonators, improves noise model implement, allows v…
dexonoir Nov 11, 2024
03e3f9f
calibrated readout parameters
dexonoir Nov 13, 2024
c632b67
implementation accounting for sweepers and averaging
dexonoir Nov 17, 2024
112587d
changes accommodating readout for sweeping
dexonoir Nov 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
307 changes: 307 additions & 0 deletions examples/emulator readout/pulse_simulator_readout.ipynb

Large diffs are not rendered by default.

55 changes: 55 additions & 0 deletions examples/emulator readout/pulse_simulator_readout.py
dexonoir marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import os

import matplotlib.pyplot as plt

import qibolab
from qibolab.pulses import PulseSequence

os.environ["QIBOLAB_PLATFORMS"] = r"tests\emulators"

platform = qibolab.create_platform("ibmfakebelem_q01")

pi_pulse = platform.create_RX_pulse(0)
readout_pulse = platform.create_qubit_readout_pulse(qubit=0, start=pi_pulse.finish)
# #for two readout pulses
# pi_pulse2 = platform.create_RX90_pulse(0, start = readout_pulse.finish)
# readout_pulse2 = platform.create_qubit_readout_pulse(qubit=0, start=pi_pulse2.finish)

ps = PulseSequence(*[readout_pulse])
opts = qibolab.ExecutionParameters(
nshots=100,
relaxation_time=100e3,
acquisition_type=qibolab.AcquisitionType.INTEGRATION,
averaging_mode=qibolab.AveragingMode.SINGLESHOT,
)

gnd = platform.execute_pulse_sequence(ps, opts)
print(gnd)
print(gnd[0].voltage)


ps.add(pi_pulse)
excited = platform.execute_pulse_sequence(ps, opts)


i_ground = gnd[0].voltage_i
q_ground = gnd[0].voltage_q

i_excited = excited[0].voltage_i
q_excited = excited[0].voltage_q

# evenly distributed as measurement of ground state(excited state) results in
# excited state(ground state) due to noise and statistical fluctuations in measurement
plt.scatter(i_ground, q_ground, label=r"$|0\rangle$")
plt.scatter(i_excited, q_excited, label=r"$|1\rangle$")

plt.xlabel(r"$V_I$ [a.u.]")
plt.ylabel(r"$V_Q$ [a.u.]")

# #when scale in noise_model = 0
# plt.xlim([0,0.8])

plt.legend()
plt.tight_layout()
# plt.savefig("IQ_pulse_simulator.png", dpi=300)
plt.show()
228 changes: 228 additions & 0 deletions examples/emulator readout/readout_example.ipynb

Large diffs are not rendered by default.

104 changes: 104 additions & 0 deletions examples/emulator readout/readout_example.py
dexonoir marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import matplotlib.pyplot as plt
import numpy as np

from qibolab.instruments.emulator.readout import ReadoutSimulator, lamb_shift
from qibolab.pulses import ReadoutPulse
from qibolab.qubits import Qubit

bare_resonator_frequency = 5.045e9
nshots = 100

SNR = 30 # dB
READOUT_AMPLITUDE = 1
NOISE_AMP = np.power(10, -SNR / 20)
AWGN = lambda t: np.random.normal(loc=0, scale=NOISE_AMP, size=len(t)) * 3e4

qb = Qubit(
0,
bare_resonator_frequency=bare_resonator_frequency,
drive_frequency=3.99e9,
anharmonicity=-263e6,
)
readout = ReadoutSimulator(
qubit=qb,
g=10e6,
noise_model=AWGN,
internal_Q=2.5e6,
coupling_Q=6e4,
sampling_rate=1966.08e6,
)


# demonstrates effect of state dependent dispersive shift on amplitude of reflected microwave from resonator;
# we first prepare a centre frequency for frequency sweeping, which may be modified during analysis, to search for the dispersive shift
# note that dispersive shift and lamb shift depends on detuning(i.e.: drive_frequency - bare_resonator_frequency)
# lamb shifted frequncy would then be shifted dispersively depending on ground_state or excited state of qubit
# fitting of |S21| is discussed here: https://github.com/qiboteam/qibocal/pull/917
span = 1e6
center_frequency = bare_resonator_frequency
freq_sweep = np.linspace(center_frequency - span / 2, center_frequency + span / 2, 1000)
y_gnd = np.abs(readout.ground_s21(freq_sweep))
y_exc = np.abs(readout.excited_s21(freq_sweep))

freq_sweep /= 1e9
plt.plot(freq_sweep, y_gnd, label=r"$|0\rangle$")
plt.plot(freq_sweep, y_exc, label=r"$|1\rangle$")
plt.ylabel("|S21| [arb. units]")
plt.xlabel("Readout Frequency [GHz]")
plt.legend()
plt.grid()
plt.tight_layout()
# plt.savefig("S21.png", dpi=300)
plt.show()

freq_sweep *= 1e9


# demonstrates effect of state dependent dispersive shift on phase of reflected microwave from resonator; (for codomain of [-pi/2,pi/2])
# note that we can always shift the phase/angle by pi, as a result of arctan(), as there are two angles resulting in the same tan() value
# this means that we can shift the positive angles downwards by subtracting them with pi, resulting in codomain of [0,-2pi]
# for a clearer picture (using codomain of [0,-2pi] @see https://arxiv.org/pdf/1904.06560), we can see that
# the phase response of resonator shall be maximally separated when resonator is probed just in-between two qubit-state dependent resonance frequencies.
y_gnd1 = np.angle(readout.ground_s21(freq_sweep))
y_exc1 = np.angle(readout.excited_s21(freq_sweep))
freq_sweep /= 1e9
plt.plot(freq_sweep, y_gnd1, label=r"$|0\rangle$")
plt.plot(freq_sweep, y_exc1, label=r"$|1\rangle$")
plt.ylabel(r"$\theta$ phase [rad]")
plt.xlabel("Readout Frequency [GHz]")
plt.legend()
plt.grid()
plt.tight_layout()
# plt.savefig("phase.png", dpi=300)
plt.show()


# demonstrates the separation of V_I/V_Q data of reflected microwave on V_I/V_Q plane
# we prepare a lambshifted readout pulse frequency according to the first and second demonstration,
# so that we can inspect the phase response of resonator (being plotted on V_I/V_Q plane),
# which shall be maximally separated when resonator is probed just in-between two qubit-state dependent resonance frequencies.
# in other words, the data probed from resonator for particular qubit states should be well separated as demonstrated previously
delta = qb.drive_frequency - qb.bare_resonator_frequency
ro_frequency = 5.0450e9 - lamb_shift(g=10e6, delta=delta)
ro_pulse = ReadoutPulse(
start=0,
duration=1000,
amplitude=READOUT_AMPLITUDE,
frequency=ro_frequency,
shape="Rectangular()",
relative_phase=0,
)

rgnd = [readout.simulate_ground_state_iq(ro_pulse) for k in range(nshots)]
rexc = [readout.simulate_excited_state_iq(ro_pulse) for k in range(nshots)]
plt.scatter(np.real(rgnd), np.imag(rgnd), label=r"$|0\rangle$")
plt.scatter(np.real(rexc), np.imag(rexc), label=r"$|1\rangle$")
# when we set NOISE_AMP to zero, using the follow axes limits allow us to see the maximally separated data
# plt.xlim([0,250])
# plt.ylim([-250,250])
plt.xlabel(r"$V_I$ [a.u.]")
plt.ylabel(r"$V_Q$ [a.u.]")
plt.legend()
plt.tight_layout()
# plt.savefig("IQ.png", dpi=300)
plt.show()
65 changes: 65 additions & 0 deletions src/qibolab/instruments/emulator/pulse_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from qibolab.instruments.abstract import Controller
from qibolab.instruments.emulator.engines.qutip_engine import QutipSimulator
from qibolab.instruments.emulator.models import general_no_coupler_model
from qibolab.instruments.emulator.readout import ReadoutSimulator
from qibolab.pulses import PulseSequence, PulseType, ReadoutPulse
from qibolab.qubits import Qubit, QubitId
from qibolab.result import IntegratedResults, SampleResults
Expand All @@ -35,6 +36,11 @@

GHZ = 1e9

# noise_model
SNR = 30 # dB
NOISE_AMP = np.power(10, -SNR / 20)
AWGN = lambda t: np.random.normal(loc=0, scale=NOISE_AMP, size=len(t)) * 3e4

dexonoir marked this conversation as resolved.
Show resolved Hide resolved

class PulseSimulator(Controller):
"""Runs quantum dynamics simulation of model of device.
Expand Down Expand Up @@ -87,6 +93,26 @@ def setup(self, **kwargs):
self.simulate_dissipation = simulation_config["simulate_dissipation"]
self.output_state_history = simulation_config["output_state_history"]

try:
readout_simulator_config = kwargs["readout_simulator_config"]
readout_simulator_config["noise_model"] = AWGN
self.readout_simulator_config = readout_simulator_config
except KeyError:
self.readout_simulator_config = None
dexonoir marked this conversation as resolved.
Show resolved Hide resolved
"""readout_simulator_config = Dict(g=, noise_model=, internal_Q=, coupling_Q=, sampling_rate=)
Additional parameters needed for ReadoutSimulator for demodulation
emulation.

Example of calibrated noise_model (found in examples/emulator readout in readout_example.ipynb or readout_example.py):
SNR = 30 # dB
NOISE_AMP = np.power(10, -SNR / 20)
AWGN = lambda t: np.random.normal(loc=0, scale=NOISE_AMP, size=len(t)) * 3e4

noise_model = AWGN

**initialize scale=0 if noise_model is not needed
"""

def connect(self):
pass

Expand Down Expand Up @@ -180,6 +206,32 @@ def play(
"output_states": output_states,
}

if execution_parameters.acquisition_type is AcquisitionType.INTEGRATION:
if self.readout_simulator_config is not None:
dexonoir marked this conversation as resolved.
Show resolved Hide resolved
for index, ro_pulse in enumerate(ro_pulse_list):
dexonoir marked this conversation as resolved.
Show resolved Hide resolved
# obtain qubit with its qubitID
qubit = qubits[get_qubit(ro_pulse.qubit, qubits)]
# bare_resonator_frequency is prepared considering readout pulse frequency = lambshifted readout pulse frequency,
# such that the V_I/V_Q are maximally seperated (i.e.: bare_resonator_frequency = lambshifted readout pulse frequency + lamb shift)
# solve quadratic equation for bare_resonator frequency
readout = ReadoutSimulator(
qubit=qubit, **self.readout_simulator_config
)
values = np.array(samples[ro_pulse.qubit])
for i in range(len(values)):
values = values.astype(np.complex128)
dexonoir marked this conversation as resolved.
Show resolved Hide resolved
if values[i] == 0:
values[i] = readout.simulate_ground_state_iq(ro_pulse)
elif values[i] == 1:
values[i] = readout.simulate_excited_state_iq(ro_pulse)
else:
raise ValueError("measurement output is not 0 or 1")

processed_values = IntegratedResults(values)
results[ro_pulse.qubit] = results[ro_pulse.serial] = (
processed_values
)

return results

### sweeper adapted from icarusqfpga ###
Expand Down Expand Up @@ -410,6 +462,19 @@ def merge_sweep_results(
}


def get_qubit(target_qubit, qubits):
"""Return the name of target physical qubit (qubitID) corresponding to a
logical qubit.

Temporary fix for the compiler to work for platforms where the
qubits are not named as 0, 1, 2, ...
"""
try:
return qubits[target_qubit].name
except KeyError:
return list(qubits.keys())[target_qubit]


def ps_to_waveform_dict(
sequence: PulseSequence,
platform_to_simulator_channels: dict,
Expand Down
Loading