from entropylab.api.execution import EntropyContext
from entropylab.graph_experiment import pynode
from entropylab_qpudb._quaconfig import QuaConfig
from qm import ControllerConnection, InterOpxChannel
from scipy.signal.windows import gaussian, cosine
import numpy as np
import sys, pickle
# import pickle

sys.path.append('..')
from qpu_resolver import resolve
from base_configuration import config as _config
from qm_helper_functions import (iq_imbalance_corr, update_op_from_pulse_dict)

def drag_pulse(N, alpha, anharomonicity):
    t = np.linspace(0, N - 1, N)
    sig_i = (0.5 - 2 ** -16) * 0.5 * (1 - np.cos(2 * np.pi * t / (N - 1)))
    sig_q = -(0.5 - 2 ** -16) * 0.5 * - (alpha / (anharomonicity * (N - 1))) * np.sin(2 * np.pi * t / (N - 1))
    return sig_i, sig_q


controller_connections = [
    ControllerConnection(InterOpxChannel(f"con{i}", j),
                         InterOpxChannel(f"con{k}", l))
    for i, j, k, l in
    list(zip(
        [1] * 12 + [2] * 8 + [3] * 4,
        [i for i in range(12)] + [i for i in range(4, 12)] + [i for i in range(8, 12)],
        [2] * 4 + [3] * 4 + [4] * 4 + [3] * 4 + [4] * 4 + [4] * 4,
        [i for i in range(4)] * 3 + [i for i in range(4, 8)] * 2 + [i for i in range(8, 12)] * 1
        ))
    ]

z_channels = [('con1', 7),  # q1_z
              ('con1', 8),  # q2_z
              ('con2', 7),  # q3_z
              ('con2', 8),  # q4_z
              ('con3', 7),  # q5_z
              ('con3', 8),  # q6_z
              ('con3', 9),  # q7_z
              ('con4', 7),  # q8_z
              ('con4', 8),  # q9_z
              ('con4', 9)]  # q10_z

# old controllers
# controller_connections = [ControllerConnection(InterOpxChannel(f"con{i}", j), InterOpxChannel(f"con{k}", l))
#                           for i, j, k, l in
#                           list(zip(
#                               [1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3],
#                               [0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7],
#                               [2, 2, 2, 2, 3, 3, 4, 4, 3, 3, 4, 4, 4, 4, 4, 4],
#                               [0, 1, 2, 3, 0, 1, 0, 1, 2, 3, 2, 3, 4, 5, 6, 7]))]

debug = False

@pynode("root_from_config_file", output_vars={'config'})
def root_from_config_file(context:EntropyContext):
    return {'config': QuaConfig(_config)}

