# --- # jupyter: # jupytext: # formats: ipynb,py:percent # hide_notebook_metadata: false # text_representation: # extension: .py # format_name: percent # format_version: '1.3' # jupytext_version: 1.14.5 # kernelspec: # display_name: .venv # language: python # name: python3 # --- # %% [markdown] # Download Script # Download Job # #
#

Job Files for Complete Examples

#

# To be able to run the complete examples without having to submit your program to hardware and wait, you'll # need to download the associated job files. These files contain the results of running the program on # the quantum hardware. # # You can download the job files by clicking the "Download _ Job" button above. You'll then need to place # the job file in the `data` directory that was created for you when you ran the `import` part of the script # (alternatively you can make the directory yourself, it should live at the same level as wherever you put this script). #

#
# # %% [markdown] # # Single Qubit Floquet Dynamics # ## Introduction # Rounding out the single qubit examples we will show how to generate a Floquet # protocol. We will define the protocol using a python function and then use the # Bloqade API to sample the function at certain intervals to make it compatible with # the hardware, which only supports piecewise linear/constant functions. First let us # start with the imports. # %% import os import numpy as np import matplotlib.pyplot as plt from bloqade.analog import cast, load, save, start if not os.path.isdir("data"): os.mkdir("data") # %% [markdown] # ## Define the program. # For the Floquet protocol we keep We do the same Rabi drive but allow the detuning to # vary sinusoidally. We do this by defining a smooth function for the detuning and then # sampling it at certain intervals (in this case, the minimum hardware-supported time # step). Note that the `sample` method will always sample at equal to or greater than # the specified time step. If the total time interval is not divisible by the time # step, the last time step will be larger than the specified time step. Also note that # the arguments of your function must be named arguments, e.g. no `*args` or `**kwargs`, # because Bloqade will analyze the function signature to and generate variables for # each argument. # %% min_time_step = 0.05 durations = cast(["ramp_time", "run_time", "ramp_time"]) def detuning_wf(t, drive_amplitude, drive_frequency): return drive_amplitude * np.sin(drive_frequency * t) floquet_program = ( start.add_position((0, 0)) .rydberg.rabi.amplitude.uniform.piecewise_linear( durations, [0, "rabi_max", "rabi_max", 0] ) .detuning.uniform.fn(detuning_wf, sum(durations)) .sample("min_time_step", "linear") ) # %% [markdown] # We assign values to the necessary variables and then run_async the program to both # the emulator and actual hardware. # %% run_times = np.linspace(0.05, 3.0, 101) floquet_job = floquet_program.assign( ramp_time=0.06, min_time_step=0.05, rabi_max=15, drive_amplitude=15, drive_frequency=15, ).batch_assign(run_time=run_times) # %% [markdown] # have to start the time at 0.05 because the hardware does not support anything less # than that time step. We can now run_async the job to the emulator and hardware. # %% [markdown] # ## Run Emulator and Hardware # Like in the first tutorial, we will run the program on the emulator and hardware. # Note that for the hardware we will use the `parallelize` method to run multiple # copies of the program in parallel. For more information about this process, see the # first tutorial. # #
#

Hardware Execution Cost

#

# # For this particular program, 101 tasks are generated with each task having 50 shots, amounting to # __USD \\$80.80__ on AWS Braket. # #

#
# %% emu_filename = os.path.join(os.path.abspath(""), "data", "floquet-emulation.json") print(emu_filename) if not os.path.isfile(emu_filename): emu_batch = floquet_job.bloqade.python().run(10000) save(emu_batch, emu_filename) hardware_filename = os.path.join(os.path.abspath(""), "data", "floquet-job.json") if not os.path.isfile(hardware_filename): batch = floquet_job.parallelize(24).braket.aquila().run_async(shots=50) save(batch, hardware_filename) # %% [markdown] # ## Plotting the Results # Exactly like in the Rabi Oscillation example, we can now plot the results from the # hardware and emulation together. Again we will use the `report` to calculate the mean # Rydberg population for each run, and then plot the results. # # first we load the results from the emulation and hardware. # %% emu_batch = load(emu_filename) assert not isinstance(emu_batch, dict) hardware_batch = load(hardware_filename) assert not isinstance(hardware_batch, dict) # hardware_batch.fetch() # save(filename, hardware_batch) # %% [markdown] # Next we extract the run times and the Rydberg population from the report. We can then # plot the results. # %% hardware_report = hardware_batch.report() emulator_report = emu_batch.report() times = emulator_report.list_param("run_time") density = [1 - ele.mean() for ele in emulator_report.bitstrings()] plt.plot(times, density, color="#878787", marker=".", label="Emulator") times = hardware_report.list_param("run_time") density = [1 - ele.mean() for ele in hardware_report.bitstrings()] plt.plot(times, density, color="#6437FF", linewidth=4, label="QPU") plt.xlabel("Time ($\mu s$)") plt.ylabel("Rydberg population") plt.legend() plt.show()