diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02a_Resonator_Spectroscopy.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02a_Resonator_Spectroscopy.py index 75c58dfe1..a2215fbe3 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02a_Resonator_Spectroscopy.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02a_Resonator_Spectroscopy.py @@ -46,7 +46,7 @@ class Parameters(NodeParameters): simulation_duration_ns: int = 2500 timeout: int = 100 load_data_id: Optional[int] = None - multiplexed: bool = False + multiplexed: bool = True node = QualibrationNode(name="02a_Resonator_Spectroscopy", parameters=Parameters()) @@ -103,8 +103,8 @@ class Parameters(NodeParameters): # save data save(I[i], I_st[i]) save(Q[i], Q_st[i]) - if not node.parameters.multiplexed: - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -143,27 +143,28 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) if node.parameters.load_data_id is not None: - ds = load_dataset(node.parameters.load_data_id) + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) else: ds = fetch_results_as_xarray(job.result_handles, qubits, {"freq": dfs}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits) - # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) - ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) - # Derive the phase IQ_abs = angle(I + j*Q) - ds = ds.assign({"phase": subtract_slope(apply_angle(ds.I + 1j * ds.Q, dim="freq"), dim="freq")}) - # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting - ds = ds.assign_coords( - { - "freq_full": ( - ["qubit", "freq"], - np.array([dfs + q.resonator.RF_frequency for q in qubits]), - ) - } - ) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits) + # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) + ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) + # Derive the phase IQ_abs = angle(I + j*Q) + ds = ds.assign({"phase": subtract_slope(apply_angle(ds.I + 1j * ds.Q, dim="freq"), dim="freq")}) + # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting + ds = ds.assign_coords( + { + "freq_full": ( + ["qubit", "freq"], + np.array([dfs + q.resonator.RF_frequency for q in qubits]), + ) + } + ) # Add the dataset to the node node.results = {"ds": ds} @@ -232,10 +233,12 @@ class Parameters(NodeParameters): for index, q in enumerate(qubits): q.resonator.intermediate_frequency += int(fits[q.name].params["omega_r"].value) - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() - print("Results saved") + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() + print("Results saved") + +# %% diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02b_resonator_spectroscopy_vs_flux.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02b_resonator_spectroscopy_vs_flux.py index 2f0d08675..33318fa88 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02b_resonator_spectroscopy_vs_flux.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02b_resonator_spectroscopy_vs_flux.py @@ -24,7 +24,7 @@ from quam_libs.macros import qua_declaration from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.lib.fit import fit_oscillation from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -55,7 +55,7 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 - + load_data_id: Optional[int] = None node = QualibrationNode(name="02b_Resonator_Spectroscopy_vs_Flux", parameters=Parameters()) @@ -81,7 +81,9 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() + # %% {QUA_program} @@ -111,13 +113,8 @@ class Parameters(NodeParameters): # resonator of the qubit rr = resonators[i] # Bring the active qubits to the minimum frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.align() with for_(n, 0, n < n_avg, n + 1): save(n, n_st) @@ -163,7 +160,7 @@ class Parameters(NodeParameters): node.results = {"figure": plt.gcf()} node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(multi_res_spec_vs_flux) results = fetching_tool(job, ["n"], mode="live") @@ -173,29 +170,33 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"freq": dfs, "flux": dcs}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits) - # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) - ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) - # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting - RF_freq = np.array([dfs + q.resonator.RF_frequency for q in qubits]) - ds = ds.assign_coords({"freq_full": (["qubit", "freq"], RF_freq)}) - ds.freq_full.attrs["long_name"] = "Frequency" - ds.freq_full.attrs["units"] = "GHz" - # Add the current axis of each qubit to the dataset coordinates for plotting - current = np.array([ds.flux.values / node.parameters.input_line_impedance_in_ohm for q in qubits]) - ds = ds.assign_coords({"current": (["qubit", "flux"], current)}) - ds.current.attrs["long_name"] = "Current" - ds.current.attrs["units"] = "A" - # Add attenuated current to dataset - attenuation_factor = 10 ** (-node.parameters.line_attenuation_in_db / 20) - attenuated_current = ds.current * attenuation_factor - ds = ds.assign_coords({"attenuated_current": (["qubit", "flux"], attenuated_current.values)}) - ds.attenuated_current.attrs["long_name"] = "Attenuated Current" - ds.attenuated_current.attrs["units"] = "A" + if node.parameters.load_data_id is not None: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) + else: + ds = fetch_results_as_xarray(job.result_handles, qubits, {"freq": dfs, "flux": dcs}) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits) + # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) + ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) + # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting + RF_freq = np.array([dfs + q.resonator.RF_frequency for q in qubits]) + ds = ds.assign_coords({"freq_full": (["qubit", "freq"], RF_freq)}) + ds.freq_full.attrs["long_name"] = "Frequency" + ds.freq_full.attrs["units"] = "GHz" + # Add the current axis of each qubit to the dataset coordinates for plotting + current = np.array([ds.flux.values / node.parameters.input_line_impedance_in_ohm for q in qubits]) + ds = ds.assign_coords({"current": (["qubit", "flux"], current)}) + ds.current.attrs["long_name"] = "Current" + ds.current.attrs["units"] = "A" + # Add attenuated current to dataset + attenuation_factor = 10 ** (-node.parameters.line_attenuation_in_db / 20) + attenuated_current = ds.current * attenuation_factor + ds = ds.assign_coords({"attenuated_current": (["qubit", "flux"], attenuated_current.values)}) + ds.attenuated_current.attrs["long_name"] = "Attenuated Current" + ds.attenuated_current.attrs["units"] = "A" # Add the dataset to the node node.results = {"ds": ds} @@ -214,15 +215,16 @@ class Parameters(NodeParameters): flux_min = flux_min * (np.abs(flux_min) < 0.5) + 0.5 * (flux_min > 0.5) - 0.5 * (flux_min < -0.5) # finding the frequency as the sweet spot flux rel_freq_shift = peak_freq.sel(flux=idle_offset, method="nearest") - + abs_freqs = ds.sel(flux=idle_offset, method="nearest").sel(freq = rel_freq_shift).freq_full # Save fitting results fit_results = {} for q in qubits: fit_results[q.name] = {} - fit_results[q.name]["resonator_frequency"] = rel_freq_shift.sel(qubit=q.name).values + q.resonator.RF_frequency + fit_results[q.name]["resonator_frequency"] = abs_freqs.sel(qubit=q.name).values fit_results[q.name]["min_offset"] = float(flux_min.sel(qubit=q.name).data) fit_results[q.name]["offset"] = float(idle_offset.sel(qubit=q.name).data) fit_results[q.name]["dv_phi0"] = 1 / fit_osc.sel(fit_vals="f", qubit=q.name).values + attenuation_factor = 10 ** (-node.parameters.line_attenuation_in_db / 20) fit_results[q.name]["m_pH"] = ( 1e12 * 2.068e-15 @@ -277,7 +279,7 @@ class Parameters(NodeParameters): # Location of the current resonator frequency ax.plot( idle_offset.loc[qubit].values, - (machine.qubits[qubit["qubit"]].resonator.RF_frequency + rel_freq_shift.sel(qubit=qubit["qubit"]).values) + (abs_freqs.sel(qubit=qubit["qubit"]).values) * 1e-9, "r*", markersize=10, @@ -291,25 +293,29 @@ class Parameters(NodeParameters): node.results["figure"] = grid.fig # %% {Update_state} - with node.record_state_updates(): - for q in qubits: - if not (np.isnan(float(idle_offset.sel(qubit=q.name).data))): - if flux_point == "independent": - q.z.independent_offset = float(idle_offset.sel(qubit=q.name).data) - else: - q.z.joint_offset = float(idle_offset.sel(qubit=q.name).data) - - if update_flux_min: - q.z.min_offset = float(flux_min.sel(qubit=q.name).data) - q.resonator.intermediate_frequency += float(rel_freq_shift.sel(qubit=q.name).data) - q.phi0_voltage = fit_results[q.name]["dv_phi0"] - q.phi0_current = ( - fit_results[q.name]["dv_phi0"] * node.parameters.input_line_impedance_in_ohm * attenuation_factor - ) - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() - + if not node.parameters.load_data_id: + with node.record_state_updates(): + for q in qubits: + if not (np.isnan(float(idle_offset.sel(qubit=q.name).data))): + if flux_point == "independent": + q.z.independent_offset = float(idle_offset.sel(qubit=q.name).data) + else: + q.z.joint_offset = float(idle_offset.sel(qubit=q.name).data) + + if update_flux_min: + q.z.min_offset = float(flux_min.sel(qubit=q.name).data) + q.resonator.intermediate_frequency += float(rel_freq_shift.sel(qubit=q.name).data) + q.phi0_voltage = fit_results[q.name]["dv_phi0"] + q.phi0_current = ( + fit_results[q.name]["dv_phi0"] * node.parameters.input_line_impedance_in_ohm * attenuation_factor + ) + + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() + + + +# %% diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02c_resonator_spectroscopy_vs_amplitude.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02c_resonator_spectroscopy_vs_amplitude.py index 727f846fe..d53584504 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02c_resonator_spectroscopy_vs_amplitude.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/02c_resonator_spectroscopy_vs_amplitude.py @@ -28,7 +28,7 @@ from quam_libs.macros import qua_declaration from quam_libs.lib.qua_datasets import convert_IQ_to_V, subtract_slope, apply_angle from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.trackable_object import tracked_updates from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -51,7 +51,7 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 - max_power_dbm: int = -30 + max_power_dbm: int = -20 min_power_dbm: int = -50 num_power_points: int = 100 max_amp: float = 0.1 @@ -61,7 +61,7 @@ class Parameters(NodeParameters): derivative_smoothing_window_num_points: int = 20 moving_average_filter_window_num_points: int = 15 multiplexed: bool = False - + load_data_id: Optional[int] = None node = QualibrationNode(name="02c_Resonator_Spectroscopy_vs_Amplitude", parameters=Parameters()) @@ -93,7 +93,10 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() + + # %% {QUA_program} n_avg = node.parameters.num_averages # The number of averages @@ -123,14 +126,9 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): # Bring the active qubits to the desired frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() - + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.align() + # resonator of this qubit rr = qubit.resonator @@ -151,7 +149,7 @@ class Parameters(NodeParameters): save(I[i], I_st[i]) save(Q[i], Q_st[i]) if not node.parameters.multiplexed: - align(*[rr.name for rr in resonators]) + align() with stream_processing(): n_st.save("n") @@ -178,11 +176,9 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(multi_res_spec_vs_amp) - - # %% {Live_plot} results = fetching_tool(job, ["n"], mode="live") while results.is_processing(): # Fetch results @@ -190,29 +186,34 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - power_dbm = np.linspace( - node.parameters.min_power_dbm, - node.parameters.max_power_dbm, - node.parameters.num_power_points - ) - node.parameters.ro_line_attenuation_dB - ds = fetch_results_as_xarray(job.result_handles, qubits, {"power_dbm": power_dbm, "freq": dfs}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits) - # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) - ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) - ds = ds.assign({"phase": subtract_slope(apply_angle(ds.I + 1j * ds.Q, dim="freq"), dim="freq")}) - # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting - RF_freq = np.array([dfs + q.resonator.RF_frequency for q in qubits]) - ds = ds.assign_coords({"freq_full": (["qubit", "freq"], RF_freq)}) - ds.freq_full.attrs["long_name"] = "Frequency" - ds.freq_full.attrs["units"] = "GHz" - ds.power_dbm.attrs["long_name"] = "Power" - ds.power_dbm.attrs["units"] = "dBm" - - # Normalize the IQ_abs with respect to the amplitude axis - ds = ds.assign({"IQ_abs_norm": ds["IQ_abs"] / ds.IQ_abs.mean(dim=["freq"])}) + if node.parameters.load_data_id is not None: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) + else: + power_dbm = np.linspace( + node.parameters.min_power_dbm, + node.parameters.max_power_dbm, + node.parameters.num_power_points + ) - node.parameters.ro_line_attenuation_dB + ds = fetch_results_as_xarray(job.result_handles, qubits, {"power_dbm": power_dbm, "freq": dfs}) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits) + # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) + ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) + ds = ds.assign({"phase": subtract_slope(apply_angle(ds.I + 1j * ds.Q, dim="freq"), dim="freq")}) + # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting + RF_freq = np.array([dfs + q.resonator.RF_frequency for q in qubits]) + ds = ds.assign_coords({"freq_full": (["qubit", "freq"], RF_freq)}) + ds.freq_full.attrs["long_name"] = "Frequency" + ds.freq_full.attrs["units"] = "GHz" + ds.power_dbm.attrs["long_name"] = "Power" + ds.power_dbm.attrs["units"] = "dBm" + + # Normalize the IQ_abs with respect to the amplitude axis + ds = ds.assign({"IQ_abs_norm": ds["IQ_abs"] / ds.IQ_abs.mean(dim=["freq"])}) + # Add the dataset to the node node.results = {"ds": ds} @@ -299,21 +300,27 @@ class Parameters(NodeParameters): fit_results = {} for q in qubits: fit_results[q.name] = {} - with node.record_state_updates(): - if not np.isnan(rr_optimal_power_dbm[q.name]): - power_settings = q.resonator.set_output_power( - power_in_dbm=rr_optimal_power_dbm[q.name].item(), - max_amplitude=0.1 - ) - if not np.isnan(rr_optimal_frequencies[q.name]): - q.resonator.intermediate_frequency += rr_optimal_frequencies[q.name] + if node.parameters.load_id is None: + with node.record_state_updates(): + if not np.isnan(rr_optimal_power_dbm[q.name]): + power_settings = q.resonator.set_output_power( + power_in_dbm=rr_optimal_power_dbm[q.name].item(), + max_amplitude=0.1 + ) + if not np.isnan(rr_optimal_frequencies[q.name]): + q.resonator.intermediate_frequency += rr_optimal_frequencies[q.name] fit_results[q.name] = power_settings fit_results[q.name]["RO_frequency"] = q.resonator.RF_frequency node.results["fit_results"] = fit_results # %% {Save_results} + if node.parameters.load_data_id is not None: + if node.storage_manager is not None: + node.storage_manager.active_machine_path = None node.outcomes = {q.name: "successful" for q in qubits} node.results["initial_parameters"] = node.parameters.model_dump() node.machine = machine node.save() + +# %% diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/03a_Qubit_Spectroscopy.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/03a_Qubit_Spectroscopy.py index d0b960dd5..ff940b515 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/03a_Qubit_Spectroscopy.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/03a_Qubit_Spectroscopy.py @@ -30,7 +30,7 @@ from quam_libs.macros import qua_declaration from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.lib.fit import peaks_dips from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -53,13 +53,15 @@ class Parameters(NodeParameters): operation_len_in_ns: Optional[int] = None frequency_span_in_mhz: float = 50 frequency_step_in_mhz: float = 0.25 - flux_point_joint_or_independent_or_arbitrary: Literal["joint", "independent", "arbitrary"] = "independent" + flux_point_joint_or_independent: Literal["joint", "independent"] = "independent" target_peak_width: Optional[float] = 2e6 arbitrary_flux_bias: Optional[float] = None arbitrary_qubit_frequency_in_ghz: Optional[float] = None simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="03a_Qubit_Spectroscopy", parameters=Parameters()) @@ -73,8 +75,9 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() - +if node.parameters.load_data_id is None: + qmm = machine.connect() + # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": qubits = machine.active_qubits @@ -97,29 +100,19 @@ class Parameters(NodeParameters): span = node.parameters.frequency_span_in_mhz * u.MHz step = node.parameters.frequency_step_in_mhz * u.MHz dfs = np.arange(-span // 2, +span // 2, step, dtype=np.int32) -flux_point = node.parameters.flux_point_joint_or_independent_or_arbitrary +flux_point = node.parameters.flux_point_joint_or_independent qubit_freqs = {q.name: q.xy.RF_frequency for q in qubits} # for opx # Set the qubit frequency for a given flux point -if flux_point == "arbitrary": - if node.parameters.arbitrary_flux_bias is None and node.parameters.arbitrary_qubit_frequency_in_ghz is None: - raise ValueError( - "arbitrary_flux_bias or arbitrary_qubit_frequency_in_ghz must be provided when flux_point is 'arbitrary'" - ) - elif ( - node.parameters.arbitrary_flux_bias is not None and node.parameters.arbitrary_qubit_frequency_in_ghz is not None - ): - raise ValueError( - "provide either arbitrary_flux_bias or arbitrary_qubit_frequency_in_ghz, not both when flux_point is 'arbitrary'" - ) - elif node.parameters.arbitrary_flux_bias is not None: - arb_flux_bias_offset = {q.name: node.parameters.arbitrary_flux_bias for q in qubits} - detunings = {q.name: q.freq_vs_flux_01_quad_term * arb_flux_bias_offset[q.name] ** 2 for q in qubits} - else: - detunings = { - q.name: 1e9 * node.parameters.arbitrary_qubit_frequency_in_ghz - qubit_freqs[q.name] for q in qubits - } - arb_flux_bias_offset = {q.name: np.sqrt(detunings[q.name] / q.freq_vs_flux_01_quad_term) for q in qubits} +if node.parameters.arbitrary_flux_bias is not None: + arb_flux_bias_offset = {q.name: node.parameters.arbitrary_flux_bias for q in qubits} + detunings = {q.name: q.freq_vs_flux_01_quad_term * arb_flux_bias_offset[q.name] ** 2 for q in qubits} +elif node.parameters.arbitrary_qubit_frequency_in_ghz is not None: + detunings = { + q.name: 1e9 * node.parameters.arbitrary_qubit_frequency_in_ghz - qubit_freqs[q.name] for q in qubits + } + arb_flux_bias_offset = {q.name: np.sqrt(detunings[q.name] / q.freq_vs_flux_01_quad_term) for q in qubits} + else: arb_flux_bias_offset = {q.name: 0.0 for q in qubits} detunings = {q.name: 0.0 for q in qubits} @@ -137,39 +130,26 @@ class Parameters(NodeParameters): df = declare(int) # QUA variable for the qubit frequency for i, qubit in enumerate(qubits): - # Bring the active qubits to the minimum frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - dc_offset = qubit.z.independent_offset - elif flux_point == "joint" or "arbitrary": - machine.apply_all_flux_to_joint_idle() - dc_offset = qubit.z.joint_offset - else: - machine.apply_all_flux_to_zero() - dc_offset = 0.0 - + # Bring the active qubits to the desired frequency point + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.align() + with for_(n, 0, n < n_avg, n + 1): save(n, n_st) with for_(*from_array(df, dfs)): - # Bring the qubit to the desired point - qubit.z.set_dc_offset(dc_offset + arb_flux_bias_offset[qubit.name]) - qubit.z.settle() # Update the qubit frequency qubit.xy.update_frequency(df + qubit.xy.intermediate_frequency + detunings[qubit.name]) qubit.align() - + duration = operation_len * u.ns if operation_len is not None else qubit.xy.operations[operation].length // 4 + # Bring the qubit to the desired point during the satruration pulse + qubit.z.play("const", amplitude_scale=arb_flux_bias_offset[qubit.name] / qubit.z.operations["const"].amplitude, duration=duration) # Play the saturation pulse qubit.xy.play( operation, amplitude_scale=operation_amp, - duration=(operation_len * u.ns if operation_len is not None else None), + duration=duration, ) qubit.align() - # Bring back the flux for readout - qubit.z.set_dc_offset(dc_offset) - qubit.z.settle() - qubit.align() # readout the resonator qubit.resonator.measure("readout", qua_vars=(I[i], Q[i])) @@ -180,7 +160,8 @@ class Parameters(NodeParameters): save(Q[i], Q_st[i]) # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -207,11 +188,9 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(qubit_spec) - - # %% {Live_plot} results = fetching_tool(job, ["n"], mode="live") while results.is_processing(): # Fetch results @@ -219,25 +198,30 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"freq": dfs}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits) - # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) and phase - ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) - ds = ds.assign({"phase": np.arctan2(ds.Q, ds.I)}) - # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting - ds = ds.assign_coords( - { - "freq_full": ( - ["qubit", "freq"], - np.array([dfs + qubit_freqs[q.name] + detunings[q.name] for q in qubits]), - ) - } - ) - ds.freq_full.attrs["long_name"] = "Frequency" - ds.freq_full.attrs["units"] = "GHz" +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + + if node.parameters.load_data_id is not None: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) + else: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"freq": dfs}) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits) + # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) and phase + ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) + ds = ds.assign({"phase": np.arctan2(ds.Q, ds.I)}) + # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting + ds = ds.assign_coords( + { + "freq_full": ( + ["qubit", "freq"], + np.array([dfs + qubit_freqs[q.name] + detunings[q.name] for q in qubits]), + ) + } + ) + ds.freq_full.attrs["long_name"] = "Frequency" + ds.freq_full.attrs["units"] = "GHz" # Add the dataset to the node node.results = {"ds": ds} @@ -250,7 +234,7 @@ class Parameters(NodeParameters): ds.sel(freq=shifts).I - ds.I.mean(dim="freq"), ) # rotate the data to the new I axis - ds = ds.assign({"I_rot": np.real(ds.IQ_abs * np.exp(1j * (ds.phase - angle)))}) + ds = ds.assign({"I_rot" : ds.I * np.cos(angle) + ds.Q * np.sin(angle)}) # Find the peak with minimal prominence as defined, if no such peak found, returns nan result = peaks_dips(ds.I_rot, dim="freq", prominence_factor=5) # The resonant RF frequency of the qubits @@ -258,9 +242,9 @@ class Parameters(NodeParameters): [ ( q.name, - result.sel(qubit=q.name).position.values + qubit_freqs[q.name] + detunings[q.name], + ds.freq_full.sel(freq = result.position).sel(qubit=q.name).values, ) - for q in qubits + for q in qubits if not np.isnan(result.sel(qubit=q.name).position.values) ] ) @@ -304,15 +288,16 @@ class Parameters(NodeParameters): # Plot the line (ds.assign_coords(freq_GHz=ds.freq_full / 1e9).loc[qubit].I_rot * 1e3).plot(ax=ax, x="freq_GHz") # Identify the resonance peak - ax.plot( - abs_freqs[qubit["qubit"]] / 1e9, - ds.loc[qubit].sel(freq=result.loc[qubit].position.values, method="nearest").I_rot * 1e3, - ".r", - ) - # Identify the width - (approx_peak.assign_coords(freq_GHz=ds.freq_full / 1e9).loc[qubit] * 1e3).plot( - ax=ax, x="freq_GHz", linewidth=0.5, linestyle="--" - ) + if not np.isnan(result.sel(qubit=qubit["qubit"]).position.values): + ax.plot( + abs_freqs[qubit["qubit"]] / 1e9, + ds.loc[qubit].sel(freq=result.loc[qubit].position.values, method="nearest").I_rot * 1e3, + ".r", + ) + # # Identify the width + (approx_peak.assign_coords(freq_GHz=ds.freq_full / 1e9).loc[qubit] * 1e3).plot( + ax=ax, x="freq_GHz", linewidth=0.5, linestyle="--" + ) ax.set_xlabel("Qubit freq [GHz]") ax.set_ylabel("Trans. amp. [mV]") ax.set_title(qubit["qubit"]) @@ -322,42 +307,45 @@ class Parameters(NodeParameters): node.results["figure"] = grid.fig # %% {Update_state} - with (node.record_state_updates()): - for q in qubits: - if not np.isnan(result.sel(qubit=q.name).position.values): - if flux_point == "arbitrary": - q.arbitrary_intermediate_frequency = float( - result.sel(qubit=q.name).position.values + detunings[q.name] + q.xy.intermediate_frequency - ) - q.z.arbitrary_offset = arb_flux_bias_offset[q.name] - else: - q.xy.intermediate_frequency += float(result.sel(qubit=q.name).position.values) - if not flux_point == "arbitrary": - prev_angle = q.resonator.operations["readout"].integration_weights_angle - if not prev_angle: - prev_angle = 0.0 - q.resonator.operations["readout"].integration_weights_angle = ( - prev_angle + angle.sel(qubit=q.name).values - ) % (2 * np.pi) - Pi_length = q.xy.operations["x180"].length - used_amp = q.xy.operations["saturation"].amplitude * operation_amp - factor_cw = float(target_peak_width / result.sel(qubit=q.name).width.values) - factor_pi = np.pi / (result.sel(qubit=q.name).width.values * Pi_length * 1e-9) - limits = instrument_limits(q.xy) - if factor_cw * used_amp / operation_amp < limits.max_wf_amplitude: - q.xy.operations["saturation"].amplitude = factor_cw * used_amp / operation_amp + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for q in qubits: + if not np.isnan(result.sel(qubit=q.name).position.values): + if flux_point == "arbitrary": + q.arbitrary_intermediate_frequency = float( + result.sel(qubit=q.name).position.values + detunings[q.name] + q.xy.intermediate_frequency + ) + q.z.arbitrary_offset = arb_flux_bias_offset[q.name] else: - q.xy.operations["saturation"].amplitude = limits.max_wf_amplitude - - if factor_pi * used_amp < limits.max_x180_wf_amplitude: - q.xy.operations["x180"].amplitude = factor_pi * used_amp - elif factor_pi * used_amp >= limits.max_x180_wf_amplitude: - q.xy.operations["x180"].amplitude = limits.max_x180_wf_amplitude - node.results["ds"] = ds - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() - + q.xy.intermediate_frequency += float(result.sel(qubit=q.name).position.values) + if not flux_point == "arbitrary": + prev_angle = q.resonator.operations["readout"].integration_weights_angle + if not prev_angle: + prev_angle = 0.0 + q.resonator.operations["readout"].integration_weights_angle = ( + prev_angle + angle.sel(qubit=q.name).values + ) % (2 * np.pi) + Pi_length = q.xy.operations["x180"].length + used_amp = q.xy.operations["saturation"].amplitude * operation_amp + factor_cw = float(target_peak_width / result.sel(qubit=q.name).width.values) + factor_pi = np.pi / (result.sel(qubit=q.name).width.values * Pi_length * 1e-9) + limits = instrument_limits(q.xy) + if factor_cw * used_amp / operation_amp < limits.max_wf_amplitude: + q.xy.operations["saturation"].amplitude = factor_cw * used_amp / operation_amp + else: + q.xy.operations["saturation"].amplitude = limits.max_wf_amplitude + + if factor_pi * used_amp < limits.max_x180_wf_amplitude: + q.xy.operations["x180"].amplitude = factor_pi * used_amp + elif factor_pi * used_amp >= limits.max_x180_wf_amplitude: + q.xy.operations["x180"].amplitude = limits.max_x180_wf_amplitude + node.results["ds"] = ds + + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() + + +# %% diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/03b_qubit_spectroscopy_vs_flux.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/03b_qubit_spectroscopy_vs_flux.py index 361a987c2..dffcd8574 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/03b_qubit_spectroscopy_vs_flux.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/03b_qubit_spectroscopy_vs_flux.py @@ -22,7 +22,7 @@ from quam_libs.macros import qua_declaration from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.lib.fit import peaks_dips from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -43,15 +43,17 @@ class Parameters(NodeParameters): operation: str = "saturation" operation_amplitude_factor: Optional[float] = 0.1 operation_len_in_ns: Optional[int] = None - frequency_span_in_mhz: float = 20 - frequency_step_in_mhz: float = 0.1 + frequency_span_in_mhz: float = 30 + frequency_step_in_mhz: float = 0.25 min_flux_offset_in_v: float = -0.01 max_flux_offset_in_v: float = 0.01 - num_flux_points: int = 51 + num_flux_points: int = 21 flux_point_joint_or_independent: Literal["joint", "independent"] = "independent" simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="03b_Qubit_Spectroscopy_vs_Flux", parameters=Parameters()) @@ -65,7 +67,8 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": @@ -105,13 +108,9 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): # Bring the active qubits to the minimum frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() with for_(n, 0, n < n_avg, n + 1): save(n, n_st) @@ -119,34 +118,19 @@ class Parameters(NodeParameters): with for_(*from_array(df, dfs)): # Update the qubit frequency qubit.xy.update_frequency(df + qubit.xy.intermediate_frequency) - with for_(*from_array(dc, dcs)): # Flux sweeping for a qubit - if flux_point == "independent": - qubit.z.set_dc_offset(dc + qubit.z.independent_offset) - elif flux_point == "joint": - qubit.z.set_dc_offset(dc + qubit.z.joint_offset) - else: - raise RuntimeError(f"unknown flux_point") - qubit.z.settle() qubit.align() - + duration = operation_len * u.ns if operation_len is not None else qubit.xy.operations[operation].length // 4 + # Bring the qubit to the desired point during the satruration pulse + qubit.z.play("const", amplitude_scale=dc / qubit.z.operations["const"].amplitude, duration=duration) # Apply saturation pulse to all qubits qubit.xy.play( operation, amplitude_scale=operation_amp, - duration=(operation_len * u.ns if operation_len is not None else None), + duration=duration, ) qubit.align() - # Bring back the flux for readout - if flux_point == "independent": - qubit.z.set_dc_offset(qubit.z.independent_offset) - elif flux_point == "joint": - qubit.z.set_dc_offset(qubit.z.joint_offset) - else: - raise RuntimeError(f"unknown flux_point") - qubit.z.settle() - qubit.align() # QUA macro to read the state of the active resonators qubit.resonator.measure("readout", qua_vars=(I[i], Q[i])) # save data @@ -156,7 +140,8 @@ class Parameters(NodeParameters): qubit.resonator.wait(machine.depletion_time * u.ns) # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -183,7 +168,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(multi_qubit_spec_vs_flux) results = fetching_tool(job, ["n"], mode="live") @@ -193,30 +178,34 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"flux": dcs, "freq": dfs}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits) - # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) - ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) - # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting - ds = ds.assign_coords( - { - "freq_full": ( - ["qubit", "freq"], - np.array([dfs + q.xy.RF_frequency for q in qubits]), - ) - } - ) - ds.freq_full.attrs["long_name"] = "Frequency" - ds.freq_full.attrs["units"] = "GHz" +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is not None: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) + else: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"flux": dcs, "freq": dfs}) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits) + # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) + ds = ds.assign({"IQ_abs": np.sqrt(ds["I"] ** 2 + ds["Q"] ** 2)}) + # Add the resonator RF frequency axis of each qubit to the dataset coordinates for plotting + ds = ds.assign_coords( + { + "freq_full": ( + ["qubit", "freq"], + np.array([dfs + q.xy.RF_frequency for q in qubits]), + ) + } + ) + ds.freq_full.attrs["long_name"] = "Frequency" + ds.freq_full.attrs["units"] = "GHz" # Add the dataset to the node node.results = {"ds": ds} # %% {Data_analysis} # Find the resonance dips for each flux point - peaks = peaks_dips(ds.I, dim="freq", prominence_factor=6) + peaks = peaks_dips(ds.I, dim="freq", prominence_factor=5) # Fit the result with a parabola parabolic_fit_results = peaks.position.polyfit("flux", 2) # Try to fit again with a smaller prominence factor (may need some adjustment) @@ -280,22 +269,23 @@ class Parameters(NodeParameters): node.results["figure"] = grid.fig # %% {Update_state} - with node.record_state_updates(): - for q in qubits: - if not np.isnan(flux_shift.sel(qubit=q.name).values): - if flux_point == "independent": - q.z.independent_offset += fit_results[q.name]["flux_shift"] - elif flux_point == "joint": - q.z.joint_offset += fit_results[q.name]["flux_shift"] - q.xy.intermediate_frequency += fit_results[q.name]["drive_freq"] - q.freq_vs_flux_01_quad_term = fit_results[q.name]["quad_term"] - - ds = ds.drop_vars("freq_full") - node.results["ds"] = ds - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for q in qubits: + if not np.isnan(flux_shift.sel(qubit=q.name).values): + if flux_point == "independent": + q.z.independent_offset += fit_results[q.name]["flux_shift"] + elif flux_point == "joint": + q.z.joint_offset += fit_results[q.name]["flux_shift"] + q.xy.intermediate_frequency += fit_results[q.name]["drive_freq"] + q.freq_vs_flux_01_quad_term = fit_results[q.name]["quad_term"] + + # %% {Save_results} + + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() + +# %% diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/04_Power_Rabi.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/04_Power_Rabi.py index 4faf3c695..ae8cd1ef6 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/04_Power_Rabi.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/04_Power_Rabi.py @@ -23,7 +23,7 @@ from quam_libs.macros import qua_declaration from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.lib.fit import fit_oscillation, oscillation from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -40,17 +40,18 @@ class Parameters(NodeParameters): qubits: Optional[List[str]] = None - num_averages: int = 200 + num_averages: int = 100 operation: str = "x180" min_amp_factor: float = 0.001 max_amp_factor: float = 2.0 - amp_factor_step: float = 0.005 + amp_factor_step: float = 0.01 max_number_rabi_pulses_per_sweep: int = 1 flux_point_joint_or_independent: Literal["joint", "independent"] = "independent" simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 - + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="04_Power_Rabi", parameters=Parameters()) @@ -63,7 +64,8 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": @@ -95,27 +97,26 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): # Bring the active qubits to the minimum frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() with for_(n, 0, n < n_avg, n + 1): save(n, n_st) with for_(*from_array(npi, N_pi_vec)): with for_(*from_array(a, amps)): + qubit.resonator.wait(5*qubit.thermalization_time * u.ns) # Loop for error amplification (perform many qubit pulses) + qubit.align() with for_(count, 0, count < npi, count + 1): qubit.xy.play(operation, amplitude_scale=a) - align() + qubit.align() qubit.resonator.measure("readout", qua_vars=(I[i], Q[i])) save(I[i], I_st[i]) save(Q[i], Q_st[i]) - qubit.resonator.wait(qubit.thermalization_time * u.ns) - align() + + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -142,7 +143,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(power_rabi) results = fetching_tool(job, ["n"], mode="live") @@ -152,20 +153,24 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"amp": amps, "N": N_pi_vec}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits) - # Add the qubit pulse absolute amplitude to the dataset - ds = ds.assign_coords( +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"amp": amps, "N": N_pi_vec}) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits) + # Add the qubit pulse absolute amplitude to the dataset + ds = ds.assign_coords( { "abs_amp": ( ["qubit", "amp"], np.array([q.xy.operations[operation].amplitude * amps for q in qubits]), ) - } - ) + } + ) + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} @@ -242,15 +247,16 @@ class Parameters(NodeParameters): node.results["figure"] = grid.fig # %% {Update_state} - with node.record_state_updates(): - for q in qubits: - q.xy.operations[operation].amplitude = fit_results[q.name]["Pi_amplitude"] - if operation == "x180": - q.xy.operations["x90"].amplitude = fit_results[q.name]["Pi_amplitude"] / 2 + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for q in qubits: + q.xy.operations[operation].amplitude = fit_results[q.name]["Pi_amplitude"] + if operation == "x180": + q.xy.operations["x90"].amplitude = fit_results[q.name]["Pi_amplitude"] / 2 - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/05_T1.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/05_T1.py index e4e7675e8..d0391ac8e 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/05_T1.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/05_T1.py @@ -19,7 +19,7 @@ from quam_libs.macros import qua_declaration, active_reset, readout_state from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.lib.fit import fit_decay_exp, decay_exp from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -45,7 +45,8 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 - + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="05_T1", parameters=Parameters()) @@ -58,13 +59,14 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() - +if node.parameters.load_data_id is None: + qmm = machine.connect() + # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": qubits = machine.active_qubits else: - qubits = [machine.qubits[q] for q in node.parameters.qubits.replace(" ", "").split(",")] + qubits = [machine.qubits[q] for q in node.parameters.qubits] num_qubits = len(qubits) @@ -94,13 +96,9 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): # Bring the active qubits to the desired frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint" or "arbitrary": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() with for_(n, 0, n < n_avg, n + 1): save(n, n_st) @@ -132,7 +130,8 @@ class Parameters(NodeParameters): save(I[i], I_st[i]) save(Q[i], Q_st[i]) # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -162,7 +161,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(t1) results = fetching_tool(job, ["n"], mode="live") @@ -172,14 +171,18 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"idle_time": idle_times}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits) - # Convert time into µs - ds = ds.assign_coords(idle_time=4 * ds.idle_time / u.us) # convert to µs - ds.idle_time.attrs = {"long_name": "idle time", "units": "µs"} +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"idle_time": idle_times}) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits) + # Convert time into µs + ds = ds.assign_coords(idle_time=4 * ds.idle_time / u.us) # convert to µs + ds.idle_time.attrs = {"long_name": "idle time", "units": "µs"} + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} @@ -235,16 +238,17 @@ class Parameters(NodeParameters): node.results["figure_raw"] = grid.fig # %% {Update_state} - with node.record_state_updates(): - for index, q in enumerate(qubits): - if ( + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for index, q in enumerate(qubits): + if ( float(tau.sel(qubit=q.name).values) > 0 and tau_error.sel(qubit=q.name).values / float(tau.sel(qubit=q.name).values) < 1 - ): - q.T1 = float(tau.sel(qubit=q.name).values) * 1e-6 + ): + q.T1 = float(tau.sel(qubit=q.name).values) * 1e-6 - # %% {Save_results} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + # %% {Save_results} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/06_Ramsey.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/06_Ramsey.py index f04bcd34d..e015c6268 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/06_Ramsey.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/06_Ramsey.py @@ -24,7 +24,7 @@ from quam_libs.macros import qua_declaration, readout_state from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.lib.fit import fit_oscillation_decay_exp, oscillation_decay_exp from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -44,7 +44,7 @@ class Parameters(NodeParameters): num_averages: int = 100 frequency_detuning_in_mhz: float = 1.0 min_wait_time_in_ns: int = 16 - max_wait_time_in_ns: int = 30000 + max_wait_time_in_ns: int = 3000 num_time_points: int = 500 log_or_linear_sweep: Literal["log", "linear"] = "log" flux_point_joint_or_independent: Literal["joint", "independent"] = "independent" @@ -52,9 +52,10 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 + load_data_id: Optional[int] = None + multiplexed: bool = False - -node = QualibrationNode(name="06a_Ramsey", parameters=Parameters()) +node = QualibrationNode(name="06_Ramsey", parameters=Parameters()) # %% {Initialize_QuAM_and_QOP} @@ -65,8 +66,9 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() - +if node.parameters.load_data_id is None: + qmm = machine.connect() + # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": qubits = machine.active_qubits @@ -132,16 +134,16 @@ class Parameters(NodeParameters): assign(phi, Cast.mul_fixed_by_int(detuning * 1e-9, 4 * t)) with else_(): assign(phi, Cast.mul_fixed_by_int(-detuning * 1e-9, 4 * t)) - align() + qubit.align() # # Strict_timing ensures that the sequence will be played without gaps - with strict_timing_(): - qubit.xy.play("x90") - qubit.xy.wait(t) - qubit.xy.frame_rotation_2pi(phi) - qubit.xy.play("x90") + # with strict_timing_(): + qubit.xy.play("x90") + qubit.xy.wait(t) + qubit.xy.frame_rotation_2pi(phi) + qubit.xy.play("x90") # Align the elements to measure after playing the qubit pulse. - align() + qubit.align() # Measure the state of the resonators and save data if node.parameters.use_state_discrimination: readout_state(qubit, state[i]) @@ -156,7 +158,8 @@ class Parameters(NodeParameters): # Reset the frame of the qubits in order not to accumulate rotations reset_frame(qubit.xy.name) # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -186,7 +189,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(ramsey) results = fetching_tool(job, ["n"], mode="live") @@ -197,15 +200,19 @@ class Parameters(NodeParameters): progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"sign": [-1, 1], "time": idle_times}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits) - # Add the absolute time to the dataset - ds = ds.assign_coords({"time": (["time"], 4 * idle_times)}) - ds.time.attrs["long_name"] = "idle_time" - ds.time.attrs["units"] = "ns" +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"sign": [-1, 1], "time": idle_times}) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits) + # Add the absolute time to the dataset + ds = ds.assign_coords({"time": (["time"], 4 * idle_times)}) + ds.time.attrs["long_name"] = "idle_time" + ds.time.attrs["units"] = "ns" + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} @@ -314,14 +321,17 @@ class Parameters(NodeParameters): # %% {Update_state} - with node.record_state_updates(): - for q in qubits: - q.xy.intermediate_frequency -= float(fit_results[q.name]["freq_offset"]) - q.T2ramsey = float(fit_results[qubit["qubit"]]["decay"]) + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for q in qubits: + q.xy.intermediate_frequency -= float(fit_results[q.name]["freq_offset"]) + q.T2ramsey = float(fit_results[q.name]["decay"]) - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() + +# %% diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07b_Readout_Frequency_Optimization.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07a_Readout_Frequency_Optimization.py similarity index 78% rename from Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07b_Readout_Frequency_Optimization.py rename to Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07a_Readout_Frequency_Optimization.py index 61021b372..15b4c268d 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07b_Readout_Frequency_Optimization.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07a_Readout_Frequency_Optimization.py @@ -22,7 +22,7 @@ from quam_libs.components import QuAM from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array from qualang_tools.multi_user import qm_session @@ -45,9 +45,10 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 + load_data_id: Optional[int] = None + multiplexed: bool = False - -node = QualibrationNode(name="07b_Readout_Frequency_Optimization", parameters=Parameters()) +node = QualibrationNode(name="07a_Readout_Frequency_Optimization", parameters=Parameters()) # %% {Initialize_QuAM_and_QOP} @@ -58,7 +59,8 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": @@ -92,13 +94,10 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): # Bring the active qubits to the desired frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() + with for_(n, 0, n < n_avg, n + 1): save(n, n_st) @@ -127,7 +126,8 @@ class Parameters(NodeParameters): save(I_e[i], I_e_st[i]) save(Q_e[i], Q_e_st[i]) # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -156,7 +156,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(ro_freq_opt) results = fetching_tool(job, ["n"], mode="live") @@ -164,30 +164,34 @@ class Parameters(NodeParameters): n = results.fetch_all()[0] progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"freq": dfs}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits, ["I_g", "Q_g", "I_e", "Q_e"]) - # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) for |g> and |e> as well as the distance between the two blobs D - ds = ds.assign( - { - "D": np.sqrt((ds.I_g - ds.I_e) ** 2 + (ds.Q_g - ds.Q_e) ** 2), - "IQ_abs_g": np.sqrt(ds.I_g**2 + ds.Q_g**2), - "IQ_abs_e": np.sqrt(ds.I_e**2 + ds.Q_e**2), - } - ) - # Add the absolute frequency to the dataset - ds = ds.assign_coords( - { - "freq_full": ( - ["qubit", "freq"], - np.array([dfs + q.resonator.RF_frequency for q in qubits]), - ) - } - ) - ds.freq_full.attrs["long_name"] = "Frequency" - ds.freq_full.attrs["units"] = "GHz" +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"freq": dfs}) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits, ["I_g", "Q_g", "I_e", "Q_e"]) + # Derive the amplitude IQ_abs = sqrt(I**2 + Q**2) for |g> and |e> as well as the distance between the two blobs D + ds = ds.assign( + { + "D": np.sqrt((ds.I_g - ds.I_e) ** 2 + (ds.Q_g - ds.Q_e) ** 2), + "IQ_abs_g": np.sqrt(ds.I_g**2 + ds.Q_g**2), + "IQ_abs_e": np.sqrt(ds.I_e**2 + ds.Q_e**2), + } + ) + # Add the absolute frequency to the dataset + ds = ds.assign_coords( + { + "freq_full": ( + ["qubit", "freq"], + np.array([dfs + q.resonator.RF_frequency for q in qubits]), + ) + } + ) + ds.freq_full.attrs["long_name"] = "Frequency" + ds.freq_full.attrs["units"] = "GHz" + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} @@ -240,14 +244,15 @@ class Parameters(NodeParameters): node.results["figure2"] = grid.fig # %% {Update_state} - for q in qubits: - with node.record_state_updates(): - q.resonator.intermediate_frequency += int(fit_results[q.name]["detuning"]) - q.chi = float(fit_results[q.name]["chi"]) - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + if node.parameters.load_data_id is None: + for q in qubits: + with node.record_state_updates(): + q.resonator.intermediate_frequency += int(fit_results[q.name]["detuning"]) + q.chi = float(fit_results[q.name]["chi"]) + + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07a_IQ_Blobs.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07b_IQ_Blobs.py similarity index 78% rename from Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07a_IQ_Blobs.py rename to Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07b_IQ_Blobs.py index 9a3b69cd8..656c2cd42 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07a_IQ_Blobs.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07b_IQ_Blobs.py @@ -27,7 +27,7 @@ from quam_libs.macros import qua_declaration, active_reset from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from qualang_tools.analysis.discriminator import two_state_discriminator from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.multi_user import qm_session @@ -51,9 +51,11 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 + load_data_id: Optional[int] = None + multiplexed: bool = False -node = QualibrationNode(name="07a_IQ_Blobs", parameters=Parameters()) +node = QualibrationNode(name="07b_IQ_Blobs", parameters=Parameters()) # %% {Initialize_QuAM_and_QOP} @@ -64,8 +66,9 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() - +if node.parameters.load_data_id is None: + qmm = machine.connect() + # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": qubits = machine.active_qubits @@ -86,13 +89,9 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): # Bring the active qubits to the desired frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint" or "arbitrary": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() with for_(n, 0, n < n_runs, n + 1): # ground iq blobs for all qubits @@ -119,16 +118,18 @@ class Parameters(NodeParameters): qubit.wait(qubit.thermalization_time * u.ns) else: raise ValueError(f"Unrecognized reset type {reset_type}.") - align() + qubit.align() qubit.xy.play("x180") - align() + qubit.align() qubit.resonator.measure(operation_name, qua_vars=(I_e[i], Q_e[i])) qubit.resonator.wait(qubit.resonator.depletion_time * u.ns) # save data save(I_e[i], I_e_st[i]) save(Q_e[i], Q_e_st[i]) + # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -156,8 +157,8 @@ class Parameters(NodeParameters): node.results = {"figure": plt.gcf()} node.machine = machine node.save() - -else: + +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(iq_blobs) for i in range(num_qubits): @@ -166,25 +167,30 @@ class Parameters(NodeParameters): n = results.fetch_all()[0] progress_counter(n, n_runs, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"N": np.linspace(1, n_runs, n_runs)}) - - # Fix the structure of ds to avoid tuples - def extract_value(element): - if isinstance(element, tuple): - return element[0] - return element - - ds = xr.apply_ufunc( - extract_value, - ds, - vectorize=True, # This ensures the function is applied element-wise - dask="parallelized", # This allows for parallel processing - output_dtypes=[float], # Specify the output data type - ) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits, ["I_g", "Q_g", "I_e", "Q_e"]) +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"N": np.linspace(1, n_runs, n_runs)}) + + # Fix the structure of ds to avoid tuples + def extract_value(element): + if isinstance(element, tuple): + return element[0] + return element + + ds = xr.apply_ufunc( + extract_value, + ds, + vectorize=True, # This ensures the function is applied element-wise + dask="parallelized", # This allows for parallel processing + output_dtypes=[float], # Specify the output data type + ) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits, ["I_g", "Q_g", "I_e", "Q_e"]) + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) + # %% {Data_analysis} node.results = {"ds": ds, "figs": {}, "results": {}} plot_individual = False @@ -301,29 +307,30 @@ def extract_value(element): node.results["figure_fidelity"] = grid.fig # %% {Update_state} - with node.record_state_updates(): - for qubit in qubits: - qubit.resonator.operations[operation_name].integration_weights_angle -= float( - node.results["results"][qubit.name]["angle"] - ) - # Convert the thresholds back in demod units - qubit.resonator.operations[operation_name].threshold = ( - float(node.results["results"][qubit.name]["threshold"]) - * qubit.resonator.operations[operation_name].length - / 2**12 - ) - # todo: add conf matrix to the readout operation rather than the resonator - qubit.resonator.operations[operation_name].rus_exit_threshold = ( - float(node.results["results"][qubit.name]["rus_threshold"]) - * qubit.resonator.operations[operation_name].length - / 2**12 - ) - if operation_name == "readout": - qubit.resonator.confusion_matrix = node.results["results"][qubit.name]["confusion_matrix"].tolist() - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for qubit in qubits: + qubit.resonator.operations[operation_name].integration_weights_angle -= float( + node.results["results"][qubit.name]["angle"] + ) + # Convert the thresholds back in demod units + qubit.resonator.operations[operation_name].threshold = ( + float(node.results["results"][qubit.name]["threshold"]) + * qubit.resonator.operations[operation_name].length + / 2**12 + ) + # todo: add conf matrix to the readout operation rather than the resonator + qubit.resonator.operations[operation_name].rus_exit_threshold = ( + float(node.results["results"][qubit.name]["rus_threshold"]) + * qubit.resonator.operations[operation_name].length + / 2**12 + ) + if operation_name == "readout": + qubit.resonator.confusion_matrix = node.results["results"][qubit.name]["confusion_matrix"].tolist() + + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07c_Readout_Power_Optimization.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07c_Readout_Power_Optimization.py index d63175a01..2c194acff 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07c_Readout_Power_Optimization.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/07c_Readout_Power_Optimization.py @@ -25,7 +25,7 @@ from quam_libs.components import QuAM from quam_libs.macros import qua_declaration, active_reset from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from qualang_tools.analysis import two_state_discriminator from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -54,6 +54,8 @@ class Parameters(NodeParameters): end_amp: float = 1.99 num_amps: int = 10 outliers_threshold: float = 0.98 + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="07c_Readout_Power_Optimization", parameters=Parameters()) @@ -67,7 +69,8 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": @@ -90,14 +93,10 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): - # Bring the active qubits to the minimum frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + # Bring the active qubits to the desired frequency point + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() with for_(n, 0, n < n_runs, n + 1): # ground iq blobs for all qubits @@ -131,7 +130,8 @@ class Parameters(NodeParameters): save(Q_e[i], Q_e_st[i]) # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -159,7 +159,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(iq_blobs) @@ -173,30 +173,36 @@ class Parameters(NodeParameters): progress_counter(n, n_runs, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"amplitude": amps, "N": np.linspace(1, n_runs, n_runs)}) - # Add the absolute readout power to the dataset - ds = ds.assign_coords({"readout_amp": (["qubit", "amplitude"], np.array([amps * q.resonator.operations["readout"].amplitude for q in qubits]))}) - # Rearrange the data to combine I_g and I_e into I, and Q_g and Q_e into Q - ds_rearranged = xr.Dataset() - # Combine I_g and I_e into I - ds_rearranged["I"] = xr.concat([ds.I_g, ds.I_e], dim="state") - ds_rearranged["I"] = ds_rearranged["I"].assign_coords(state=[0, 1]) - # Combine Q_g and Q_e into Q - ds_rearranged["Q"] = xr.concat([ds.Q_g, ds.Q_e], dim="state") - ds_rearranged["Q"] = ds_rearranged["Q"].assign_coords(state=[0, 1]) - # Copy other coordinates and data variables - for var in ds.coords: - if var not in ds_rearranged.coords: - ds_rearranged[var] = ds[var] - - for var in ds.data_vars: - if var not in ["I_g", "I_e", "Q_g", "Q_e"]: - ds_rearranged[var] = ds[var] - - # Replace the original dataset with the rearranged one - ds = ds_rearranged +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"amplitude": amps, "N": np.linspace(1, n_runs, n_runs)}) + # Add the absolute readout power to the dataset + ds = ds.assign_coords({"readout_amp": (["qubit", "amplitude"], np.array([amps * q.resonator.operations["readout"].amplitude for q in qubits]))}) + # Rearrange the data to combine I_g and I_e into I, and Q_g and Q_e into Q + ds_rearranged = xr.Dataset() + # Combine I_g and I_e into I + ds_rearranged["I"] = xr.concat([ds.I_g, ds.I_e], dim="state") + ds_rearranged["I"] = ds_rearranged["I"].assign_coords(state=[0, 1]) + # Combine Q_g and Q_e into Q + ds_rearranged["Q"] = xr.concat([ds.Q_g, ds.Q_e], dim="state") + ds_rearranged["Q"] = ds_rearranged["Q"].assign_coords(state=[0, 1]) + # Copy other coordinates and data variables + for var in ds.coords: + if var not in ds_rearranged.coords: + ds_rearranged[var] = ds[var] + + for var in ds.data_vars: + if var not in ["I_g", "I_e", "Q_g", "Q_e"]: + ds_rearranged[var] = ds[var] + + # Replace the original dataset with the rearranged one + ds = ds_rearranged + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) + + node.results = {"ds": ds, "results": {}, "figs": {}} plot_raw = False @@ -384,21 +390,22 @@ def apply_fit_gmm(I, Q): # %% {Update_state} - with node.record_state_updates(): - for qubit in qubits: - qubit.resonator.operations["readout"].integration_weights_angle -= float( - node.results["results"][qubit.name]["angle"] - ) - qubit.resonator.operations["readout"].threshold = float(node.results["results"][qubit.name]["threshold"]) - qubit.resonator.operations["readout"].rus_exit_threshold = float( - node.results["results"][qubit.name]["rus_threshold"] - ) - qubit.resonator.operations["readout"].amplitude = float(node.results["results"][qubit.name]["best_amp"]) - qubit.resonator.confusion_matrix = node.results["results"][qubit.name]["confusion_matrix"].tolist() - - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for qubit in qubits: + qubit.resonator.operations["readout"].integration_weights_angle -= float( + node.results["results"][qubit.name]["angle"] + ) + qubit.resonator.operations["readout"].threshold = float(node.results["results"][qubit.name]["threshold"]) + qubit.resonator.operations["readout"].rus_exit_threshold = float( + node.results["results"][qubit.name]["rus_threshold"] + ) + qubit.resonator.operations["readout"].amplitude = float(node.results["results"][qubit.name]["best_amp"]) + qubit.resonator.confusion_matrix = node.results["results"][qubit.name]["confusion_matrix"].tolist() + + + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/08a_Power_Rabi_Error_Amplification.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/08a_Power_Rabi_Error_Amplification.py index 5fc6acea3..09d969fe4 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/08a_Power_Rabi_Error_Amplification.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/08a_Power_Rabi_Error_Amplification.py @@ -22,9 +22,9 @@ from qualibrate import QualibrationNode, NodeParameters from quam_libs.components import QuAM from quam_libs.lib.instrument_limits import instrument_limits -from quam_libs.macros import qua_declaration, active_reset +from quam_libs.macros import qua_declaration, active_reset, readout_state from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.lib.fit import fit_oscillation, oscillation from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -52,6 +52,8 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="08_Power_Rabi_Error_Amplification", parameters=Parameters()) @@ -65,7 +67,8 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": @@ -98,21 +101,17 @@ class Parameters(NodeParameters): with program() as power_rabi: I, _, Q, _, n, n_st = qua_declaration(num_qubits=num_qubits) - state = [declare(bool) for _ in range(num_qubits)] + state = [declare(int) for _ in range(num_qubits)] state_stream = [declare_stream() for _ in range(num_qubits)] a = declare(fixed) # QUA variable for the qubit drive amplitude pre-factor npi = declare(int) # QUA variable for the number of qubit pulses count = declare(int) # QUA variable for counting the qubit pulses for i, qubit in enumerate(qubits): - # Bring the active qubits to the minimum frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + # Bring the active qubits to the desired frequency point + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() with for_(n, 0, n < n_avg, n + 1): save(n, n_st) @@ -122,27 +121,30 @@ class Parameters(NodeParameters): if reset_type == "active": active_reset(qubit, "readout") else: - wait(qubit.thermalization_time * u.ns) + qubit.wait(qubit.thermalization_time * u.ns) qubit.align() # Loop for error amplification (perform many qubit pulses) with for_(count, 0, count < npi, count + 1): qubit.xy.play(operation, amplitude_scale=a) - align() - qubit.resonator.measure("readout", qua_vars=(I[i], Q[i])) - assign(state[i], I[i] > qubit.resonator.operations["readout"].threshold) + qubit.align() + # qubit.resonator.measure("readout", qua_vars=(I[i], Q[i])) + # assign(state[i], I[i] > qubit.resonator.operations["readout"].threshold) + readout_state(qubit, state[i]) save(state[i], state_stream[i]) - align() + + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") for i, qubit in enumerate(qubits): if operation == "x180": - state_stream[i].boolean_to_int().buffer(len(amps)).buffer(np.ceil(N_pi / 2)).average().save( + state_stream[i].buffer(len(amps)).buffer(np.ceil(N_pi / 2)).average().save( f"state{i + 1}" ) elif operation in ["x90", "-x90", "y90", "-y90"]: - state_stream[i].boolean_to_int().buffer(len(amps)).buffer(np.ceil(N_pi / 4)).average().save( + state_stream[i].buffer(len(amps)).buffer(np.ceil(N_pi / 4)).average().save( f"state{i + 1}" ) else: @@ -167,28 +169,30 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(power_rabi) - - # %% {Live_plot} results = fetching_tool(job, ["n"], mode="live") while results.is_processing(): n = results.fetch_all()[0] progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"amp": amps, "N": N_pi_vec}) - # Add the qubit pulse absolute amplitude to the dataset - ds = ds.assign_coords( +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"amp": amps, "N": N_pi_vec}) + # Add the qubit pulse absolute amplitude to the dataset + ds = ds.assign_coords( { "abs_amp": ( ["qubit", "amp"], np.array([q.xy.operations[operation].amplitude * amps for q in qubits]), ) - } - ) + } + ) + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} @@ -264,12 +268,13 @@ class Parameters(NodeParameters): node.results["figure"] = grid.fig # %% {Update_state} - with node.record_state_updates(): - for q in qubits: - q.xy.operations[operation].amplitude = fit_results[q.name]["Pi_amplitude"] - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for q in qubits: + q.xy.operations[operation].amplitude = fit_results[q.name]["Pi_amplitude"] + + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/08b_Ramsey_vs_Flux_Calibration.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/08b_Ramsey_vs_Flux_Calibration.py index 0a2e2c7bf..7fc974624 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/08b_Ramsey_vs_Flux_Calibration.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/08b_Ramsey_vs_Flux_Calibration.py @@ -24,7 +24,7 @@ from quam_libs.macros import qua_declaration, readout_state from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.lib.fit import fit_oscillation_decay_exp, oscillation_decay_exp from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -51,7 +51,8 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 - + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="08b_Ramsey_vs_Flux_Calibration", parameters=Parameters()) @@ -64,7 +65,8 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() # Get the relevant QuAM components if node.parameters.qubits is None or node.parameters.qubits == "": @@ -101,15 +103,10 @@ class Parameters(NodeParameters): flux = declare(fixed) # QUA variable for the flux dc level for i, qubit in enumerate(qubits): - - # Bring the active qubits to the minimum frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + # Bring the active qubits to the desired frequency point + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() with for_(n, 0, n < n_avg, n + 1): save(n, n_st) @@ -143,7 +140,8 @@ class Parameters(NodeParameters): reset_frame(qubit.xy.name) qubit.align() - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -169,7 +167,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(ramsey) results = fetching_tool(job, ["n"], mode="live") @@ -179,12 +177,16 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - ds = fetch_results_as_xarray(job.result_handles, qubits, {"idle_time": idle_times, "flux": fluxes}) - # Add the absolute time in µs to the dataset - ds = ds.assign_coords(idle_time=4 * ds.idle_time / 1e3) - ds.flux.attrs = {"long_name": "flux", "units": "V"} - ds.idle_time.attrs = {"long_name": "idle time", "units": "µs"} +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + ds = fetch_results_as_xarray(job.result_handles, qubits, {"idle_time": idle_times, "flux": fluxes}) + # Add the absolute time in µs to the dataset + ds = ds.assign_coords(idle_time=4 * ds.idle_time / 1e3) + ds.flux.attrs = {"long_name": "flux", "units": "V"} + ds.idle_time.attrs = {"long_name": "idle time", "units": "µs"} + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} @@ -226,7 +228,9 @@ class Parameters(NodeParameters): / fitvals.sel(qubit=q.name, degree=2).polyfit_coefficients ).values ) - freq_offset[q.name] = 1e6 * float(fitvals.sel(qubit=q.name, degree=0).polyfit_coefficients.values) - detuning + freq_offset[q.name] = 1e6 * (flux_offset[q.name]**2 * float(fitvals.sel(qubit=q.name, degree=2).polyfit_coefficients.values) + + flux_offset[q.name] * float(fitvals.sel(qubit=q.name, degree=1).polyfit_coefficients.values) + + float(fitvals.sel(qubit=q.name, degree=0).polyfit_coefficients.values)) - detuning # Save fitting results node.results["fit_results"] = {} @@ -270,19 +274,20 @@ class Parameters(NodeParameters): node.results["figure"] = grid.fig # %% {Update_state} - with node.record_state_updates(): - for qubit in qubits: - qubit.xy.intermediate_frequency -= freq_offset[qubit.name] - if flux_point == "independent": - qubit.z.independent_offset += flux_offset[qubit.name] - elif flux_point == "joint": - qubit.z.joint_offset += flux_offset[qubit.name] - else: - raise RuntimeError(f"unknown flux_point") - qubit.freq_vs_flux_01_quad_term = float(a[qubit.name]) - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for qubit in qubits: + qubit.xy.intermediate_frequency -= freq_offset[qubit.name] + if flux_point == "independent": + qubit.z.independent_offset += flux_offset[qubit.name] + elif flux_point == "joint": + qubit.z.joint_offset += flux_offset[qubit.name] + else: + raise RuntimeError(f"unknown flux_point") + qubit.freq_vs_flux_01_quad_term = float(a[qubit.name]) + + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09a_Stark_Detuning.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09a_Stark_Detuning.py index abf5833ef..39a7840f5 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09a_Stark_Detuning.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09a_Stark_Detuning.py @@ -26,7 +26,7 @@ from quam_libs.macros import qua_declaration, active_reset from quam_libs.lib.qua_datasets import convert_IQ_to_V from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.trackable_object import tracked_updates from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -53,7 +53,8 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 - + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="09a_Stark_Detuning", parameters=Parameters()) @@ -84,7 +85,8 @@ class Parameters(NodeParameters): # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() # %% {QUA_program} @@ -109,13 +111,9 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): # Bring the active qubits to the desired frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() with for_(n, 0, n < n_avg, n + 1): save(n, n_st) @@ -149,7 +147,8 @@ class Parameters(NodeParameters): save(I[i], I_st[i]) save(Q[i], Q_st[i]) # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -177,7 +176,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(stark_detuning) results = fetching_tool(job, ["n"], mode="live") @@ -187,11 +186,15 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"freq": dfs, "N": N_pi_vec}) - # Convert IQ data into volts - ds = convert_IQ_to_V(ds, qubits) +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"freq": dfs, "N": N_pi_vec}) + # Convert IQ data into volts + ds = convert_IQ_to_V(ds, qubits) + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} @@ -224,15 +227,16 @@ class Parameters(NodeParameters): # Revert the change done at the beginning of the node for qubit in tracked_qubits: qubit.revert_changes() - with node.record_state_updates(): - for qubit in qubits: - qubit.xy.operations[operation].detuning = float(fit_results[qubit.name]["detuning"]) - if node.parameters.DRAG_setpoint is not None: - qubit.xy.operations[operation].alpha = node.parameters.DRAG_setpoint - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for qubit in qubits: + qubit.xy.operations[operation].detuning = float(fit_results[qubit.name]["detuning"]) + if node.parameters.DRAG_setpoint is not None: + qubit.xy.operations[operation].alpha = node.parameters.DRAG_setpoint + + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09b_DRAG_Calibration_180_minus_180.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09b_DRAG_Calibration_180_minus_180.py index 4ef5c0a1c..d23096646 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09b_DRAG_Calibration_180_minus_180.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09b_DRAG_Calibration_180_minus_180.py @@ -23,7 +23,7 @@ from quam_libs.components import QuAM from quam_libs.macros import qua_declaration, active_reset from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.trackable_object import tracked_updates from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -50,6 +50,10 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 + alpha_setpoint: Optional[float] = -1.0 + load_data_id: Optional[int] = None + multiplexed: bool = False + node = QualibrationNode(name="09b_DRAG_Calibration_180_minus_180", parameters=Parameters()) @@ -70,15 +74,17 @@ class Parameters(NodeParameters): # Update the readout power to match the desired range, this change will be reverted at the end of the node. tracked_qubits = [] -for q in qubits: - with tracked_updates(q, auto_revert=False, dont_assign_to_none=True) as q: - q.xy.operations[operation].alpha = -1.0 - tracked_qubits.append(q) +if node.parameters.alpha_setpoint is not None: + for q in qubits: + with tracked_updates(q, auto_revert=False, dont_assign_to_none=True) as q: + q.xy.operations[operation].alpha = node.parameters.alpha_setpoint + tracked_qubits.append(q) # Generate the OPX and Octave configurations config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() if node.parameters.qubits is None or node.parameters.qubits == "": qubits = machine.active_qubits @@ -111,13 +117,9 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): # Bring the active qubits to the desired frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() with for_(n, 0, n < n_avg, n + 1): save(n, n_st) @@ -127,7 +129,7 @@ class Parameters(NodeParameters): if reset_type == "active": active_reset(qubit, "readout") else: - qubit.resonator.wait(qubit.thermalization_time * u.ns) + qubit.wait(qubit.thermalization_time * u.ns) qubit.align() # Loop for error amplification (perform many qubit pulses) with for_(count, 0, count < npi, count + 1): @@ -145,7 +147,8 @@ class Parameters(NodeParameters): assign(state[i], I[i] > qubit.resonator.operations["readout"].threshold) save(state[i], state_stream[i]) # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -171,7 +174,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(drag_calibration) results = fetching_tool(job, ["n"], mode="live") @@ -181,18 +184,22 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"amp": amps, "N": N_pi_vec}) - # Add the qubit pulse absolute alpha coefficient to the dataset - ds = ds.assign_coords( +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"amp": amps, "N": N_pi_vec}) + # Add the qubit pulse absolute alpha coefficient to the dataset + ds = ds.assign_coords( { "alpha": ( ["qubit", "amp"], np.array([q.xy.operations[operation].alpha * amps for q in qubits]), ) } - ) + ) + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} @@ -228,13 +235,14 @@ class Parameters(NodeParameters): for qubit in tracked_qubits: qubit.revert_changes() # Update the state - with node.record_state_updates(): - for q in qubits: - q.xy.operations[operation].alpha = fit_results[q.name]["alpha"] - - # %% {Save_results} - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for q in qubits: + q.xy.operations[operation].alpha = fit_results[q.name]["alpha"] + + # %% {Save_results} + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09c_DRAG_Calibration_180_90.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09c_DRAG_Calibration_180_90.py index 83de09713..78d881b27 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09c_DRAG_Calibration_180_90.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/09c_DRAG_Calibration_180_90.py @@ -24,7 +24,7 @@ from quam_libs.components import QuAM from quam_libs.macros import qua_declaration, active_reset from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.trackable_object import tracked_updates from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.loops import from_array @@ -51,7 +51,8 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 - + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="09c_DRAG_Calibration_180_90", parameters=Parameters()) @@ -77,7 +78,8 @@ class Parameters(NodeParameters): config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() # %% {QUA_program} @@ -102,13 +104,10 @@ class Parameters(NodeParameters): for i, qubit in enumerate(qubits): # Bring the active qubits to the desired frequency point - if flux_point == "independent": - machine.apply_all_flux_to_min() - qubit.z.to_independent_idle() - elif flux_point == "joint": - machine.apply_all_flux_to_joint_idle() - else: - machine.apply_all_flux_to_zero() + machine.set_all_fluxes(flux_point=flux_point, target=qubit) + qubit.z.settle() + qubit.align() + with for_(n, 0, n < n_avg, n + 1): save(n, n_st) @@ -132,7 +131,8 @@ class Parameters(NodeParameters): assign(state[i], I[i] > qubit.resonator.operations["readout"].threshold) save(state[i], state_stream[i]) # Measure sequentially - align() + if not node.parameters.multiplexed: + align() with stream_processing(): n_st.save("n") @@ -158,7 +158,7 @@ class Parameters(NodeParameters): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: job = qm.execute(drag_calibration) results = fetching_tool(job, ["n"], mode="live") @@ -168,13 +168,17 @@ class Parameters(NodeParameters): # Progress bar progress_counter(n, n_avg, start_time=results.start_time) - # %% {Data_fetching_and_dataset_creation} - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray(job.result_handles, qubits, {"amp": amps, "sequence": [0, 1]}) - # Add the qubit pulse absolute alpha coefficient to the dataset - ds = ds.assign_coords( - {"alpha": (["qubit", "amp"], np.array([q.xy.operations[operation].alpha * amps for q in qubits]))} - ) +# %% {Data_fetching_and_dataset_creation} +if not node.parameters.simulate: + if node.parameters.load_data_id is None: + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray(job.result_handles, qubits, {"amp": amps, "sequence": [0, 1]}) + # Add the qubit pulse absolute alpha coefficient to the dataset + ds = ds.assign_coords( + {"alpha": (["qubit", "amp"], np.array([q.xy.operations[operation].alpha * amps for q in qubits]))} + ) + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} @@ -215,11 +219,12 @@ class Parameters(NodeParameters): for qubit in tracked_qubits: qubit.revert_changes() # Update the state - with node.record_state_updates(): - for q in qubits: - q.xy.operations[operation].alpha = fit_results[q.name]["alpha"] - - # %% {Save_results} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + if node.parameters.load_data_id is None: + with node.record_state_updates(): + for q in qubits: + q.xy.operations[operation].alpha = fit_results[q.name]["alpha"] + + # %% {Save_results} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/10a_Single_Qubit_Randomized_Benchmarking.py b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/10a_Single_Qubit_Randomized_Benchmarking.py index e73b95833..2124e4323 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/10a_Single_Qubit_Randomized_Benchmarking.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/calibration_graph/10a_Single_Qubit_Randomized_Benchmarking.py @@ -24,7 +24,7 @@ from quam_libs.components import QuAM, Transmon from quam_libs.macros import qua_declaration, active_reset from quam_libs.lib.plot_utils import QubitGrid, grid_iter -from quam_libs.lib.save_utils import fetch_results_as_xarray +from quam_libs.lib.save_utils import fetch_results_as_xarray, load_dataset from quam_libs.lib.fit import fit_decay_exp, decay_exp from qualang_tools.results import progress_counter, fetching_tool from qualang_tools.bakery.randomized_benchmark_c1 import c1_table @@ -54,7 +54,8 @@ class Parameters(NodeParameters): simulate: bool = False simulation_duration_ns: int = 2500 timeout: int = 100 - + load_data_id: Optional[int] = None + multiplexed: bool = False node = QualibrationNode(name="10a_Single_Qubit_Randomized_Benchmarking", parameters=Parameters()) @@ -68,7 +69,8 @@ class Parameters(NodeParameters): config = machine.generate_config() # Open Communication with the QOP -qmm = machine.connect() +if node.parameters.load_data_id is None: + qmm = machine.connect() if node.parameters.qubits is None or node.parameters.qubits == "": qubits = machine.active_qubits @@ -242,6 +244,8 @@ def play_sequence(sequence_list, depth, qubit: Transmon): if flux_point == "independent": machine.apply_all_flux_to_min() qubit.z.to_independent_idle() + if node.parameters.multiplexed: + qubit.align() else: align() # Initialize the qubits @@ -299,7 +303,7 @@ def play_sequence(sequence_list, depth, qubit: Transmon): node.machine = machine node.save() -else: +elif node.parameters.load_data_id is None: # Prepare data for saving node.results = {} with qm_session(qmm, config, timeout=node.parameters.timeout) as qm: @@ -314,17 +318,19 @@ def play_sequence(sequence_list, depth, qubit: Transmon): progress_counter(m, num_of_sequences, start_time=results.start_time) # %% {Data_fetching_and_dataset_creation} - depths = np.arange(0, max_circuit_depth + 0.1, delta_clifford) - depths[0] = 1 - # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) - ds = fetch_results_as_xarray( + if node.parameters.load_data_id is None: + depths = np.arange(0, max_circuit_depth + 0.1, delta_clifford) + depths[0] = 1 + # Fetch the data from the OPX and convert it into a xarray with corresponding axes (from most inner to outer loop) + ds = fetch_results_as_xarray( job.result_handles, qubits, - {"depths": depths, "sequence": np.arange(num_of_sequences)}, - ) + {"depths": depths, "sequence": np.arange(num_of_sequences)}, + ) + else: + ds, machine, json_data, qubits, node.parameters = load_dataset(node.parameters.load_data_id, parameters = node.parameters) # Add the dataset to the node node.results = {"ds": ds} - # %% {Data_analysis} da_state = 1 - ds["state"].mean(dim="sequence") da_state: xr.DataArray @@ -384,10 +390,10 @@ def play_sequence(sequence_list, depth, qubit: Transmon): node.results["figure"] = grid.fig -# %% {Save_results} -if not node.parameters.simulate: - node.outcomes = {q.name: "successful" for q in qubits} - node.results["initial_parameters"] = node.parameters.model_dump() - node.machine = machine - node.save() + # %% {Save_results} + if not node.parameters.simulate: + node.outcomes = {q.name: "successful" for q in qubits} + node.results["initial_parameters"] = node.parameters.model_dump() + node.machine = machine + node.save() diff --git a/Quantum-Control-Applications-QuAM/Superconducting/quam_libs/components/quam_root.py b/Quantum-Control-Applications-QuAM/Superconducting/quam_libs/components/quam_root.py index 59642ea72..623f58d8a 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/quam_libs/components/quam_root.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/quam_libs/components/quam_root.py @@ -16,7 +16,6 @@ from .transmon import Transmon from .transmon_pair import TransmonPair -from qm.qua import align from qm import QuantumMachinesManager, QuantumMachine from qualang_tools.results.data_handler import DataHandler @@ -98,49 +97,64 @@ def thermalization_time(self) -> int: def apply_all_couplers_to_min(self) -> None: """Apply the offsets that bring all the active qubit pairs to a decoupled point.""" - align() for qp in self.active_qubit_pairs: if qp.coupler is not None: qp.coupler.to_decouple_idle() - align() def apply_all_flux_to_joint_idle(self) -> None: """Apply the offsets that bring all the active qubits to the joint sweet spot.""" - align() for q in self.active_qubits: if q.z is not None: q.z.to_joint_idle() - q.z.settle() else: warnings.warn(f"Didn't find z-element on qubit {q.name}, didn't set to joint-idle") for q in self.qubits: if self.qubits[q] not in self.active_qubits: if self.qubits[q].z is not None: self.qubits[q].z.to_min() - self.qubits[q].z.settle() else: warnings.warn(f"Didn't find z-element on qubit {q}, didn't set to min") - align() + self.apply_all_couplers_to_min() def apply_all_flux_to_min(self) -> None: """Apply the offsets that bring all the active qubits to the minimum frequency point.""" - align() for q in self.qubits: if self.qubits[q].z is not None: self.qubits[q].z.to_min() - self.qubits[q].z.settle() else: warnings.warn(f"Didn't find z-element on qubit {q}, didn't set to min") self.apply_all_couplers_to_min() - align() def apply_all_flux_to_zero(self) -> None: """Apply the offsets that bring all the active qubits to the zero bias point.""" - align() for q in self.active_qubits: q.z.to_zero() - q.z.settle() - align() + + + def set_all_fluxes(self, flux_point : str, target : Union[Transmon, TransmonPair]): + if flux_point == "independent": + assert isinstance(target, Transmon), "Independent flux point is only supported for individual transmons" + elif flux_point == "pairwise": + assert isinstance(target, TransmonPair), "Pairwise flux point is only supported for transmon pairs" + + if flux_point == "joint": + self.apply_all_flux_to_joint_idle() + if isinstance(target, TransmonPair): + target_bias =target.mutual_flux_bias + else: + target_bias = target.z.joint_offset + else: + self.apply_all_flux_to_min() + + if flux_point == "independent": + target.z.to_independent_idle() + target_bias = target.z.independent_offset + + elif flux_point == "pairwise": + target.to_mutual_idle() + target_bias = target.mutual_flux_bias + + return target_bias def connect(self) -> QuantumMachinesManager: """Open a Quantum Machine Manager with the credentials ("host" and "cluster_name") as defined in the network file. diff --git a/Quantum-Control-Applications-QuAM/Superconducting/quam_libs/lib/save_utils.py b/Quantum-Control-Applications-QuAM/Superconducting/quam_libs/lib/save_utils.py index 0a2fa07ac..eee959033 100644 --- a/Quantum-Control-Applications-QuAM/Superconducting/quam_libs/lib/save_utils.py +++ b/Quantum-Control-Applications-QuAM/Superconducting/quam_libs/lib/save_utils.py @@ -3,7 +3,7 @@ import os from pathlib import Path import xarray as xr - +import json def extract_string(input_string): # Find the index of the first occurrence of a digit in the input string @@ -50,44 +50,68 @@ def get_storage_path(): return Path(storage_location) -def find_folder(path, id): - for root, dirs, _ in os.walk(path): - for dir in dirs: - if f"#{id}" in dir: - return os.path.join(root, dir) +def find_numbered_folder(base_path, number): + """ + Find folder that starts with '#number_' + Will match '#number_something' but not '#number' alone + """ + search_prefix = f"#{number}_" + + # Manual search for folder starting with #number_ and having something after + for root, dirs, _ in os.walk(base_path): + matching_dirs = [d for d in dirs if d.startswith(search_prefix) and len(d) > len(search_prefix)] + if matching_dirs: + return os.path.join(root, matching_dirs[0]) + return None -def load_dataset(serial_number): + +def load_dataset(serial_number, target_filename = "ds", parameters = None): """ Loads a dataset from a file based on the serial number. - + Args: serial_number: The serial number to search for. base_folder: The base directory to search in. - + Returns: An xarray Dataset if found, None otherwise. """ - if type(serial_number) == int: - serial_number = str(serial_number) - - base_folder = find_folder(get_storage_path(), serial_number) + if not isinstance(serial_number, int): + raise ValueError("serial_number must be an integer") + + base_folder = find_numbered_folder(get_storage_path(),serial_number) # Look for .nc files in the subfolder - nc_files = [f for f in os.listdir(base_folder) if f.endswith(".h5")] - + nc_files = [f for f in os.listdir(base_folder) if f.endswith('.h5')] + + # look for filename.h5 + is_present = target_filename in [file.split('.')[0] for file in nc_files] + filename = [file for file in nc_files if target_filename == file.split('.')[0]][0] if is_present else None + json_filename = "data.json" + if nc_files: # Assuming there's only one .nc file per folder - file_path = os.path.join(base_folder, nc_files[0]) - + file_path = os.path.join(base_folder, filename) + json_path = os.path.join(base_folder, json_filename) # Open the dataset ds = xr.open_dataset(file_path) + with open(json_path, 'r') as f: + json_data = json.load(f) try: - machine = QuAM.load(base_folder) + machine = QuAM.load(base_folder + "//quam_state.json") except Exception as e: print(f"Error loading machine: {e}") machine = None - return ds, machine + qubits = [machine.qubits[qname] for qname in ds.qubit.values] + if parameters is not None: + for param_name, param_value in parameters: + if param_name is not "load_data_id": + if param_name in json_data["initial_parameters"]: + setattr(parameters, param_name, json_data["initial_parameters"][param_name]) + return ds, machine, json_data, qubits,parameters + else: + return ds, machine, json_data, qubits else: print(f"No .nc file found in folder: {base_folder}") return None