@pynode("root", output_vars={'config'})
def root(context: EntropyContext):
    qpu_db = context.get_resource('qpu_db')

    # Set XY LO from qpu_db
    xy_lo_freq = qpu_db.get("XY_LO", "frequency").value
    xy_lo_power = qpu_db.get("XY_LO", "power").value
    xy_lo_output = qpu_db.get("XY_LO", "output").value
    
    XY_LO = context.get_resource('XY_LO')    
    # XY_LO.set_freq(xy_lo_freq)
    # XY_LO.set_power(xy_lo_power)
    # XY_LO.set_output(xy_lo_output)
    # if debug:
    #     print("INFO: Updated XY_LO settings from qpu_db")
    #     print("\t" + ', '.join(["Frequency: %.4f GHz" % (xy_lo_freq / 1e9),
    #                             "Power: %.4f dBm" % (xy_lo_power),
    #                             "Output: %r" % (xy_lo_output)]))
    # # Set RO LO from qpu_db
    # ro_lo_freq = qpu_db.get("RO_LO", "frequency").value
    # ro_lo_power = qpu_db.get("RO_LO", "power").value
    # ro_lo_output = qpu_db.get("RO_LO", "output").value

    # RO_LO = context.get_resource('RO_LO')    
    # RO_LO.set_freq(ro_lo_freq)
    # RO_LO.set_power(ro_lo_power)
    # RO_LO.set_output(ro_lo_output)
    # if debug:
    #     print("INFO: Updated RO_LO settings from qpu_db")
    #     print("\t" + ', '.join(["Frequency: %.4f GHz" % (ro_lo_freq / 1e9),
    #                             "Power: %.4f dBm" % (ro_lo_power),
    #                             "Output: %r" % (ro_lo_output)]))    
    # # Set TWPA1 LO from qpu_db
    # twpa1_lo_freq = qpu_db.get("TWPA1_LO", "frequency").value
    # twpa1_lo_power = qpu_db.get("TWPA1_LO", "power").value
    # twpa1_lo_output = qpu_db.get("TWPA1_LO", "output").value

    # TWPA1_LO = context.get_resource('TWPA1_LO')
    # TWPA1_LO.set_freq(twpa1_lo_freq)
    # TWPA1_LO.set_power(twpa1_lo_power)
    # TWPA1_LO.set_output(twpa1_lo_output)
    # BRF1 = context.get_resource('BRF1')
    # BRF1.set_freq(twpa1_lo_freq)
    # if debug:
    #     print("INFO: Updated TWPA1_LO settings from qpu_db")
    #     print("\t" + ', '.join(["Frequency: %.4f GHz" % (twpa1_lo_freq / 1e9),
    #                             "Power: %.4f dBm" % (twpa1_lo_power),
    #                             "Output: %r" % (twpa1_lo_output)]))
    # # Set TWPA2 LO from qpu_db
    # twpa2_lo_freq = qpu_db.get("TWPA2_LO", "frequency").value
    # twpa2_lo_power = qpu_db.get("TWPA2_LO", "power").value
    # twpa2_lo_output = qpu_db.get("TWPA2_LO", "output").value

    # TWPA2_LO = context.get_resource('TWPA2_LO')
    # TWPA2_LO.set_freq(twpa2_lo_freq)
    # TWPA2_LO.set_power(twpa2_lo_power)
    # TWPA2_LO.set_output(twpa2_lo_output)
    # BRF2 = context.get_resource('BRF2')
    # BRF2.set_freq(twpa2_lo_freq)
    # if debug:
    #     print("INFO: Updated TWPA2_LO settings from qpu_db")
    #     print("\t" + ', '.join(["Frequency: %.4f GHz" % (twpa2_lo_freq / 1e9),
    #                             "Power: %.4f dBm" % (twpa2_lo_power),
    #                             "Output: %r" % (twpa2_lo_output)]))
    # # Set LabBrick Attenuation
    # LabBrick_RO = context.get_resource('LabBrick_RO')
    # LabBrick_RO_att = qpu_db.get('LabBrick_RO', 'attenuation').value
    # LabBrick_RO.set_attenuation(LabBrick_RO_att)
    # if debug:
    #     print("INFO: Updated LabBrick_RO settings from qpu_db")
    #     print("\t" + "Attenuation: %.1f dB" % LabBrick_RO_att)
    
    # LabBrick_DC1 = context.get_resource('LabBrick_DC1')
    # LabBrick_DC1_att = qpu_db.get('LabBrick_DC1', 'attenuation').value
    # LabBrick_DC1.set_attenuation(LabBrick_DC1_att)
    # if debug:
    #     print("INFO: Updated LabBrick_DC1 settings from qpu_db")
    #     print("\t" + "Attenuation: %.1f dB" % LabBrick_DC1_att)

    # LabBrick_DC2 = context.get_resource('LabBrick_DC2')
    # LabBrick_DC2_att = qpu_db.get('LabBrick_DC2', 'attenuation').value
    # LabBrick_DC2.set_attenuation(LabBrick_DC2_att)
    # if debug:
    #     print("INFO: Updated LabBrick_DC2 settings from qpu_db")
    #     print("\t" + "Attenuation: %.1f dB" % LabBrick_DC2_att)

    # # Set QDAC to bias voltages from qpu_db
    # QDAC = context.get_resource('QDAC')
    # for i in range(qpu_db.num_qubits):
    #     QDAC.set_voltage(i + 1, qpu_db.q(i + 1).v_bias.value)
    # if debug:
    #     print("INFO: Updated QDAC settings from qpu_db")

    xy_if_freq = np.array([qpu_db.q(i + 1).f_if.value for i in range(qpu_db.num_qubits)])
    ro_if_freq = np.array([qpu_db.res(i + 1).f_if.value for i in range(qpu_db.num_qubits)])

    xy_lo_freq = np.array([qpu_db.q(i + 1).f_lo.value for i in range(qpu_db.num_qubits)])
    ro_lo_freq = np.array([qpu_db.res(i + 1).f_lo.value for i in range(qpu_db.num_qubits)])

    # for collection of readout signals from port 1 of the device
    ro_1_channels = [{'inputs': {'I': ('con1', 1), 'Q': ('con1', 2)}, 'outputs': {'out1': ('con1', 1), 'out2': ('con1', 2)}}, # r1_1
                     {'inputs': {'I': ('con1', 1), 'Q': ('con1', 2)}, 'outputs': {'out1': ('con1', 1), 'out2': ('con1', 2)}}, # r2_1
                     {'inputs': {'I': ('con1', 1), 'Q': ('con1', 2)}, 'outputs': {'out1': ('con1', 1), 'out2': ('con1', 2)}}, # r3_1
                     {'inputs': {'I': ('con1', 1), 'Q': ('con1', 2)}, 'outputs': {'out1': ('con1', 1), 'out2': ('con1', 2)}}, # r4_1
                     {'inputs': {'I': ('con1', 1), 'Q': ('con1', 2)}, 'outputs': {'out1': ('con1', 1), 'out2': ('con1', 2)}}, # r5_1
                     {'inputs': {'I': ('con2', 1), 'Q': ('con2', 2)}, 'outputs': {'out1': ('con2', 1), 'out2': ('con2', 2)}}, # r6_1
                     {'inputs': {'I': ('con2', 1), 'Q': ('con2', 2)}, 'outputs': {'out1': ('con2', 1), 'out2': ('con2', 2)}}, # r7_1
                     {'inputs': {'I': ('con2', 1), 'Q': ('con2', 2)}, 'outputs': {'out1': ('con2', 1), 'out2': ('con2', 2)}}, # r8_1
                     {'inputs': {'I': ('con2', 1), 'Q': ('con2', 2)}, 'outputs': {'out1': ('con2', 1), 'out2': ('con2', 2)}}, # r9_1
                     {'inputs': {'I': ('con2', 1), 'Q': ('con2', 2)}, 'outputs': {'out1': ('con2', 1), 'out2': ('con2', 2)}}] # r10_1

    ro_1_dig1_channels = [('con1', 1), # r1_1
                          ('con1', 1), # r2_1
                          ('con1', 1), # r3_1
                          ('con1', 1), # r4_1
                          ('con1', 1), # r5_1
                          ('con2', 1), # r6_1
                          ('con2', 1), # r7_1
                          ('con2', 1), # r8_1
                          ('con2', 1), # r9_1
                          ('con2', 1)] # r10_1
    ro_1_dig1_delay = [131, 131, 131, 131, 131, 131, 131, 131, 131, 131]
    ro_1_dig1_buffer = [20, 20, 20, 20, 20, 20, 20, 20, 20, 20]

    # for collection of readout signals from port 2 of the device
    ro_2_channels = [{'inputs': {'I': ('con3', 1), 'Q': ('con3', 2)}, 'outputs': {'out1': ('con3', 1), 'out2': ('con3', 2)}}, # r1_2
                     {'inputs': {'I': ('con3', 1), 'Q': ('con3', 2)}, 'outputs': {'out1': ('con3', 1), 'out2': ('con3', 2)}}, # r2_2
                     {'inputs': {'I': ('con3', 1), 'Q': ('con3', 2)}, 'outputs': {'out1': ('con3', 1), 'out2': ('con3', 2)}}, # r3_2
                     {'inputs': {'I': ('con3', 1), 'Q': ('con3', 2)}, 'outputs': {'out1': ('con3', 1), 'out2': ('con3', 2)}}, # r4_2
                     {'inputs': {'I': ('con3', 1), 'Q': ('con3', 2)}, 'outputs': {'out1': ('con3', 1), 'out2': ('con3', 2)}}, # r5_2
                     {'inputs': {'I': ('con4', 1), 'Q': ('con4', 2)}, 'outputs': {'out1': ('con4', 1), 'out2': ('con4', 2)}}, # r6_2
                     {'inputs': {'I': ('con4', 1), 'Q': ('con4', 2)}, 'outputs': {'out1': ('con4', 1), 'out2': ('con4', 2)}}, # r7_2
                     {'inputs': {'I': ('con4', 1), 'Q': ('con4', 2)}, 'outputs': {'out1': ('con4', 1), 'out2': ('con4', 2)}}, # r8_2
                     {'inputs': {'I': ('con4', 1), 'Q': ('con4', 2)}, 'outputs': {'out1': ('con4', 1), 'out2': ('con4', 2)}}, # r9_2
                     {'inputs': {'I': ('con4', 1), 'Q': ('con4', 2)}, 'outputs': {'out1': ('con4', 1), 'out2': ('con4', 2)}}] # r10_2

    xy_channels = [{'I': ('con1', 3), 'Q': ('con1', 4)},    # q1_xy
                   {'I': ('con1', 5), 'Q': ('con1', 6)},    # q2_xy
                   {'I': ('con2', 3), 'Q': ('con2', 4)},    # q3_xy
                   {'I': ('con2', 5), 'Q': ('con2', 6)},    # q4_xy
                   {'I': ('con3', 1), 'Q': ('con3', 2)},    # q5_xy
                   {'I': ('con3', 3), 'Q': ('con3', 4)},    # q6_xy
                   {'I': ('con3', 5), 'Q': ('con3', 6)},    # q7_xy
                   {'I': ('con4', 1), 'Q': ('con4', 2)},    # q8_xy
                   {'I': ('con4', 3), 'Q': ('con4', 4)},   # q9_xy
                   {'I': ('con4', 5), 'Q': ('con4', 6)}]   # q10_xy

    xy_dig1_channels = [('con1', 3),
                        ('con1', 5),
                        ('con2', 3),
                        ('con2', 5),
                        ('con3', 1),
                        ('con3', 3),
                        ('con3', 5),
                        ('con4', 1),
                        ('con4', 3),
                        ('con4', 5)]
    
    xy_dig1_delay = [128, 130, 130, 130, 130, 130, 128, 130, 130, 128]
    xy_dig1_buffer = [20, 20, 20, 20, 20, 20, 20, 20, 20, 20]

    
    ro_mixer = ['mixer_ro1', 'mixer_ro1', 'mixer_ro1', 'mixer_ro1', 'mixer_ro1',
                'mixer_ro2', 'mixer_ro2', 'mixer_ro2', 'mixer_ro2', 'mixer_ro2']

    # common pulses
    const_pulse_dict = qpu_db.get('common_pulses', 'const_pulse').value
    gaussian_pulse_dict = qpu_db.get('common_pulses', 'gaussian_pulse').value
    cosine_pulse_dict = qpu_db.get('common_pulses', 'cosine_pulse').value

    # qubit xy pulses
    spec_xy_pulse_dict = [qpu_db.q(n + 1).spectroscopy_pulse.value for n in range(qpu_db.num_qubits)]
    fine_spec_xy_pulse_dict = [qpu_db.q(n + 1).fine_spectroscopy_pulse.value for n in range(qpu_db.num_qubits)]
    saturation_pulse_dict = [qpu_db.q(n + 1).saturation_pulse.value for n in range(qpu_db.num_qubits)]
    pi_pulse_dict = [qpu_db.q(n + 1).pi_pulse.value for n in range(qpu_db.num_qubits)]
    pi2_pulse_dict = [qpu_db.q(n + 1).pi2_pulse.value for n in range(qpu_db.num_qubits)]

    # readout pulses
    readout_pulse_dict = [qpu_db.res(n + 1).readout_pulse.value for n in range(qpu_db.num_qubits)]
    readout_demod_phase = [qpu_db.res(n + 1).readout_demod_phase.value for n in range(qpu_db.num_qubits)]

    z_readout_pulse_dict = [qpu_db.res(n + 1).z_readout_pulse.value for n in range(qpu_db.num_qubits)]

    rapid_readout_pulse_dict = [qpu_db.res(n + 1).rapid_readout_pulse.value for n in range(qpu_db.num_qubits)]
    rapid_readout_demod_phase = [qpu_db.res(n + 1).rapid_readout_demod_phase.value for n in range(qpu_db.num_qubits)]

    # z_rapid_readout_pulse_dict = [qpu_db.res(n + 1).z_rapid_readout_pulse.value for n in range(qpu_db.num_qubits)] #!!!
    
    # long_readout_pulse_dict = [qpu_db.res(n + 1).long_readout_pulse.value for n in range(qpu_db.num_qubits)]
    # long_ro_demod_phase = [qpu_db.res(n + 1).long_readout_demod_phase.value for n in range(qpu_db.num_qubits)]

    spec_ro_pulse_dict = [qpu_db.res(n + 1).spectroscopy_pulse.value for n in range(qpu_db.num_qubits)]
    fine_spec_ro_pulse_dict = [qpu_db.res(n + 1).fine_spectroscopy_pulse.value for n in range(qpu_db.num_qubits)]

    ## new 0806XZ
    time_of_flight_1 = [qpu_db.get(ro_1_channels[n]['outputs']['out1'][0],
                                                 'time_of_flight').value
                        for n in range(qpu_db.num_qubits)]
    time_of_flight_2 = [qpu_db.get(ro_2_channels[n]['outputs']['out1'][0],
                                                 'time_of_flight').value
                        for n in range(qpu_db.num_qubits)]
    # feedforward_taps = [[[]] * 10] * 4
    con_port_delay = np.zeros((4, 10))
    delay_flag = qpu_db.get('system', 'use_delay').value
    if delay_flag:
        abs_xy_delay = [qpu_db.q(n + 1).abs_xy_delay.value for n in range(qpu_db.num_qubits)]
        abs_z_delay = [qpu_db.q(n + 1).abs_z_delay.value for n in range(qpu_db.num_qubits)]
        abs_ro_delay = [qpu_db.res(n + 1).abs_ro_delay.value for n in range(qpu_db.num_qubits)]
        abs_ro_delay_0 = np.mean(abs_ro_delay[:5])
        abs_ro_delay_5 = np.mean(abs_ro_delay[5:])
        for j in range(qpu_db.num_qubits):
            if j < 5:
                abs_ro_delay[j] = abs_ro_delay_0
            else:
                abs_ro_delay[j] = abs_ro_delay_5
        
        con_port_delay[int(ro_1_channels[0]['inputs']['I'][0][-1]) - 1, ro_1_channels[0]['inputs']['I'][1] - 1] = abs_ro_delay_0
        con_port_delay[int(ro_1_channels[0]['inputs']['Q'][0][-1]) - 1, ro_1_channels[0]['inputs']['Q'][1] - 1] = abs_ro_delay_0
        con_port_delay[int(ro_1_channels[5]['inputs']['I'][0][-1]) - 1, ro_1_channels[5]['inputs']['I'][1] - 1] = abs_ro_delay_5
        con_port_delay[int(ro_1_channels[5]['inputs']['Q'][0][-1]) - 1, ro_1_channels[5]['inputs']['Q'][1] - 1] = abs_ro_delay_5
        for j, xy_chan in enumerate(xy_channels):
            con_port_delay[int(xy_chan['I'][0][-1]) - 1, xy_chan['I'][1] - 1] = abs_xy_delay[j]
            con_port_delay[int(xy_chan['Q'][0][-1]) - 1, xy_chan['Q'][1] - 1] = abs_xy_delay[j]
        for j, z_chan in enumerate(z_channels):
            con_port_delay[int(z_chan[0][-1]) - 1, z_chan[1] - 1] = abs_z_delay[j]
        # feedforward_taps = []
        # for i in range(4):
        #     ff_tap_con = []
        #     for j in range(10):
        #         ff_tap_port = [0] * 40
        #         ff_tap_port[int(np.ceil(con_port_delay[i, j]))] = 1
        #         # feedforward_taps[i][j] = ff_tap_port
        #         ff_tap_con.append(ff_tap_port)
        #     feedforward_taps.append(ff_tap_con)
        
        ## the dig channel delay also needs to be changed!!!
        for j in range(qpu_db.num_qubits): #!!! added 48ns delay due to the use of dummy filters
            ro_1_dig1_delay[j] += int(np.ceil(abs_ro_delay[j])) + 48
            xy_dig1_delay[j] += int(np.ceil(abs_xy_delay[j])) + 48
            time_of_flight_1[j] += (int(np.ceil(abs_ro_delay[j])) // 4) * 4 + 48
            time_of_flight_2[j] += (int(np.ceil(abs_ro_delay[j])) // 4) * 4 + 48

    config = {
        "version": 1,
        "controllers": {
            **{
                f"con{i + 1}": {
                    "type": "opx1",
                    "analog_outputs": {
                        **{
                            (j + 1): {
                                "offset": qpu_db.get(f"con{i + 1}", f"dac{j + 1}_offset").value,
                                "delay": int(con_port_delay[i][j]),
                                # "filter":{
                                #     "feedback": [],
                                #     "feedforward": feedforward_taps[i][j],}  ## new
                                } for j in range(10)
                            },
                        },
                    "digital_outputs": {
                        **{
                            (j + 1): {} for j in range(10)
                            }
                        },
                    "analog_inputs": {
                        **{
                            (j + 1): {
                                "offset": qpu_db.get(f"con{i + 1}", f"adc{j + 1}_offset").value
                                } for j in range(2)
                            }
                        }
                    } for i in range(4)
                }
            },
        "elements": {
            # Readout elements
            **{
                f'r{n + 1}_1': {
                    'mixInputs': {
                        'I': ro_1_channels[n]['inputs']['I'],
                        'Q': ro_1_channels[n]['inputs']['Q'],
                        'lo_frequency': ro_lo_freq[n],
                        'mixer': ro_mixer[n]
                        },
                    'intermediate_frequency': ro_if_freq[n],
                    'digitalInputs': {
                        'switch': {
                            'port': ro_1_dig1_channels[n],
                            'delay': ro_1_dig1_delay[n],
                            'buffer': ro_1_dig1_buffer[n]
                            },
                        },
                    'operations': {
                        'zero': 'zero_pulse',
                        'rapid_readout': f'rapid_readout_pulse_r{n + 1}',
                        'readout': f'readout_pulse_r{n + 1}',
                        # 'z_rapid_readout': f'z_rapid_readout_pulse_r{n + 1}',
                        'z_readout': f'z_readout_pulse_r{n + 1}',
                        # 'long_readout': f'long_readout_pulse_r{n + 1}',
                        'const': 'const_pulse',
                        'spectroscopy': f'spectroscopy_pulse_r{n + 1}',
                        'fine_spectroscopy': f'fine_spectroscopy_pulse_r{n + 1}'
                        },
                    'outputs': {
                        'out1': ro_1_channels[n]['outputs']['out1'],
                        'out2': ro_1_channels[n]['outputs']['out2']
                        },
                    'time_of_flight': time_of_flight_1[n],  ## NEW
                    'smearing': 0,
                    # 'thread': f'thread1_{n + 1}',
                    } for n in range(qpu_db.num_qubits)
                },

            # Readout elements 2
            **{
                f'r{n + 1}_2': {
                    'mixInputs': {
                        'I': ro_2_channels[n]['inputs']['I'],
                        'Q': ro_2_channels[n]['inputs']['Q'],
                        'lo_frequency': ro_lo_freq[n],
                        'mixer': 'fake_ro_mixer'
                        },
                    'intermediate_frequency': ro_if_freq[n],
                    'operations': {
                        'zero': 'zero_pulse',
                        'readout': f'fake_readout_pulse_r{n + 1}',
                        'rapid_readout': f'fake_rapid_readout_pulse_r{n + 1}',
                        # 'z_rapid_readout': f'fake_z_rapid_readout_pulse_r{n + 1}',
                        'z_readout': f'fake_z_readout_pulse_r{n + 1}',
                        # 'long_readout': f'fake_long_readout_pulse_r{n + 1}',
                        'spectroscopy': f'fake_spectroscopy_pulse_r{n + 1}',
                        'fine_spectroscopy': f'fake_spectroscopy_pulse_r{n + 1}',
                        },
                    'outputs': {
                        'out1': ro_2_channels[n]['outputs']['out1'],
                        'out2': ro_2_channels[n]['outputs']['out2']
                        },
                    'time_of_flight': time_of_flight_2[n],  ## NEW
                    'smearing': 0,
                    # 'thread': f'thread2_{n + 1}',
                    } for n in range(qpu_db.num_qubits)
                },

            # Qubit xy elements
            **{
                f'q{n + 1}_xy': {
                    'mixInputs': {
                        'I': xy_channels[n]['I'],
                        'Q': xy_channels[n]['Q'],
                        'lo_frequency': xy_lo_freq[n],
                        'mixer': f'mixer_q{n + 1}_xy'
                        },
                    'intermediate_frequency': xy_if_freq[n],
                    'digitalInputs': {
                        # 'switch': {
                        #     'port': xy_dig1_channels[n],
                        #     'delay': xy_dig1_delay[n],
                        #     'buffer': xy_dig1_buffer[n]
                        #     },
                        # 'trigger': {
                        #     'port': (xy_dig1_channels[n][0], 10),
                        #     'delay': 136,
                        #     'buffer': 0
                        #     }
                        },
                    'operations': {
                        'zero': 'zero_pulse',
                        'const': 'const_pulse',
                        'cosine': 'cosine_pulse',
                        'gaussian': 'gaussian_pulse',
                        'saturation': f'saturation_pulse_q{n + 1}',
                        'spectroscopy': f'spectroscopy_pulse_q{n + 1}',
                        'fine_spectroscopy': f'fine_spectroscopy_pulse_q{n + 1}',
                        'pi': f'pi_pulse_q{n + 1}',
                        'pi2': f'pi_2_pulse_q{n + 1}'
                        },
                    # 'thread': f'thread1_{n + 1}',
                    } for n in range(qpu_db.num_qubits)
                },
            
            # Qubit z elements
            **{
                f'q{n + 1}_z': {
                    'singleInput': {
                        'port': z_channels[n]
                        },
                    'intermediate_frequency': 0,  ## modified after QM's suggestion, added to enable update_correction XZ 20211224
                    'operations': {
                        'const': 'const_flux_pulse'
                        }
                    } for n in range(qpu_db.num_qubits)
                }
            },
        "pulses": {
            ## Common pulses
            'dig_pulse': {
                'operation': 'control',
                'length': 600,
                'digital_marker': 'ON',
                'waveforms': {
                    'single': 'const_wf'
                    }
                },
            
            'zero_pulse': {
                'operation': 'measurement',
                'length': 200,
                'waveforms': {
                    'I': 'zero_wf',
                    'Q': 'zero_wf'
                    },
                'integration_weights': {
                    'integW_cos': 'integW_cos',
                    'integW_sine': 'integW_sine',
                    'integW_minus_sine': 'integW_minus_sine'
                    },
                'digital_marker': 'ON'
                },

            'const_pulse': {
                'operation': 'control',
                'length': const_pulse_dict['properties']['len'],
                'waveforms': {
                    'I': 'const_wf',
                    'Q': 'zero_wf',
                    },
                'digital_marker': 'ON'
                },

            'const_flux_pulse': {
                'operation': 'control',
                'length': const_pulse_dict['properties']['len'],
                'waveforms': {
                    'single': 'const_wf'
                    },
                },

            'gaussian_pulse': {
                'operation': 'control',
                'length': gaussian_pulse_dict['properties']['len'],
                'waveforms': {
                    'I': 'gaussian_wf',
                    'Q': 'zero_wf'
                    },
                'digital_marker': 'ON'
                },

            'cosine_pulse': {
                'operation': 'control',
                'length': cosine_pulse_dict['properties']['len'],
                'waveforms': {
                    'I': 'cosine_wf',
                    'Q': 'zero_wf'
                    },
                'digital_marker': 'ON'
                },

            ## Qubit xy pulses
            # saturation pulse
            **{
                f'saturation_pulse_q{n + 1}': {
                    'operation': 'control',
                    'length': saturation_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'saturation_wf_q{n + 1}',
                        'Q': 'zero_wf'
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },

            # spectroscopy pulse
            **{
                f'spectroscopy_pulse_q{n + 1}': {
                    'operation': 'control',
                    'length': spec_xy_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'spectroscopy_wf_q{n + 1}',
                        'Q': 'zero_wf'
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },

            # fine spectroscopy pulse
            **{
                f'fine_spectroscopy_pulse_q{n + 1}': {
                    'operation': 'control',
                    'length': fine_spec_xy_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'fine_spectroscopy_wf_q{n + 1}',
                        'Q': 'zero_wf'
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },     

            # pi pulse
            **{
                f'pi_pulse_q{n + 1}': {
                    'operation': 'control',
                    'length': pi_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'pi_wfI_q{n + 1}',
                        'Q': f'pi_wfQ_q{n + 1}'
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },
        
            # pi/2 pulse
            **{
                f'pi_2_pulse_q{n + 1}': {
                    'operation': 'control',
                    'length': pi2_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'pi2_wfI_q{n + 1}',
                        'Q': f'pi2_wfQ_q{n + 1}'
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },
            
            # # pi pulse (ef transition)
            # **{
            #     f'pi_ef_pulse_q{n + 1}': {
            #         'operation': 'control',
            #         'length': qpu_db.q(n + 1).pi2_len.value,
            #         'waveforms': {
            #             'I': 'pi_ef_wf_q{}'.format(n + 1),
            #             'Q': 'zero_wf'
            #             },
            #         'digital_marker': 'ON'
            #         } for n in range(qpu_db.num_qubits)
            #     },

            ## Readout pulses
            # regular readout pulse
            **{
                f'readout_pulse_r{n + 1}': {
                    'operation': 'measurement',
                    'length': readout_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'readout_wf_r{n + 1}',
                        'Q': 'zero_wf'
                        },
                    'integration_weights': {
                        'integW_cos': f'integW_cos_r{n + 1}',
                        'integW_sine': f'integW_sine_r{n + 1}',
                        'integW_minus_sine': f'integW_minus_sine_r{n + 1}',
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'z_readout_pulse_r{n + 1}': {
                    'operation': 'measurement',
                    'length': z_readout_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'z_readout_wf_r{n + 1}',
                        'Q': 'zero_wf'
                        },
                    'integration_weights': {
                        'integW_cos': f'z_integW_cos_r{n + 1}',
                        'integW_sine': f'z_integW_sine_r{n + 1}',
                        'integW_minus_sine': f'z_integW_minus_sine_r{n + 1}',
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },


            # Rapid readout pulse
            **{
                f'rapid_readout_pulse_r{n + 1}': {
                    'operation': 'measurement',
                    'length': rapid_readout_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'rapid_readout_wf_r{n + 1}',
                        'Q': 'zero_wf'
                        },
                    'integration_weights': {
                        'integW_cos': f'rapid_integW_cos_r{n + 1}',
                        'integW_sine': f'rapid_integW_sine_r{n + 1}',
                        'integW_minus_sine': f'rapid_integW_minus_sine_r{n + 1}',
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },

            # **{
            #     f'z_rapid_readout_pulse_r{n + 1}': {
            #         'operation': 'measurement',
            #         'length': z_rapid_readout_pulse_dict[n]['properties']['len'],
            #         'waveforms': {
            #             'I': f'z_rapid_readout_wf_r{n + 1}',
            #             'Q': 'zero_wf'
            #             },
            #         'integration_weights': {
            #             'integW_cos': f'z_rapid_integW_cos_r{n + 1}',
            #             'integW_sine': f'z_rapid_integW_sine_r{n + 1}',
            #             'integW_minus_sine': f'z_rapid_integW_minus_sine_r{n + 1}',
            #             },
            #         'digital_marker': 'ON'
            #         } for n in range(qpu_db.num_qubits)
            #     },
            # # Long readout pulse
            # **{
            #     f'long_readout_pulse_r{n + 1}': {
            #         'operation': 'measurement',
            #         'length': long_ro_len[n],
            #         'waveforms': {
            #             'I': f'long_readout_wf_r{n + 1}',
            #             'Q': 'zero_wf'
            #             },
            #         'integration_weights': {
            #             'integW_cos': f'long_integW_cos_r{n + 1}',
            #             'integW_sine': f'long_integW_sine_r{n + 1}',
            #             'integW_minus_sine': f'long_integW_minus_sine_r{n + 1}'
            #             },
            #         'digital_marker': 'ON'
            #         } for n in range(qpu_db.num_qubits)
            #     },

            # Spectroscopy pulse
            **{
                f'spectroscopy_pulse_r{n + 1}': {
                    'operation': 'measurement',
                    'length': spec_ro_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'spectroscopy_wf_r{n + 1}',
                        'Q': 'zero_wf'
                        },
                    'integration_weights': {
                        'integW_cos': f'spec_integW_cos_r{n + 1}',
                        'integW_sine': f'spec_integW_sine_r{n + 1}',
                        'integW_minus_sine': f'spec_integW_minus_sine_r{n + 1}'
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },

                # Spectroscopy pulse
            **{
                f'fine_spectroscopy_pulse_r{n + 1}': {
                    'operation': 'measurement',
                    'length': fine_spec_ro_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': f'fine_spectroscopy_wf_r{n + 1}',
                        'Q': 'zero_wf'
                        },
                    'integration_weights': {
                        'integW_cos': f'spec_integW_cos_r{n + 1}',
                        'integW_sine': f'spec_integW_sine_r{n + 1}',
                        'integW_minus_sine': f'spec_integW_minus_sine_r{n + 1}'
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },

            # Fake readout pulse (for collecting additional readout signal)
            **{
                f'fake_readout_pulse_r{n + 1}': {
                    'operation': 'measurement',
                    'length': readout_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': 'zero_wf',
                        'Q': 'zero_wf'
                        },
                    'integration_weights': {
                        'integW_cos': f'integW_cos_r{n + 1}',
                        'integW_sine': f'integW_sine_r{n + 1}',
                        'integW_minus_sine': f'integW_minus_sine_r{n + 1}',
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },
            
            **{
                f'fake_z_readout_pulse_r{n + 1}': {
                    'operation': 'measurement',
                    'length': z_readout_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': 'zero_wf',
                        'Q': 'zero_wf'
                        },
                    'integration_weights': {
                        'integW_cos': f'z_integW_cos_r{n + 1}',
                        'integW_sine': f'z_integW_sine_r{n + 1}',
                        'integW_minus_sine': f'z_integW_minus_sine_r{n + 1}',
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'fake_rapid_readout_pulse_r{n + 1}': {
                    'operation': 'measurement',
                    'length': rapid_readout_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': 'zero_wf',
                        'Q': 'zero_wf'
                        },
                    'integration_weights': {
                        'integW_cos': f'rapid_integW_cos_r{n + 1}',
                        'integW_sine': f'rapid_integW_sine_r{n + 1}',
                        'integW_minus_sine': f'rapid_integW_minus_sine_r{n + 1}',
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },

            # **{
            #     f'fake_z_rapid_readout_pulse_r{n + 1}': {
            #         'operation': 'measurement',
            #         'length': z_rapid_readout_pulse_dict[n]['properties']['len'],
            #         'waveforms': {
            #             'I': 'zero_wf',
            #             'Q': 'zero_wf'
            #             },
            #         'integration_weights': {
            #             'integW_cos': f'z_rapid_integW_cos_r{n + 1}',
            #             'integW_sine': f'z_rapid_integW_sine_r{n + 1}',
            #             'integW_minus_sine': f'z_rapid_integW_minus_sine_r{n + 1}',
            #             },
            #         'digital_marker': 'ON'
            #         } for n in range(qpu_db.num_qubits)
            #     },
            # **{
            #     f'fake_long_readout_pulse_r{n + 1}': {
            #         'operation': 'measurement',
            #         'length': long_ro_len[n],
            #         'waveforms': {
            #             'I': 'zero_wf',
            #             'Q': 'zero_wf'
            #             },
            #         'integration_weights': {
            #             'integW_cos': 'long_integW_cos_r{n + 1}',
            #             'integW_sine': 'long_integW_sine_r{n + 1}',
            #             'integW_minus_sine': f'long_integW_minus_sine_r{n + 1}'
            #             },
            #         'digital_marker': 'ON'
            #         } for n in range(qpu_db.num_qubits)
            #     },
            **{
                f'fake_spectroscopy_pulse_r{n + 1}': {
                    'operation': 'measurement',
                    'length': spec_ro_pulse_dict[n]['properties']['len'],
                    'waveforms': {
                        'I': 'zero_wf',
                        'Q': 'zero_wf'
                        },
                    'integration_weights': {
                        'integW_cos': f'spec_integW_cos_r{n + 1}',
                        'integW_sine': f'spec_integW_sine_r{n + 1}',
                        'integW_minus_sine': f'spec_integW_minus_sine_r{n + 1}'
                        },
                    'digital_marker': 'ON'
                    } for n in range(qpu_db.num_qubits)
                },
            },
        'waveforms': {
            ## Common waveforms
            # gaussian waveform
            'gaussian_wf': {
                'type': 'arbitrary',
                'samples': []
                },      
            # cosine waveform
            'cosine_wf': {
                'type': 'arbitrary',
                'samples': []
                },
            # zero waveform
            'zero_wf': {
                'type': 'constant',
                'sample': 0.0
                },
            # const waveform
            'const_wf': {
                'type': 'constant',
                'sample': const_pulse_dict['properties']['amp']
                },

            ## Qubit xy waveforms
            # saturation waveform
            **{
                f'saturation_wf_q{n + 1}': {
                    'type': 'constant',
                    'sample': saturation_pulse_dict[n]['properties']['amp']
                    }
                for n in range(qpu_db.num_qubits)
                },

            **{
                f'spectroscopy_wf_q{n + 1}': {
                    'type': 'constant',
                    'sample': spec_xy_pulse_dict[n]['properties']['amp']
                    }
                for n in range(qpu_db.num_qubits)
                },

            **{
                f'fine_spectroscopy_wf_q{n + 1}': {
                    'type': 'constant',
                    'sample': fine_spec_xy_pulse_dict[n]['properties']['amp']
                    }
                for n in range(qpu_db.num_qubits)
                },

            # pi waveform
            **{
                f'pi_wfI_q{n + 1}': {
                    'type': 'arbitrary',
                    'samples': []
                    }
                for n in range(qpu_db.num_qubits)
                },   
            **{
                f'pi_wfQ_q{n + 1}': {
                    'type': 'arbitrary',
                    'samples': []
                    }
                for n in range(qpu_db.num_qubits)
                },   

            # pi/2 waveform
            **{
                f'pi2_wfI_q{n + 1}': {
                    'type': 'arbitrary',
                    'samples': []
                    }
                for n in range(qpu_db.num_qubits)
                },
            **{
                f'pi2_wfQ_q{n + 1}': {
                    'type': 'arbitrary',
                    'samples': []
                    }
                for n in range(qpu_db.num_qubits)
                },
            # # pi waveform (ef transition)
            # **{
            #     'pi_ef_wf_q{}'.format(n + 1): {
            #         'type': 'arbitrary',
            #         'samples': (pi_ef_amp[n] * gaussian(pi_ef_len[n],
            #                                             0.2 * pi_ef_len[n])).tolist()
            #         }
            #     for n in range(len(xy_IF_freq))
            #     },
            # # pi/2 waveform (ef transition)
            # **{
            #     'pi_2_ef_wf_q{}'.format(n + 1): {
            #         'type': 'arbitrary',
            #         'samples': (pi_2_ef_amp[n] * gaussian(pi_2_ef_len[n],
            #                                               0.2 * pi_2_ef_len[n])).tolist()
            #         }
            #     for n in range(len(xy_IF_freq))
            #     },
            ## Readout waveforms
            # readout waveform
            **{
                f'readout_wf_r{n + 1}': {
                    'type': 'arbitrary',
                    'samples': []
                    }
                for n in range(qpu_db.num_qubits)
                },

            **{
                f'z_readout_wf_r{n + 1}': {
                    'type': 'arbitrary',
                    'samples': []
                    }
                for n in range(qpu_db.num_qubits)
                },

            # rapid readout waveform
            **{
                f'rapid_readout_wf_r{n + 1}': {
                    'type': 'arbitrary',
                    'samples': []
                    }
                for n in range(qpu_db.num_qubits)
                },

            # **{
            #     f'z_rapid_readout_wf_r{n + 1}': {
            #         'type': 'arbitrary',
            #         'samples': []
            #         }
            #     for n in range(qpu_db.num_qubits)
            #     },

            # # long readout waveform
            # **{
            #     f'long_ro_wf_r{n + 1}': {
            #         'type': 'constant',
            #         'sample': long_ro_amp[n]
            #         }
            #     for n in range(qpu_db.num_qubits)
            #     },

            **{
                f'spectroscopy_wf_r{n + 1}': {
                    'type': 'constant',
                    'sample': spec_ro_pulse_dict[n]['properties']['amp']
                    }
                for n in range(qpu_db.num_qubits)
                },

            **{
                f'fine_spectroscopy_wf_r{n + 1}': {
                    'type': 'constant',
                    'sample': fine_spec_ro_pulse_dict[n]['properties']['amp']
                    }
                for n in range(qpu_db.num_qubits)
                },
            },
        'digital_waveforms': {
            'ON': {
                'samples': [(1,0)]
                },
            },
        'integration_weights': {
            
            # integration weights for zero pulse
            'integW_cos': {
                'cosine': [1.0] * int(1000 / 4),
                'sine': [0.0] * int(1000 / 4)
                },
            'integW_sine': {
                'cosine': [0.0] * int(1000 / 4),
                'sine': [1.0] * int(1000 / 4)
                },
            'integW_minus_sine': {
                'cosine': [0.0] * int(1000 / 4),
                'sine': [-1.0] * int(1000 / 4)
                },

            **{
                f'integW_cos_r{n + 1}': {
                    'cosine': [np.cos(readout_demod_phase[n])] * int(readout_pulse_dict[n]['properties']['len'] / 4),
                    'sine': [-np.sin(readout_demod_phase[n])] * int(readout_pulse_dict[n]['properties']['len'] / 4)
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'integW_sine_r{n + 1}': {
                    'cosine': [np.sin(readout_demod_phase[n])] * int(readout_pulse_dict[n]['properties']['len'] / 4),
                    'sine': [np.cos(readout_demod_phase[n])] * int(readout_pulse_dict[n]['properties']['len'] / 4)
                    } for n in range(qpu_db.num_qubits)
                },
        
            **{
                f'integW_minus_sine_r{n + 1}': {
                    'cosine': [-np.sin(readout_demod_phase[n])] * int(readout_pulse_dict[n]['properties']['len'] / 4),
                    'sine': [-np.cos(readout_demod_phase[n])] * int(readout_pulse_dict[n]['properties']['len'] / 4)
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'rapid_integW_cos_r{n + 1}': {
                    'cosine': [np.cos(rapid_readout_demod_phase[n])] * int(rapid_readout_pulse_dict[n]['properties']['len'] / 4),
                    'sine': [-np.sin(rapid_readout_demod_phase[n])] * int(rapid_readout_pulse_dict[n]['properties']['len'] / 4)
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'rapid_integW_sine_r{n + 1}': {
                    'cosine': [np.sin(rapid_readout_demod_phase[n])] * int(rapid_readout_pulse_dict[n]['properties']['len'] / 4),
                    'sine': [np.cos(rapid_readout_demod_phase[n])] * int(rapid_readout_pulse_dict[n]['properties']['len'] / 4)
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'rapid_integW_minus_sine_r{n + 1}': {
                    'cosine': [-np.sin(rapid_readout_demod_phase[n])] * int(rapid_readout_pulse_dict[n]['properties']['len'] / 4),
                    'sine': [-np.cos(rapid_readout_demod_phase[n])] * int(rapid_readout_pulse_dict[n]['properties']['len'] / 4)
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'z_integW_cos_r{n + 1}': {
                    'cosine': [np.cos(readout_demod_phase[n])] * int(z_readout_pulse_dict[n]['properties']['len'] / 4),
                    'sine': [-np.sin(readout_demod_phase[n])] * int(z_readout_pulse_dict[n]['properties']['len'] / 4)
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'z_integW_sine_r{n + 1}': {
                    'cosine': [np.sin(readout_demod_phase[n])] * int(z_readout_pulse_dict[n]['properties']['len'] / 4),
                    'sine': [np.cos(readout_demod_phase[n])] * int(z_readout_pulse_dict[n]['properties']['len'] / 4)
                    } for n in range(qpu_db.num_qubits)
                },
        
            **{
                f'z_integW_minus_sine_r{n + 1}': {
                    'cosine': [-np.sin(readout_demod_phase[n])] * int(z_readout_pulse_dict[n]['properties']['len'] / 4),
                    'sine': [-np.cos(readout_demod_phase[n])] * int(z_readout_pulse_dict[n]['properties']['len'] / 4)
                    } for n in range(qpu_db.num_qubits)
                },

            # **{
            #     f'z_rapid_integW_cos_r{n + 1}': {
            #         'cosine': [np.cos(rapid_readout_demod_phase[n])] * int(z_rapid_readout_pulse_dict[n]['properties']['len'] / 4),
            #         'sine': [-np.sin(rapid_readout_demod_phase[n])] * int(z_rapid_readout_pulse_dict[n]['properties']['len'] / 4)
            #         } for n in range(qpu_db.num_qubits)
            #     },

            # **{
            #     f'z_rapid_integW_sine_r{n + 1}': {
            #         'cosine': [np.sin(rapid_readout_demod_phase[n])] * int(z_rapid_readout_pulse_dict[n]['properties']['len'] / 4),
            #         'sine': [np.cos(rapid_readout_demod_phase[n])] * int(z_rapid_readout_pulse_dict[n]['properties']['len'] / 4)
            #         } for n in range(qpu_db.num_qubits)
            #     },

            # **{
            #     f'z_rapid_integW_minus_sine_r{n + 1}': {
            #         'cosine': [-np.sin(rapid_readout_demod_phase[n])] * int(z_rapid_readout_pulse_dict[n]['properties']['len'] / 4),
            #         'sine': [-np.cos(rapid_readout_demod_phase[n])] * int(z_rapid_readout_pulse_dict[n]['properties']['len'] / 4)
            #         } for n in range(qpu_db.num_qubits)
            #     },
            # **{
            #     f'long_integW_cos_r{n + 1}': {
            #         'cosine': [np.cos(long_ro_demod_phase[n])] * int(long_ro_len[n] / 4),
            #         'sine': [-np.sin(long_ro_demod_phase[n])] * int(long_ro_len[n] / 4)
            #         } for n in range(qpu_db.num_qubits)
            #     },

            # **{
            #     f'long_integW_sine_r{n + 1}': {
            #         'cosine': [np.sin(long_ro_demod_phase[n])] * int(long_ro_len[n] / 4),
            #         'sine': [np.cos(long_ro_demod_phase[n])] * int(long_ro_len[n] / 4)
            #         } for n in range(qpu_db.num_qubits)
            #     },

            # **{
            #     f'long_integW_minus_sine_r{n + 1}': {
            #         'cosine': [-np.sin(long_ro_demod_phase[n])] * int(long_ro_len[n] / 4),
            #         'sine': [-np.cos(long_ro_demod_phase[n])] * int(long_ro_len[n] / 4)
            #         } for n in range(qpu_db.num_qubits)
            #     },
            **{
                f'spec_integW_cos_r{n + 1}': {
                    'cosine': [(1.0, spec_ro_pulse_dict[n]['properties']['len'])], ###
                    'sine': [(0.0, spec_ro_pulse_dict[n]['properties']['len'])]    ###
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'spec_integW_sine_r{n + 1}': {
                    'cosine': [(0.0, spec_ro_pulse_dict[n]['properties']['len'])],
                    'sine': [(1.0, spec_ro_pulse_dict[n]['properties']['len'])]
                    } for n in range(qpu_db.num_qubits)
                },

            **{
                f'spec_integW_minus_sine_r{n + 1}': {
                    'cosine': [(0.0, spec_ro_pulse_dict[n]['properties']['len'])],
                    'sine': [(-1.0, spec_ro_pulse_dict[n]['properties']['len'])]
                    } for n in range(qpu_db.num_qubits)
                },

            },
        'mixers': {
            'mixer_ro1': [
                *[{'intermediate_frequency': ro_if_freq[n],
                   'lo_frequency': ro_lo_freq[n],
                   'correction': iq_imbalance_corr(0, 0)} for n in range(qpu_db.num_qubits)]],
            'mixer_ro2': [
                *[{'intermediate_frequency': ro_if_freq[n],
                   'lo_frequency': ro_lo_freq[n],
                   'correction': iq_imbalance_corr(0, 0)} for n in range(qpu_db.num_qubits)]],
            'fake_ro_mixer': [
                *[{'intermediate_frequency': ro_if_freq[n],
                   'lo_frequency': ro_lo_freq[n],
                   'correction': iq_imbalance_corr(0, 0)} for n in range(qpu_db.num_qubits)]],
            **{f'mixer_q{n + 1}_xy': [
                *[{'intermediate_frequency': xy_if_freq[n], # xy IF frequency (ge transition)
                   'lo_frequency': xy_lo_freq[n],
                   'correction': iq_imbalance_corr(0, 0)}]]
                for n in range(qpu_db.num_qubits)}
            }
        }
    
    # this is to add a dummy filter for the delay #!!!
    config["controllers"]["con1"]["analog_outputs"][10]["filter"] = {
        "feedback": [0.0, 0.0],
        "feedforward": [1.0]}

    # # manual thread allocation #!!!
    # config["elements"]["r1_2"]["thread"] = "a"
    # config["elements"]["q5_z"]["thread"] = "a"
    # config["elements"]["r6_2"]["thread"] = "b"
    # config["elements"]["q8_z"]["thread"] = "b"
    
    
    # update common pulses
    update_op_from_pulse_dict(const_pulse_dict, 'const', 'q1_xy', config)
    update_op_from_pulse_dict(cosine_pulse_dict, 'cosine', 'q1_xy', config)
    update_op_from_pulse_dict(gaussian_pulse_dict, 'gaussian', 'q1_xy', config)

    for n in range(qpu_db.num_qubits):
        # update xy pulses
        update_op_from_pulse_dict(spec_xy_pulse_dict[n], 'spectroscopy', f'q{n + 1}_xy', config)
        update_op_from_pulse_dict(fine_spec_xy_pulse_dict[n], 'fine_spectroscopy', f'q{n + 1}_xy', config)
        update_op_from_pulse_dict(saturation_pulse_dict[n], 'saturation', f'q{n + 1}_xy', config)
        update_op_from_pulse_dict(pi_pulse_dict[n], 'pi', f'q{n + 1}_xy', config)
        update_op_from_pulse_dict(pi2_pulse_dict[n], 'pi2', f'q{n + 1}_xy', config)

        # update readout pulses
        update_op_from_pulse_dict(readout_pulse_dict[n], 'readout', f'r{n + 1}_1', config)
        update_op_from_pulse_dict(rapid_readout_pulse_dict[n], 'rapid_readout', f'r{n + 1}_1', config)

        update_op_from_pulse_dict(z_readout_pulse_dict[n], 'z_readout', f'r{n + 1}_1', config)
        # update_op_from_pulse_dict(z_rapid_readout_pulse_dict[n], 'z_rapid_readout', f'r{n + 1}_1', config)

        update_op_from_pulse_dict(spec_ro_pulse_dict[n], 'spectroscopy', f'r{n + 1}_1', config)
        update_op_from_pulse_dict(fine_spec_ro_pulse_dict[n], 'fine_spectroscopy', f'r{n + 1}_1', config)
    
    with open("configuration.pkl", "wb") as file:
        pickle.dump(config, file)

    return {'config': config}