Commit afedc90e authored by Christian Schneider's avatar Christian Schneider
Browse files

Merge branch 'integrated_swept' of...

Merge branch 'integrated_swept' of https://git.uibk.ac.at/exphys/quantum-circuits/python-repo into integrated_swept
parents 57f23a35 25aced6e
......@@ -113,13 +113,13 @@ class IQCal_QM(IQCal_base):
IQ_imbalance(1-self.calibration.ratio,self.calibration.phase))
def initialize_instruments(self):
def initialize_instruments(self, set_LO=True):
"""
Start the signal generator and the QM pulse
Parameters
----------
none : None
set_LO: choose if the LO are fixed by the calibration code or not.
Returns
-------
......@@ -128,7 +128,8 @@ class IQCal_QM(IQCal_base):
import qm.qua as qua
self.set_LO(None)
if set_LO:
self.set_LO(None)
with qua.program() as mixer_calibration:
with qua.infinite_loop_():
......@@ -139,19 +140,19 @@ class IQCal_QM(IQCal_base):
self.job = self._QM.execute(mixer_calibration)
def stop_instruments(self):
def stop_instruments(self, set_LO=True):
"""
Stop the signal generator and the QM pulse
Parameters
----------
none : None
set_LO: choose if the LO are shutdown by the calibration code
Returns
-------
nothing: None
"""
self._sgLO.output(0)
if set_LO:
self._sgLO.output(0)
self.job.halt()
\ No newline at end of file
import qm.qua as qua
import numpy as np
import math
import inspect
from experiment import BondedParameter
from Instruments.QM import BondedQuantumMachine
from DataHandling.core import Measurement, DataSet
class BondedQMParameter(BondedParameter):
"""
This class is an helper to create parameter which are execute by the program in the quantum
machine.
"""
def __init__(self, parameter_name, instrument_name, data, unit=None):
super().__init__(parameter_name, instrument_name, BondedQuantumMachine.store_parameters, data, unit)
self.swept_by_instrument = False
self.must_be_swept = True
def __call__(self, instruments, value):
## Find the right one
if self.last_value != value:
self.instrument_func(instruments[self._instrument_name], self, value)
self.last_value = value
def __str__(self):
## Could be improved
return 'Quantum machine parameter used: '+self.elements[self.last_value]+' for '+self.name
class BondedQMParameterSweptByProgram(BondedParameter):
"""
This class is an helper to create parameter which are execute by the program in the quantum
machine.
"""
def __init__(self, parameter_name, instrument_name, data, unit=None):
super().__init__(parameter_name, instrument_name, BondedQuantumMachine.store_parameters, data, unit)
self.swept_by_instrument = True
self.must_be_swept = True
def __call__(self, instruments):
## Find the right one
self.instrument_func(instruments[self._instrument_name], self, self.data)
def __str__(self):
## Could be improved
return 'Swept by the quantum machine: '+self.name
class BondedQMParameterText(BondedParameter):
"""
This class is an helper to create elements or pulse parameters for the Quantum machine
"""
def __init__(self, parameter_name, instrument_name, elements):
data = np.arange(len(elements), dtype = np.int32)
self.elements = elements
super().__init__(parameter_name, instrument_name, BondedQuantumMachine.store_parameters, data, '')
self.metadata['Elements'] = elements
self.swept_by_instrument = False
self.must_be_swept = True
def __call__(self, instruments, value):
if self.last_value != value:
self.instrument_func(instruments[self._instrument_name], self, self.elements[value])
self.last_value = value
def __str__(self):
## Could be improved
return 'Quantum machine parameter used: '+self.elements[self.last_value]+' for '+self.name
class BondedQMMeasurement():
def __init__(self, name, instrument_name, suffix=''):
self.name = name
self.instrument_name = instrument_name
self.metadata = {}
self.suffix = suffix
self.quantum_machine_job = None
self.shape_swept_parameter = None
self.size_measurement = None
self.previous_program = None
def __call__(self, instruments, additionnal_option = {}):
"""
This method is called for the measurement.
"""
try:
if additionnal_option['before_measurement']:
return self.before_first_measurement(instruments[self.instrument_name])
except KeyError:
pass
program = self.get_program(instruments[self.instrument_name].get_current_parameters())
self.quantum_machine_job = instruments[self.instrument_name].execute_program(program)
try:
if additionnal_option['synchrone'] == False:
return self.get_data(instruments)
except KeyError:
return
def get_order_swept_parameters(self):
"""
Send back a list with the name of the parameters which is swept by the quantum machine
program itself in the order they are swept by it.
"""
raise NotImplementedError('This function has to be implemented in the inherited class.')
def fix_shape_swept_parameters(self, instance):
"""
Has to be called in the before_first_measurement. It take the defined parameters which are
swept by the quantum machine program and compute the shape of the measurement at the end.
instance is the QM object which manage the interaction with the QM server and keep track
of the defined parameters.
"""
order_parameter = self.get_order_swept_parameters()
parameters = instance.get_current_parameters()
self.shape_swept_parameter = [len(parameters[par]) for par in order_parameter]
self.size_measurement = math.prod(self.shape_swept_parameter)
def before_first_measurement(self, instance):
"""
Called before the first measurement, but after all the parameters are initialized. It is
responsible to send back the DataSet for the initialization of the HDF5. So it must have
the full parameters swept by the quantum machine (parameter where the swept_by_instrument
attributes is at True) and the name and type of the measurement which will send back.
"""
raise NotImplementedError('This function has to be implemented in the inherited class.')
def get_data(self, instruments):
"""
Return the DataSet with the result of the measurement. It is responsible to wait for the
quantum machine job to finish and to reshape the buffer send back by it into the right one
for saving.
"""
raise NotImplementedError('This function has to be implemented in the inherited class.')
def get_program(self, *args, **kwargs):
"""
Return the program which has to be executed by the quantum machine. The arguments of this
function are the parameters of the measurements.
It is important to note that some of these parameters can be swept by the program itself, in
that case the bonded parameter has to have the attribute swept_by_instrument set to True.
All other arguments can be swept by the python code.
"""
raise NotImplementedError('This function has to be implemented in the inherited class.')
def get_parameters_names(self):
"""
Return the list of the parameter in the get_program function. It is useful to easily add all
the parameter for a quantum machine measurement.
"""
# can be done more cleverly in principle.
parameters = []
for par in inspect.signature(self.get_program).parameters.keys():
if par not in ['self', 'kwargs']:
parameters.append(par)
return parameters
class BondedQMIQMeasurement(BondedQMMeasurement):
"""
Helper class to not have to rewrite everytimes the same before_first_measurement and get_data
when the state of a single resonator with IQ measurement is done.
"""
def before_first_measurement(self, instance):
order_parameter = self.get_order_swept_parameters()
## It is important to fix the shape which will be used
self.fix_shape_swept_parameters(instance)
## To see if it needs to be changed to the
#freq_r=self.setup_dict[qe_ro_key_var]['LO_freq']-freq_cav.reshape(len(cav_f_vec),len(qe_f_vec))
#freq_q=self.setup_dict[qe_key_var]['LO_freq']-freq_qe.reshape(len(cav_f_vec),len(qe_f_vec))
parameters = instance.get_parameters()
return DataSet(parameters,
[Measurement('I', np.empty((1), dtype=np.float64), '', order_parameter),
Measurement('Q', np.empty((1), dtype=np.float64), '', order_parameter)],
instance.get_config())
def get_data(self, instruments):
self.quantum_machine_job.result_handles.wait_for_all_values()
Ires = self.quantum_machine_job.result_handles.I_res.fetch_all()
Qres = self.quantum_machine_job.result_handles.Q_res.fetch_all()
I_r = Ires.reshape(self.shape_swept_parameter)
Q_r = Qres.reshape(self.shape_swept_parameter)
order_parameter = self.get_order_swept_parameters()
return DataSet([],
[Measurement('I', I_r, '', order_parameter),
Measurement('Q', Q_r, '', order_parameter)],
{})
class CavitySpectroscopy(BondedQMIQMeasurement):
def get_order_swept_parameters(self):
return ['cavity_frequencies', 'cavity_amplitudes']
def get_program(self, number_repetition, wait_time,
cavity_element, cavity_pulse, cavity_frequencies, cavity_amplitudes,
**kwargs):
"""
Parameters which are swept internally by the quantum machine:
cavity_frequencies, cavity_amplitude, qubit_frequencies, qubit_amplitudes
All the other parameters can be swept by the experiment class.
"""
## Add security
cavity_frequencies = np.rint(cavity_frequencies).astype(np.int32).tolist()
cavity_amplitudes = cavity_amplitudes.astype(np.float64).tolist()
with qua.program() as program:
N = qua.declare(int)
I = qua.declare(qua.fixed)
I_stream = qua.declare_stream()
Q = qua.declare(qua.fixed)
Q_stream = qua.declare_stream()
cavity_frequency = qua.declare(qua.int)
cavity_amplitude = qua.declare(qua.fixed)
with qua.for_(N, 0, N < int(number_repetition), N + 1):
with qua.for_each_(cavity_frequency, cavity_frequencies):
qua.update_frequency(cavity_element, cavity_frequency)
qua.align()
with qua.for_each_(cavity_amplitude, cavity_amplitudes):
qua.measure(cavity_pulse * qua.amp(cavity_amplitude), cavity_element, None,
(cavity_pulse+"_integW1", "out1", I),
(cavity_pulse+"_integW2", "out1", Q)) ## Likely to be still changed
qua.save(I, I_stream)
qua.save(Q, Q_stream)
qua.wait(int(wait_time*1e9/4), cavity_element)
with qua.stream_processing():
I_stream.buffer(self.size_measurement).average().save("I_res")
Q_stream.buffer(self.size_measurement).average().save("Q_res")
return program
class QubitSpectroscopyCavitySpectroscopy(BondedQMMeasurement):
def get_order_swept_parameters(self):
return ['cavity_frequencies', 'qubit_frequencies', 'cavity_amplitudes', 'qubit_amplitudes']
def before_first_measurement(self, instance):
order_parameter = self.get_order_swept_parameters()
## It is important to fix the shape which will be used
self.fix_shape_swept_parameters(instance)
## To see if it needs to be changed to the
#freq_r=self.setup_dict[qe_ro_key_var]['LO_freq']-freq_cav.reshape(len(cav_f_vec),len(qe_f_vec))
#freq_q=self.setup_dict[qe_key_var]['LO_freq']-freq_qe.reshape(len(cav_f_vec),len(qe_f_vec))
parameters = instance.get_parameters()
return DataSet(parameters,
[Measurement('I', np.empty((1), dtype=np.complex128), '', order_parameter),
Measurement('Q', np.empty((1), dtype=np.complex128), '', order_parameter)],
instance.get_config())
def get_data(self, instruments):
self.quantum_machine_job.result_handles.wait_for_all_values()
Ires = self.quantum_machine_job.result_handles.I_res.fetch_all()
Qres = self.quantum_machine_job.result_handles.Q_res.fetch_all()
swept_parameters = instruments[self.instrument_name].get_parameters()
I_r = Ires.reshape(self.shape_swept_parameter)
Q_r = Qres.reshape(self.shape_swept_parameter)
return DataSet([])
def get_program(self, number_repetition, wait_time,
cavity_element, cavity_pulse, cavity_frequencies, cavity_amplitudes,
qubit_element, qubit_pulse, qubit_frequencies, qubit_amplitudes,
**kwargs):
"""
Parameters which are swept internally by the quantum machine:
cavity_frequencies, cavity_amplitude, qubit_frequencies, qubit_amplitudes
All the other parameters can be swept by the experiment class.
"""
## Add security
cavity_frequencies = np.rint(cavity_frequencies).astype(np.int32).tolist()
cavity_amplitudes = cavity_amplitudes.astype(np.float64).tolist()
qubit_frequencies = np.rint(qubit_frequencies).astype(np.int32).tolist()
qubit_amplitudes = qubit_amplitudes.astype(np.float64).tolist()
shape = self.shape_swept_parameters
with qua.program() as program:
N = qua.declare(int)
I = qua.declare(qua.fixed)
I_stream = qua.declare_stream()
Q = qua.declare(qua.fixed)
Q_stream = qua.declare_stream()
cavity_frequency = qua.declare(qua.int)
cavity_amplitude = qua.declare(qua.fixed)
qubit_frequency = qua.declare(qua.int)
qubit_amplitude = qua.declare(qua.fixed)
with qua.for_(N, 0, N < int(number_repetition), N + 1):
with qua.for_each_(cavity_frequency, cavity_frequencies):
qua.update_frequency(cavity_element, cavity_frequency)
qua.align() ## Not sure that it is useful
with qua.for_each_(qubit_frequency, qubit_frequencies):
qua.update_frequency(qubit_element, qubit_frequency)
qua.align()
with qua.for_each_(cavity_amplitude, cavity_amplitudes):
with qua.for_each_(qubit_amplitude, qubit_amplitudes):
qua.play(qubit_pulse*qua.amp(qubit_amplitude), qubit_element)
qua.align(qubit_element, cavity_element)
qua.measure(cavity_pulse * qua.amp(cavity_amplitude), cavity_element, None,
(cavity_pulse+"_integW1", "out1", I),
(cavity_pulse+"_integW2", "out1", Q)) ## Likely to be still changed
qua.save(I, I_stream)
qua.save(Q, Q_stream)
qua.wait(int(wait_time*1e9/4), cavity_element)
with qua.stream_processing():
I_stream.buffer(self.size_measurement).average().save("I_res")
Q_stream.buffer(self.size_measurement).average().save("Q_res")
return program
"""
Some functions useful for the QM
"""
import numpy as np
def diff_gauss(amplitude, mu, sigma, length, beta, alpha):
t = np.linspace(-length/2, length/2, length)
#drag_wave = amplitude * np.exp(-((t-mu)**2)/(2*sigma**2)) + 1j* beta * (-(t-mu)/sigma**2) * amplitude * np.exp(-((t-mu)**2)/(2*sigma**2))
diff_gauss_wave = beta/alpha * (-(t-mu)/sigma**2) * amplitude * np.exp(-((t-mu)**2)/(2*sigma**2))
return diff_gauss_wave.tolist()
def DRAG_fft(omega,A,beta,alpha,sigma):
return A*np.sqrt(2*np.pi)*sigma * np.exp(-0.5*(omega*sigma)**2)*(1+omega*beta/alpha)
def generate_fft(A,beta,alpha,sigma, n = 10000):
omegas = np.linspace(-10/sigma,10/sigma,n)
y_fft = [np.real(DRAG_fft(omega,A,beta,alpha,sigma)) for omega in omegas]
return omegas,y_fft
def peaks_p(beta,alpha,sigma):
if beta == 0:
return 0
else:
return alpha/(2*beta)*(-1+ np.sqrt(1+(2*beta/(alpha*sigma))**2))
def peaks_m(beta,alpha,sigma):
"""
usually dont care because only comes into play for super high values of beta (>10)
"""
# Don't really understood the use of vectorize here...
if beta == 0:
return np.vectorize(-np.inf)
else:
return np.vectorize(alpha/(2*beta)*(-1 - np.sqrt(1+(2*beta/(alpha*sigma))**2)))
def gauss_DRAG(amplitude,mu,sigma,length,alpha):
"""
beta is normalised to alpha (beta = 1 will account for alpha)
alpha is in radians only valid for alpha > 1/sigma
sigma taken to length/6 in ns
"""
beta = beta_opt(sigma,alpha)
signal = gauss(amplitude,0,sigma,length)
return [float(x)/DRAG_fft(peaks_p(beta,alpha,sigma),amplitude,beta,alpha,sigma)*amplitude*sigma*np.sqrt(2*np.pi) for x in signal]
def diff_gauss_DRAG(amplitude,mu,sigma,length,alpha):
"""
beta is normalised to alpha (beta = 1 will account for alpha)
alpha is in radians only valid for alpha > 1/sigma
sigma taken to length/6 in ns
"""
beta = beta_opt(sigma,alpha)
signal = diff_gauss(amplitude,0,sigma,length,beta,alpha)
return [float(x)/DRAG_fft(peaks_p(beta,alpha,sigma),amplitude,beta,alpha,sigma)*amplitude*sigma*np.sqrt(2*np.pi) for x in signal]
def beta_opt(sigma,alpha):
if alpha*sigma ==1:
print('Error avoided')
return np.inf
return 1/(1 - 1/(alpha*sigma)**2)
beta_opt = np.vectorize(beta_opt)
def IQ_imbalance(g, phi):
phi = np.radians(phi)
c = np.cos(phi)
s = np.sin(phi)
N = 1 / ((1-g**2)*(2*c**2-1))
return [float(N * x) for x in [(1-g)*c, (1+g)*s, (1-g)*s, (1+g)*c]]
def plot_averaged_trace(res, readout_length, traces):
import matplotlib.pyplot as plt
s = np.array(res.raw_results.adc_data.input1_data)
avg_trace = np.zeros(readout_length)
for i in range(traces):
trace = np.array(s[(readout_length * i): (readout_length * (i+1))])
avg_trace = avg_trace + trace
plt.plot(avg_trace)
plt.show()
"""
Seems a bit useless to import pandas and KMeans for something which is currently not used.
def discriminate(I_, Q_):
from sklearn.cluster import KMeans
from pandas import DataFrame
Data = {'x': I_, 'y': Q_}
df = DataFrame(Data, columns=['x', 'y'])
kmeans = KMeans(n_clusters=2).fit(df)
color_ = []; seq_ = []
for arg in kmeans.labels_.astype(float):
if arg == 0:
seq_.append(0)
color_.append('b')
elif arg == 1:
seq_.append(1)
color_.append('r')
return seq_, color_
"""
def demodulation(SSB, Wc, Ws, S, ts0):
'''
demodulation
:param SSB: IF frequency in Hz
:param Wc: list of cosine weights
:param Ws: list of sine weights
:param S: The signal
:param ts0: The time stamp of the first sample
:return:
'''
phi0 = 2 * np.pi * SSB * (ts0 - 32) * 1e-9
Sum = 0
delta_phi = 2 * np.pi * SSB * 1e-9
Phi = [i * delta_phi + phi0 for i in range(4)]
for i in range(int(len(S) / 4)):
Svec = np.array([S[4 * i + s] for s in range(4)])
Sum = Sum + np.sum(np.multiply(np.multiply(Wc[i], np.cos(Phi)) + np.multiply(Ws[i], np.sin(Phi)), Svec))
for j in range(4):
Phi[j] = Phi[j] + 4 * delta_phi
return 16 * Sum * 2 ** -28
def extract_envelope(S, TS0_ns, SSB, W00_, W01_, W10_, W11_):
period_ns = 1 / SSB * 1e9
period_ts = int(period_ns / 4)
envI = []
envQ = []
for i in range(int(len(W00_)) - period_ts + 1):
envI.append(demodulation(SSB, W00_[i: i + period_ts], W01_[i: i + period_ts],
S[int(i * 4): int(i * 4 + period_ns)], TS0_ns + i * 4))
envQ.append(demodulation(SSB, W10_[i: i + period_ts], W11_[i: i + period_ts],
S[int(i * 4): int(i * 4 + period_ns)], TS0_ns + i * 4))
for j in range(period_ts - 1):
if j % 2 == 0:
envI.append(np.float64(0.0))
envQ.append(np.float64(0.0))
if j % 2 == 1:
envI = [0.0] + envI
envQ = [0.0] + envQ