From 5c407b171567b92f0a47b1f18b171141bdf8a826 Mon Sep 17 00:00:00 2001 From: Jun Yong Khoo Date: Wed, 29 Jan 2025 02:28:01 +0800 Subject: [PATCH] added make_emulator_runcard in pulse_simulator --- .../emulator/models/general_coupler_model.py | 200 ++++++++++++------ .../models/general_no_coupler_model.py | 123 +++++++---- .../instruments/emulator/models/methods.py | 36 +++- .../instruments/emulator/pulse_simulator.py | 92 ++++++-- 4 files changed, 311 insertions(+), 140 deletions(-) diff --git a/src/qibolab/instruments/emulator/models/general_coupler_model.py b/src/qibolab/instruments/emulator/models/general_coupler_model.py index 9b4ed034e..b3d6f0dda 100644 --- a/src/qibolab/instruments/emulator/models/general_coupler_model.py +++ b/src/qibolab/instruments/emulator/models/general_coupler_model.py @@ -3,7 +3,7 @@ import numpy as np from qibolab.instruments.emulator.models.methods import ( - default_platform_to_simulator_channels, + default_platform_to_simulator_qubits, default_platform_to_simulator_channels, ) GHZ = 1e9 @@ -70,11 +70,21 @@ def generate_default_params(): } return model_params - +# to add relabel_qubits +''' +emulator_qubits_list = [] + for i,q in enumerate(qubits_list): + if relabel_qubits: + i = str(i) + else: + i = q + emulator_qubits_list.append(int(i)) + ''' def get_model_params( platform_data_dict: dict, nlevels_q: Union[int, List[int]], nlevels_c: Union[int, List[int]], + relabel_qubits: bool=False, ) -> dict: """Generates the model paramters for the general coupler model. @@ -82,6 +92,7 @@ def get_model_params( platform_data_dict(dict): Dictionary containing the device data extracted from a device platform. nlevels_q(int, list): Number of levels for each qubit. If int, the same value gets assigned to all qubits. nlevels_c(int, list): Number of levels for each coupler. If int, the same value gets assigned to all couplers. + relabel_qubits(bool): Flag to relabel qubits to ascending integers. False by default. Returns: dict: Model parameters dictionary with all frequencies in GHz and times in ns that is required as an input to emulator runcards. @@ -96,31 +107,6 @@ def get_model_params( characterization_dict = platform_data_dict["characterization"] qubit_characterization_dict = characterization_dict["qubits"] - model_params_dict |= {"nqubits": len(qubits_list)} - model_params_dict |= {"ncouplers": len(couplers_list)} - model_params_dict |= {"qubits_list": [str(q) for q in qubits_list]} - model_params_dict |= {"couplers_list": [str(c) for c in couplers_list]} - - if type(nlevels_q) == int: - model_params_dict |= {"nlevels_q": [nlevels_q for q in qubits_list]} - elif type(nlevels_q) == list: - if len(nlevels_q) == len(qubits_list): - model_params_dict |= {"nlevels_q": nlevels_q} - else: - raise ValueError( - "Length of nlevels_q does not match number of qubits", len(qubits_list) - ) - if type(nlevels_c) == int: - model_params_dict |= {"nlevels_c": [nlevels_c for c in couplers_list]} - elif type(nlevels_c) == list: - if len(nlevels_c) == len(couplers_list): - model_params_dict |= {"nlevels_c": nlevels_c} - else: - raise ValueError( - "Length of nlevels_c does not match number of couplers", - len(couplers_list), - ) - drive_freq_dict = {} T1_dict = {} T2_dict = {} @@ -130,33 +116,41 @@ def get_model_params( anharmonicity_dict = {} readout_error_dict = {} - for q in qubits_list: + relabelled_qubits_list = [] + for i,q in enumerate(qubits_list): + if relabel_qubits: + i = str(i) + else: + i = str(q) + relabelled_qubits_list.append(i) + af = qubit_characterization_dict[q]["assignment_fidelity"] if af == 0: - readout_error_dict |= {str(q): [0.0, 0.0]} + readout_error_dict |= {i: [0.0, 0.0]} else: if type(af) is float: p0m1 = p1m0 = 1 - af else: p0m1, p1m0 = 1 - np.array(af) - readout_error_dict |= {str(q): [p0m1, p1m0]} + readout_error_dict |= {i: [p0m1, p1m0]} drive_freq_dict |= { - str(q): qubit_characterization_dict[q]["drive_frequency"] / GHZ + i: qubit_characterization_dict[q]["drive_frequency"] / GHZ } - T1_dict |= {str(q): qubit_characterization_dict[q]["T1"]} - T2_dict |= {str(q): qubit_characterization_dict[q]["T2"]} + T1_dict |= {i: qubit_characterization_dict[q]["T1"]} + T2_dict |= {i: qubit_characterization_dict[q]["T2"]} max_lo_freq_dict |= { - str(q): qubit_characterization_dict[q]["max_lo_freq"] / GHZ + i: qubit_characterization_dict[q]["max_lo_freq"] / GHZ } rabi_freq_dict |= { - str(q): qubit_characterization_dict[q]["rabi_frequency"] / GHZ + i: qubit_characterization_dict[q]["rabi_frequency"] / GHZ } anharmonicity_dict |= { - str(q): qubit_characterization_dict[q]["anharmonicity"] / GHZ + i: qubit_characterization_dict[q]["anharmonicity"] / GHZ } - # flux_quanta_dict |= {str(q): 0.1} # to be updated + # flux_quanta_dict |= {i: 0.1} # to be updated """ + # TODO for c in couplers_list: drive_freq_dict |= {str(c): qubit_characterization_dict[c]['drive_frequency']/GHZ} T1_dict |= {str(c): qubit_characterization_dict[c]['T1']} @@ -166,6 +160,31 @@ def get_model_params( anharmonicity_dict |= {str(c): qubit_characterization_dict[c]['anharmonicity']/GHZ} #flux_quanta_dict |= {str(c): 0.1} # to be updated """ + + model_params_dict |= {"nqubits": len(qubits_list)} + model_params_dict |= {"ncouplers": len(couplers_list)} + model_params_dict |= {"qubits_list": relabelled_qubits_list} + model_params_dict |= {"couplers_list": [str(c) for c in couplers_list]} + + if type(nlevels_q) == int: + model_params_dict |= {"nlevels_q": [nlevels_q for q in qubits_list]} + elif type(nlevels_q) == list: + if len(nlevels_q) == len(qubits_list): + model_params_dict |= {"nlevels_q": nlevels_q} + else: + raise ValueError( + "Length of nlevels_q does not match number of qubits", len(qubits_list) + ) + if type(nlevels_c) == int: + model_params_dict |= {"nlevels_c": [nlevels_c for c in couplers_list]} + elif type(nlevels_c) == list: + if len(nlevels_c) == len(couplers_list): + model_params_dict |= {"nlevels_c": nlevels_c} + else: + raise ValueError( + "Length of nlevels_c does not match number of couplers", + len(couplers_list), + ) model_params_dict |= {"readout_error": readout_error_dict} model_params_dict |= {"drive_freq": drive_freq_dict} @@ -206,8 +225,14 @@ def generate_model_config( model_name = model_params["model_name"] readout_error = model_params["readout_error"] - qubits_list = model_params["qubits_list"] - couplers_list = model_params["couplers_list"] + #qubits_list = model_params["qubits_list"] + device_qubits_list = model_params["qubits_list"] + #couplers_list = model_params["couplers_list"] + device_couplers_list = model_params["couplers_list"] + + platform_to_simulator_qubits = default_platform_to_simulator_qubits(device_qubits_list, device_couplers_list) + qubits_list = [platform_to_simulator_qubits[q] for q in device_qubits_list] + couplers_list = [platform_to_simulator_qubits[c] for c in device_couplers_list] if nlevels_q is None: nlevels_q = model_params["nlevels_q"] @@ -223,33 +248,45 @@ def generate_model_config( # generate instructions # single qubit terms - for i, q in enumerate(qubits_list): + #for i, q in enumerate(qubits_list): + for q in device_qubits_list: + ind = platform_to_simulator_qubits[q] # drift Hamiltonian terms (constant in time) drift_hamiltonian_dict["one_body"].append( - (2 * np.pi * model_params["drive_freq"][q], f"O_{q}", [q]) + #(2 * np.pi * model_params["drive_freq"][q], f"O_{q}", [q]) + (2 * np.pi * model_params["drive_freq"][q], f"O_{ind}", [ind]) ) drift_hamiltonian_dict["one_body"].append( ( np.pi * model_params["anharmonicity"][q], - f"O_{q} * O_{q} - O_{q}", - [q], + #f"O_{q} * O_{q} - O_{q}", + f"O_{ind} * O_{ind} - O_{ind}", + #[q], + [ind], ) ) # drive Hamiltonian terms (amplitude determined by pulse sequence) - drive_hamiltonian_dict.update({f"D-{qubits_list[i]}": []}) - drive_hamiltonian_dict[f"D-{qubits_list[i]}"].append( - (2 * np.pi * model_params["rabi_freq"][q], f"X_{q}", [q]) + #drive_hamiltonian_dict.update({f"D-{qubits_list[i]}": []}) + drive_hamiltonian_dict.update({f"D-{ind}": []}) + #drive_hamiltonian_dict[f"D-{qubits_list[i]}"].append( + # (2 * np.pi * model_params["rabi_freq"][q], f"X_{q}", [q]) + #) + drive_hamiltonian_dict[f"D-{ind}"].append( + (2 * np.pi * model_params["rabi_freq"][q], f"X_{ind}", [ind]) ) # flux Hamiltonian terms (amplitude determined by processed pulse sequence) - flux_hamiltonian_dict.update({f"F-{qubits_list[i]}": []}) - flux_hamiltonian_dict[f"F-{qubits_list[i]}"].append((2 * np.pi, f"O_{q}", [q])) + #flux_hamiltonian_dict.update({f"F-{qubits_list[i]}": []}) + flux_hamiltonian_dict.update({f"F-{ind}": []}) + #flux_hamiltonian_dict[f"F-{qubits_list[i]}"].append((2 * np.pi, f"O_{q}", [q])) + flux_hamiltonian_dict[f"F-{ind}"].append((2 * np.pi, f"O_{ind}", [ind])) # flux detuning parameters: try: flux_params_dict |= { - q: { + #q: { + ind: { "flux_quanta": model_params["flux_quanta"][q], "max_frequency": model_params["max_lo_freq"][q], "current_frequency": model_params["drive_freq"][q], @@ -260,37 +297,52 @@ def generate_model_config( # dissipation terms (one qubit, constant in time) t1 = model_params["T1"][q] - g1 = 0 if t1 == 0 else 1.0 / t1 * 2 * np.pi + g1 = 0 if t1 == 0 else 1.0 / t1 #* 2 * np.pi t2 = model_params["T2"][q] - g2 = 0 if t1 == 0 else 1.0 / t2 * 2 * np.pi + g2 = 0 if t1 == 0 else 1.0 / t2 #* 2 * np.pi - dissipation_dict["t1"].append((np.sqrt(g1 / 2), f"sp01_{q}", [q])) - dissipation_dict["t2"].append((np.sqrt(g2 / 2), f"Z01_{q}", [q])) + #dissipation_dict["t1"].append((np.sqrt(g1 / 2), f"sp01_{q}", [q])) + #dissipation_dict["t2"].append((np.sqrt(g2 / 2), f"Z01_{q}", [q])) + #dissipation_dict["t1"].append((np.sqrt(g1 / 2), f"sp01_{ind}", [ind])) + #dissipation_dict["t2"].append((np.sqrt(g2 / 2), f"Z01_{ind}", [ind])) + dissipation_dict["t1"].append((np.sqrt(g1), f"sp01_{ind}", [ind])) + dissipation_dict["t2"].append((np.sqrt(g2), f"Z01_{ind}", [ind])) # single coupler terms - for i, c in enumerate(couplers_list): + #for i, c in enumerate(couplers_list): + for c in couplers_list: + ind = platform_to_simulator_qubits[c] # drift Hamiltonian terms (constant in time) drift_hamiltonian_dict["one_body"].append( - (2 * np.pi * model_params["drive_freq"][c], f"O_{c}", [c]) + #(2 * np.pi * model_params["drive_freq"][c], f"O_{c}", [c]) + (2 * np.pi * model_params["drive_freq"][c], f"O_{ind}", [ind]) ) drift_hamiltonian_dict["one_body"].append( ( np.pi * model_params["anharmonicity"][c], - f"O_{c} * O_{c} - O_{c}", - [c], + #f"O_{c} * O_{c} - O_{c}", + #[c], + f"O_{ind} * O_{ind} - O_{ind}", + [ind], ) ) # drive Hamiltonian terms (amplitude determined by pulse sequence) - drive_hamiltonian_dict.update({f"D-{couplers_list[i]}": []}) - drive_hamiltonian_dict[f"D-{couplers_list[i]}"].append( - (2 * np.pi * model_params["rabi_freq"][c], f"X_{c}", [c]) + #drive_hamiltonian_dict.update({f"D-{couplers_list[i]}": []}) + drive_hamiltonian_dict.update({f"D-{ind}": []}) + #drive_hamiltonian_dict[f"D-{couplers_list[i]}"].append( + drive_hamiltonian_dict[f"D-{ind}"].append( + #(2 * np.pi * model_params["rabi_freq"][c], f"X_{c}", [c]) + (2 * np.pi * model_params["rabi_freq"][c], f"X_{ind}", [ind]) ) # flux Hamiltonian terms (amplitude determined by processed pulse sequence) - flux_hamiltonian_dict.update({f"F-{couplers_list[i]}": []}) - flux_hamiltonian_dict[f"F-{couplers_list[i]}"].append( - (2 * np.pi, f"O_{c}", [c]) + #flux_hamiltonian_dict.update({f"F-{couplers_list[i]}": []}) + flux_hamiltonian_dict.update({f"F-{ind}": []}) + #flux_hamiltonian_dict[f"F-{couplers_list[i]}"].append( + flux_hamiltonian_dict[f"F-{ind}"].append( + #(2 * np.pi, f"O_{c}", [c]) + (2 * np.pi, f"O_{ind}", [ind]) ) # flux detuning parameters: @@ -307,18 +359,25 @@ def generate_model_config( # dissipation terms for couplers t1 = model_params["T1"][c] - g1 = 0 if t1 == 0 else 1.0 / t1 * 2 * np.pi + g1 = 0 if t1 == 0 else 1.0 / t1 #* 2 * np.pi t2 = model_params["T2"][c] - g2 = 0 if t1 == 0 else 1.0 / t2 * 2 * np.pi + g2 = 0 if t1 == 0 else 1.0 / t2 #* 2 * np.pi - dissipation_dict["t1"].append((np.sqrt(g1 / 2), f"sp01_{c}", [c])) - dissipation_dict["t2"].append((np.sqrt(g2 / 2), f"Z01_{c}", [c])) + #dissipation_dict["t1"].append((np.sqrt(g1 / 2), f"sp01_{c}", [c])) + #dissipation_dict["t2"].append((np.sqrt(g2 / 2), f"Z01_{c}", [c])) + #dissipation_dict["t1"].append((np.sqrt(g1 / 2), f"sp01_{ind}", [ind])) + #dissipation_dict["t2"].append((np.sqrt(g2 / 2), f"Z01_{ind}", [ind])) + dissipation_dict["t1"].append((np.sqrt(g1), f"sp01_{ind}", [ind])) + dissipation_dict["t2"].append((np.sqrt(g2), f"Z01_{ind}", [ind])) # two-body terms (couplings) - for key in list(model_params["coupling_strength"].keys()): + for key in list(model_params["coupling_strength"].keys()): # consistent with device_qubits_list ind2, ind1 = key.split( "_" ) # ind2 > ind1 with ind_qubit > ind_coupler as per Hilbert space ordering + ind1 = platform_to_simulator_qubits[ind1] + ind2 = platform_to_simulator_qubits[ind2] + coupling = model_params["coupling_strength"][key] drift_hamiltonian_dict["two_body"].append( ( @@ -331,6 +390,8 @@ def generate_model_config( model_config = { "model_name": model_name, "topology": topology, + "device_qubits_list": device_qubits_list, + "device_couplers_list": device_couplers_list, "qubits_list": qubits_list, "nlevels_q": nlevels_q, "couplers_list": couplers_list, @@ -342,9 +403,10 @@ def generate_model_config( "dissipation": dissipation_dict, "method": "master_equation", "readout_error": readout_error, + "platform_to_simulator_qubits": platform_to_simulator_qubits, # "platform_to_simulator_channels": default_noflux_platform_to_simulator_channels( "platform_to_simulator_channels": default_platform_to_simulator_channels( - qubits_list, couplers_list + device_qubits_list, device_couplers_list ), } diff --git a/src/qibolab/instruments/emulator/models/general_no_coupler_model.py b/src/qibolab/instruments/emulator/models/general_no_coupler_model.py index ab647e518..71bd1913f 100644 --- a/src/qibolab/instruments/emulator/models/general_no_coupler_model.py +++ b/src/qibolab/instruments/emulator/models/general_no_coupler_model.py @@ -3,7 +3,7 @@ import numpy as np from qibolab.instruments.emulator.models.methods import ( - default_platform_to_simulator_channels, + default_platform_to_simulator_qubits, default_platform_to_simulator_channels, ) GHZ = 1e9 @@ -65,12 +65,14 @@ def generate_default_params(): def get_model_params( platform_data_dict: dict, nlevels_q: Union[int, List[int]], + relabel_qubits: bool=False, ) -> dict: """Generates the model paramters for the general no coupler model. Args: platform_data_dict(dict): Dictionary containing the device data extracted from a device platform. nlevels_q(int, list): Number of levels for each qubit. If int, the same value gets assigned to all qubits. + relabel_qubits(bool): Flag to relabel qubits to ascending integers. False by default. Returns: dict: Model parameters dictionary with all frequencies in GHz and times in ns that is required as an input to emulator runcards. @@ -85,22 +87,6 @@ def get_model_params( characterization_dict = platform_data_dict["characterization"] qubit_characterization_dict = characterization_dict["qubits"] - model_params_dict |= {"nqubits": len(qubits_list)} - model_params_dict |= {"ncouplers": len(couplers_list)} - model_params_dict |= {"qubits_list": [str(q) for q in qubits_list]} - model_params_dict |= {"couplers_list": [str(c) for c in couplers_list]} - - if type(nlevels_q) == int: - model_params_dict |= {"nlevels_q": [nlevels_q for q in qubits_list]} - elif type(nlevels_q) == list: - if len(nlevels_q) == len(qubits_list): - model_params_dict |= {"nlevels_q": nlevels_q} - else: - raise ValueError( - "Length of nlevels_q does not match number of qubits", len(qubits_list) - ) - model_params_dict |= {"nlevels_c": []} - drive_freq_dict = {} T1_dict = {} T2_dict = {} @@ -110,31 +96,54 @@ def get_model_params( anharmonicity_dict = {} readout_error_dict = {} - for q in qubits_list: + relabelled_qubits_list = [] + for i,q in enumerate(qubits_list): + if relabel_qubits: + i = str(i) + else: + i = str(q) + relabelled_qubits_list.append(i) + af = qubit_characterization_dict[q]["assignment_fidelity"] if af == 0: - readout_error_dict |= {str(q): [0.0, 0.0]} + readout_error_dict |= {i: [0.0, 0.0]} else: if type(af) is float: p0m1 = p1m0 = 1 - af else: p0m1, p1m0 = 1 - np.array(af) - readout_error_dict |= {str(q): [p0m1, p1m0]} + readout_error_dict |= {i: [p0m1, p1m0]} drive_freq_dict |= { - str(q): qubit_characterization_dict[q]["drive_frequency"] / GHZ + i: qubit_characterization_dict[q]["drive_frequency"] / GHZ } - T1_dict |= {str(q): qubit_characterization_dict[q]["T1"]} - T2_dict |= {str(q): qubit_characterization_dict[q]["T2"]} + T1_dict |= {i: qubit_characterization_dict[q]["T1"]} + T2_dict |= {i: qubit_characterization_dict[q]["T2"]} max_lo_freq_dict |= { - str(q): qubit_characterization_dict[q]["drive_frequency"] / GHZ + i: qubit_characterization_dict[q]["drive_frequency"] / GHZ } rabi_freq_dict |= { - str(q): qubit_characterization_dict[q]["rabi_frequency"] / GHZ + i: qubit_characterization_dict[q]["rabi_frequency"] / GHZ } anharmonicity_dict |= { - str(q): qubit_characterization_dict[q]["anharmonicity"] / GHZ + i: qubit_characterization_dict[q]["anharmonicity"] / GHZ } - # flux_quanta_dict |= {str(q): 0.1} # to be updated + # flux_quanta_dict |= {i: 0.1} # to be updated + + model_params_dict |= {"nqubits": len(qubits_list)} + model_params_dict |= {"ncouplers": len(couplers_list)} + model_params_dict |= {"qubits_list": relabelled_qubits_list} + model_params_dict |= {"couplers_list": []} + + if type(nlevels_q) == int: + model_params_dict |= {"nlevels_q": [nlevels_q for q in qubits_list]} + elif type(nlevels_q) == list: + if len(nlevels_q) == len(qubits_list): + model_params_dict |= {"nlevels_q": nlevels_q} + else: + raise ValueError( + "Length of nlevels_q does not match number of qubits", len(qubits_list) + ) + model_params_dict |= {"nlevels_c": []} model_params_dict |= {"readout_error": readout_error_dict} model_params_dict |= {"drive_freq": drive_freq_dict} @@ -173,8 +182,12 @@ def generate_model_config( model_name = model_params["model_name"] readout_error = model_params["readout_error"] - qubits_list = model_params["qubits_list"] + #qubits_list = model_params["qubits_list"] + device_qubits_list = model_params["qubits_list"] + platform_to_simulator_qubits = default_platform_to_simulator_qubits(device_qubits_list, couplers_list=[]) + qubits_list = [platform_to_simulator_qubits[q] for q in device_qubits_list] + if nlevels_q is None: nlevels_q = model_params["nlevels_q"] @@ -187,33 +200,45 @@ def generate_model_config( # generate instructions # single qubit terms - for i, q in enumerate(qubits_list): + #for i, q in enumerate(qubits_list): + for q in device_qubits_list: + ind = platform_to_simulator_qubits[q] # drift Hamiltonian terms (constant in time) drift_hamiltonian_dict["one_body"].append( - (2 * np.pi * model_params["drive_freq"][q], f"O_{q}", [q]) + #(2 * np.pi * model_params["drive_freq"][q], f"O_{q}", [q]) + (2 * np.pi * model_params["drive_freq"][q], f"O_{ind}", [ind]) ) drift_hamiltonian_dict["one_body"].append( ( np.pi * model_params["anharmonicity"][q], - f"O_{q} * O_{q} - O_{q}", - [q], + #f"O_{q} * O_{q} - O_{q}", + f"O_{ind} * O_{ind} - O_{ind}", + #[q], + [ind], ) ) # drive Hamiltonian terms (amplitude determined by pulse sequence) - drive_hamiltonian_dict.update({f"D-{qubits_list[i]}": []}) - drive_hamiltonian_dict[f"D-{qubits_list[i]}"].append( - (2 * np.pi * model_params["rabi_freq"][q], f"X_{q}", [q]) + #drive_hamiltonian_dict.update({f"D-{qubits_list[i]}": []}) + drive_hamiltonian_dict.update({f"D-{ind}": []}) + #drive_hamiltonian_dict[f"D-{qubits_list[i]}"].append( + # (2 * np.pi * model_params["rabi_freq"][q], f"X_{q}", [q]) + #) + drive_hamiltonian_dict[f"D-{ind}"].append( + (2 * np.pi * model_params["rabi_freq"][q], f"X_{ind}", [ind]) ) # flux Hamiltonian terms (amplitude determined by processed pulse sequence) - flux_hamiltonian_dict.update({f"F-{qubits_list[i]}": []}) - flux_hamiltonian_dict[f"F-{qubits_list[i]}"].append((2 * np.pi, f"O_{q}", [q])) + #flux_hamiltonian_dict.update({f"F-{qubits_list[i]}": []}) + flux_hamiltonian_dict.update({f"F-{ind}": []}) + #flux_hamiltonian_dict[f"F-{qubits_list[i]}"].append((2 * np.pi, f"O_{q}", [q])) + flux_hamiltonian_dict[f"F-{ind}"].append((2 * np.pi, f"O_{ind}", [ind])) # flux detuning parameters: try: flux_params_dict |= { - q: { + #q: { + ind: { "flux_quanta": model_params["flux_quanta"][q], "max_frequency": model_params["max_lo_freq"][q], "current_frequency": model_params["drive_freq"][q], @@ -224,18 +249,25 @@ def generate_model_config( # dissipation terms (one qubit, constant in time) t1 = model_params["T1"][q] - g1 = 0 if t1 == 0 else 1.0 / t1 * 2 * np.pi + g1 = 0 if t1 == 0 else 1.0 / t1 #* 2 * np.pi t2 = model_params["T2"][q] - g2 = 0 if t1 == 0 else 1.0 / t2 * 2 * np.pi + g2 = 0 if t1 == 0 else 1.0 / t2 #* 2 * np.pi - dissipation_dict["t1"].append((np.sqrt(g1 / 2), f"sp01_{q}", [q])) - dissipation_dict["t2"].append((np.sqrt(g2 / 2), f"Z01_{q}", [q])) + #dissipation_dict["t1"].append((np.sqrt(g1 / 2), f"sp01_{q}", [q])) + #dissipation_dict["t2"].append((np.sqrt(g2 / 2), f"Z01_{q}", [q])) + #dissipation_dict["t1"].append((np.sqrt(g1 / 2), f"sp01_{ind}", [ind])) + #dissipation_dict["t2"].append((np.sqrt(g2 / 2), f"Z01_{ind}", [ind])) + dissipation_dict["t1"].append((np.sqrt(g1), f"sp01_{ind}", [ind])) + dissipation_dict["t2"].append((np.sqrt(g2), f"Z01_{ind}", [ind])) # two-body terms (couplings) - for key in list(model_params["coupling_strength"].keys()): + for key in list(model_params["coupling_strength"].keys()): # consistent with device_qubits_list ind2, ind1 = key.split( "_" ) # ind2 > ind1 with ind_qubit > ind_coupler as per Hilbert space ordering + ind1 = platform_to_simulator_qubits[ind1] + ind2 = platform_to_simulator_qubits[ind2] + coupling = model_params["coupling_strength"][key] drift_hamiltonian_dict["two_body"].append( ( @@ -248,6 +280,8 @@ def generate_model_config( model_config = { "model_name": model_name, "topology": topology, + "device_qubits_list": device_qubits_list, + "device_couplers_list": [], "qubits_list": qubits_list, "nlevels_q": nlevels_q, "couplers_list": [], @@ -259,9 +293,10 @@ def generate_model_config( "dissipation": dissipation_dict, "method": "master_equation", "readout_error": readout_error, + "platform_to_simulator_qubits": platform_to_simulator_qubits, # "platform_to_simulator_channels": default_noflux_platform_to_simulator_channels( "platform_to_simulator_channels": default_platform_to_simulator_channels( - qubits_list, couplers_list=[] + device_qubits_list, couplers_list=[] ), } diff --git a/src/qibolab/instruments/emulator/models/methods.py b/src/qibolab/instruments/emulator/models/methods.py index 526b27b27..e72d0b5c1 100644 --- a/src/qibolab/instruments/emulator/models/methods.py +++ b/src/qibolab/instruments/emulator/models/methods.py @@ -4,6 +4,25 @@ import numpy as np +def default_platform_to_simulator_qubits( + qubits_list: list, couplers_list: list +) -> dict: + """Returns the default dictionary that maps platform qubit names to simulator qubit names. + Args: + qubits_list (list): List of qubit names to be included in the simulation. + couplers_list (list): List of coupler names to be included in the simulation. + + Returns: + dict: Mapping between platform qubit/coupler names to simulator qubit/coupler names. + """ + + return reduce( + operator.or_, + [{f"{q}": f"{i}"} for i,q in enumerate(qubits_list)] + + [{f"{c}": f"c{j}"} for j,c in enumerate(couplers_list)], + ) + + def default_noflux_platform_to_simulator_channels( qubits_list: list, couplers_list: list ) -> dict: @@ -13,12 +32,14 @@ def default_noflux_platform_to_simulator_channels( couplers_list (list): List of coupler names to be included in the simulation. Returns: - dict: Mapping between platform channel names to simulator chanel names. + dict: Mapping between platform channel names to simulator channel names. """ return reduce( operator.or_, - [{f"drive-{q}": f"D-{q}", f"readout-{q}": f"R-{q}"} for q in qubits_list] - + [{f"drive-{c}": f"D-{c}"} for c in couplers_list], + #[{f"drive-{q}": f"D-{q}", f"readout-{q}": f"R-{q}"} for q in qubits_list] + #+ [{f"drive-{c}": f"D-{c}"} for c in couplers_list], + [{f"drive-{q}": f"D-{i}", f"readout-{q}": f"R-{i}"} for i,q in enumerate(qubits_list)] + + [{f"drive-{c}": f"D-c{j}"} for j,c in enumerate(couplers_list)], ) @@ -36,10 +57,13 @@ def default_platform_to_simulator_channels( return reduce( operator.or_, [ - {f"drive-{q}": f"D-{q}", f"readout-{q}": f"R-{q}", f"flux-{q}": f"F-{q}"} - for q in qubits_list + #{f"drive-{q}": f"D-{q}", f"readout-{q}": f"R-{q}", f"flux-{q}": f"F-{q}"} + #for q in qubits_list + {f"drive-{q}": f"D-{i}", f"readout-{q}": f"R-{i}", f"flux-{q}": f"F-{i}"} + for i,q in enumerate(qubits_list) ] - + [{f"drive-{c}": f"D-{c}", f"flux-{c}": f"F-{c}"} for c in couplers_list], + #+ [{f"drive-{c}": f"D-{c}", f"flux-{c}": f"F-{c}"} for c in couplers_list], + + [{f"drive-{c}": f"D-c{j}", f"flux-{c}": f"F-c{j}"} for j,c in enumerate(couplers_list)], ) diff --git a/src/qibolab/instruments/emulator/pulse_simulator.py b/src/qibolab/instruments/emulator/pulse_simulator.py index 0ff6c99ed..e049e7be2 100644 --- a/src/qibolab/instruments/emulator/pulse_simulator.py +++ b/src/qibolab/instruments/emulator/pulse_simulator.py @@ -82,7 +82,6 @@ def setup(self, **kwargs): model_name = model_params["model_name"] model_config = MODELS[model_name].generate_model_config(model_params) - print(model_config) self.flux_params_dict = model_config["flux_params"] simulation_engine_name = simulation_config["simulation_engine_name"] @@ -97,6 +96,11 @@ def setup(self, **kwargs): "runcard_duration_in_dt_units" ] self.instant_measurement = simulation_config["instant_measurement"] + self.platform_to_simulator_qubits = model_config[ + "platform_to_simulator_qubits" + ] + self.simulator_to_platform_qubits = dict((v,int(k)) for k,v in self.platform_to_simulator_qubits.items()) + self.platform_to_simulator_channels = model_config[ "platform_to_simulator_channels" ] @@ -104,6 +108,8 @@ def setup(self, **kwargs): self.readout_error = { int(k): v for k, v in model_config["readout_error"].items() } + #self.readout_error = model_config["readout_error"] + self.simulate_dissipation = simulation_config["simulate_dissipation"] self.output_state_history = simulation_config["output_state_history"] @@ -186,6 +192,7 @@ def play( dict: A dictionary mapping the readout pulses serial and respective qubits to Qibolab results object, as well as simulation-related time and states data. """ + print(sequence) nshots = execution_parameters.nshots ro_pulse_list = sequence.ro_pulses times_dict, output_states, ro_reduced_dm, rdm_qubit_list = ( @@ -199,6 +206,7 @@ def play( ro_reduced_dm, rdm_qubit_list, self.simulation_engine.qid_nlevels_map, + self.simulator_to_platform_qubits, ) # apply default readout noise if self.readout_error is not None: @@ -394,6 +402,7 @@ def _sweep_play( ro_pulse_list = sequence.ro_pulses # run pulse simulation + print(sequence) times_dict, state_history, ro_reduced_dm, rdm_qubit_list = ( self.run_pulse_simulation(sequence, self.instant_measurement) ) @@ -403,6 +412,7 @@ def _sweep_play( ro_reduced_dm, rdm_qubit_list, self.simulation_engine.qid_nlevels_map, + self.simulator_to_platform_qubits, ) # apply default readout noise if self.readout_error is not None: @@ -497,17 +507,20 @@ def channel_translator(platform_channel_name, frequency): ) times_list.append(t) + ''' if pulse.type.value == "qd": platform_channel_name = f"drive-{qubit}" elif pulse.type.value == "ro": platform_channel_name = f"readout-{qubit}" elif pulse.type.value == "qf": platform_channel_name = f"flux-{qubit}" + ''' signals_list.append(pulse_signal) emulator_channel_name_list.append( - channel_translator(platform_channel_name, pulse._if) + #channel_translator(platform_channel_name, pulse._if) + channel_translator(channel, pulse._if) ) for coupler in sequence_couplers.qubits: @@ -523,15 +536,18 @@ def channel_translator(platform_channel_name, frequency): times_list.append(t) signals_list.append(pulse_signal) + ''' if pulse.type.value == "cf": platform_channel_name = f"flux-c{coupler}" elif ( pulse.type.value == "cd" ): # when drive pulse for couplers are available platform_channel_name = f"drive-{coupler}" + ''' emulator_channel_name_list.append( - channel_translator(platform_channel_name, pulse._if) + #channel_translator(platform_channel_name, pulse._if) + channel_translator(channel_name, pulse._if) ) tmin, tmax = [], [] @@ -556,16 +572,19 @@ def channel_translator(platform_channel_name, frequency): ) times_list.append(t) signals_list.append(pulse_signal) - + + ''' if pulse.type.value == "qd": platform_channel_name = f"drive-{qubit}" elif pulse.type.value == "ro": platform_channel_name = f"readout-{qubit}" elif pulse.type.value == "qf": platform_channel_name = f"flux-{qubit}" + ''' emulator_channel_name_list.append( - channel_translator(platform_channel_name, pulse._if) + #channel_translator(platform_channel_name, pulse._if) + channel_translator(channel, pulse._if) ) for coupler in sequence_couplers.qubits: @@ -581,15 +600,18 @@ def channel_translator(platform_channel_name, frequency): times_list.append(t) signals_list.append(pulse_signal) + ''' if pulse.type.value == "cf": platform_channel_name = f"flux-c{coupler}" elif ( pulse.type.value == "cd" ): # when drive pulse for couplers are available platform_channel_name = f"drive-c{coupler}" + ''' emulator_channel_name_list.append( - channel_translator(platform_channel_name, pulse._if) + #channel_translator(platform_channel_name, pulse._if) + channel_translator(channel, pulse._if) ) tmin, tmax = [], [] @@ -821,6 +843,7 @@ def get_samples( ro_reduced_dm: np.ndarray, ro_qubit_list: list, qid_nlevels_map: dict[Union[int, str], int], + simulator_to_platform_qubits: dict=None, ) -> dict[Union[str, int], list]: """Gets samples from the density matrix corresponding to the system or subsystem specified by the ordered qubit indices. @@ -859,6 +882,8 @@ def get_samples( outcomes = [ reduced_computation_basis[outcome][ind] for outcome in sample_all_ro_list ] + if simulator_to_platform_qubits: + ro_qubit = simulator_to_platform_qubits[str(ro_qubit)] samples[ro_qubit] = outcomes return samples @@ -884,20 +909,25 @@ def truncate_ro_pulses( return sequence -def extract_platform_data(platform: Platform) -> dict: +def extract_platform_data(platform: Platform, target_qubits: list=None) -> dict: """Extracts platform data relevant for generating model parameters. Estimates rabi frequency from drive pulse for each qubit if not provided in qubit characterization. Args: platform (`qibolab.platform.Platform`): Initialized device platform. - + target_qubits (list): List of qubit names to extract. Returns: dict: Selected device platform data. """ - qubits = platform.qubits - couplers = platform.couplers - ordered_pairs = platform.ordered_pairs + if target_qubits: + qubits = {qubit_name: platform.qubits[qubit_name] for qubit_name in target_qubits} + couplers = platform.couplers + ordered_pairs = [pair for pair in platform.ordered_pairs if (pair[0] in target_qubits and pair[1] in target_qubits)] + else: + qubits = platform.qubits + couplers = platform.couplers + ordered_pairs = platform.ordered_pairs qubits_list = list(qubits.keys()) couplers_list = list(couplers.keys()) @@ -955,6 +985,8 @@ def est_rabi(rx_pulse: DrivePulse, sampling_rate: int = 100) -> float: def make_emulator_runcard( platform: Platform, nlevels_q: Union[int, List[int]] = 3, + target_qubits: list=None, + relabel_qubits: bool=False, model_name: str = "general_no_coupler_model", output_folder: Optional[str] = None, ) -> dict: @@ -962,26 +994,34 @@ def make_emulator_runcard( add flux-pulse and coupler related parts. Args: + platform (`qibolab.platform.Platform`): Initialized device platform. nlevels_q(int, list): Number of levels for each qubit. If int, the same value gets assigned to all qubits. + target_qubits (list): List of qubit names to emulate. + relabel_qubits (bool): if true, relabels qubit names from 0 to nqubits-1. model_name (str): Name of model to use for emulation. Defaults to 'general_no_coupler_model'. output_folder (str): Directory to output the generated emulator runcard 'parameters.json'. Defaults to None, in which case only the runcard dictionary is returned. Returns: dict: Emulator runcard """ + settings_dict = { "nshots": platform.settings.nshots, "relaxation_time": platform.settings.relaxation_time, } - qubits_list = list(platform.qubits.keys()) - couplers_list = list(platform.couplers.keys()) - - platform_data_dict = extract_platform_data(platform) + platform_data_dict = extract_platform_data(platform, target_qubits) model_params_dict = MODELS[model_name].get_model_params( - platform_data_dict, nlevels_q + platform_data_dict, nlevels_q, relabel_qubits ) + if target_qubits: + qubits_list = platform_data_dict["qubits_list"] + couplers_list = platform_data_dict["couplers_list"] + else: + qubits_list = list(platform.qubits.keys()) + couplers_list = list(platform.couplers.keys()) + characterization = {} single_qubit_characterization = {} # two_qubit_characterization = {} @@ -992,9 +1032,16 @@ def make_emulator_runcard( # two_qubit_native_gates = {} # coupler_native_gates = {} - for q in qubits_list: - single_qubit_characterization |= {q: platform.qubits[q].characterization} - single_qubit_native_gates |= {q: platform.qubits[q].native_gates.raw} + emulator_qubits_list = [] + for i,q in enumerate(qubits_list): + if relabel_qubits: + i = str(i) + else: + i = q + emulator_qubits_list.append(int(i)) + + single_qubit_characterization |= {i: platform.qubits[q].characterization} + single_qubit_native_gates |= {i: platform.qubits[q].native_gates.raw} characterization |= {"single_qubit": single_qubit_characterization} # characterization |= {'two_qubit': two_qubit_characterization} @@ -1019,10 +1066,13 @@ def make_emulator_runcard( runcard |= {"device_name": platform.name + "_emulator"} runcard |= {"nqubits": len(qubits_list)} runcard |= {"ncouplers": 0} # runcard |= {'ncouplers': len(couplers_list)} - runcard |= {"description": f"Emulator for {platform.name} using {model_name}"} + if relabel_qubits: + runcard |= {"description": f"Emulator for {platform.name} qubits {qubits_list} using {model_name}"} + else: + runcard |= {"description": f"Emulator for {platform.name} using {model_name}"} runcard |= {"settings": settings_dict} runcard |= {"instruments": instruments} - runcard |= {"qubits": qubits_list} + runcard |= {"qubits": emulator_qubits_list} runcard |= {"couplers": []} # runcard |= {'couplers': couplers_list} runcard |= {"topology": []} # runcard |= {'topology': platform.ordered_pairs} runcard |= {"native_gates": native_gates}