Quandary
  • Home
  • Basics
    • Getting Started
    • Tutorial
  • User Guide
  • Reference
    • Python API Reference
    • C++ Configuration Reference
    • Doxygen
  • Search
  • Previous
  • Next
  • This notebook shortly explains how to use Quandary through the python interface.

This notebook shortly explains how to use Quandary through the python interface.¶

Latest update: 11/14/2023, Stefanie Guenther (guenther5@llnl.gov)

Content:

  1. How to generate control pulses for a unitary gate
  2. How to evaluate pulses on a specific sample rate
  3. How to get the propagator and/or the fidelity for given control parameters from Quandary?
  4. How to compare results to QuTIP?
  5. How to to generate pulses that realise a state to state transfer
  6. How to use the open-system solver to model decay and dephasing
  7. FAQs ("What if...")

The pythoninterface functions as used here are defined in './quandary.py'. That file might be a valuable resource to check for default settings and available options that are not described in this notebook.

In [ ]:
Copied!
#  Quandary's python interface functions are defined in /path/to/quandary/quandary.py. Import them here. 
from quandary import *
%matplotlib inline
# Quandary's python interface functions are defined in /path/to/quandary/quandary.py. Import them here. from quandary import * %matplotlib inline

1. How to use Quandary to generate pulses that realize a unitary gate¶

In [2]:
Copied!
#
# THE SHORT STORY...
# 
# Example usage of Quandary to optimize for a SWAP 02 gate. 
# This example considers a three-level qubit with one extra guard level.

# Define the number of qubits and states per qubit. Here: One qudit modelled with 3 essential levels and one extra guard level
Ne = [3]   # essential levels per qubit
Ng = [1]   # guard levels per qubit

# Required: Define the target gate and pulse duration
unitary = [[0,0,1],[0,1,0],[1,0,0]]   # Target unitary gate, here SWAP02
pulse_length = 200.0                  # Pulse duration, unit ns

# Optionally: Set bounds on the control pulse amplitudes in rotational frame [MHz]
maxctrl_MHz = 4.0  

# Pass all options to the Quandary constructor. Check out other default options within the Quandary class, e.g. with help(Quandary)
quandary = Quandary(Ne=Ne, Ng=Ng, targetgate=unitary, T=pulse_length, maxctrl_MHz=maxctrl_MHz, rand_seed=1234)

# Optimize with quandary. Check help(quandary.optimize). This runs in the background on <prod(Ne)> cores. 
t, p, q, infidelity, expectedEnergy, population = quandary.optimize()
print(f"\nOptimized Fidelity = {1.0 - infidelity}")

# Quandary returns:
#   - t              : a list of time-points where the optimized control pulses are stored (ns) 
#   - p, q           : lists of the control pulses p(t) and q(t) for each qubit (list(list)), unit MHz, evaluated at each time point in the list t
#   - infidelity     : 1-fidelity of the realized unitary compared to the target gate
#   - expectedEnergy : lists of the extected energy level of each qubit evaluated at each time point 
#   - population     : lists of the level occupations (population) evaluated at each time point, one list for each initial basis state 

# In addition, the following other results can be accessed after quandary.optimize() has been called:
#   - quandary.popt  (list)   : The optimized B-spline parameters that define the optimized pulses p and q
#   - quandary.uT    (matrix) : The final state at time T of each of the initial states. For gate optimization (basis initial states), this is the solution operator / process matrix / propagator, see below

# Plot the results. Other plotting functions are available, see quandary.py
plot_results_1osc(quandary, p[0], q[0], expectedEnergy[0], population[0])
# # THE SHORT STORY... # # Example usage of Quandary to optimize for a SWAP 02 gate. # This example considers a three-level qubit with one extra guard level. # Define the number of qubits and states per qubit. Here: One qudit modelled with 3 essential levels and one extra guard level Ne = [3] # essential levels per qubit Ng = [1] # guard levels per qubit # Required: Define the target gate and pulse duration unitary = [[0,0,1],[0,1,0],[1,0,0]] # Target unitary gate, here SWAP02 pulse_length = 200.0 # Pulse duration, unit ns # Optionally: Set bounds on the control pulse amplitudes in rotational frame [MHz] maxctrl_MHz = 4.0 # Pass all options to the Quandary constructor. Check out other default options within the Quandary class, e.g. with help(Quandary) quandary = Quandary(Ne=Ne, Ng=Ng, targetgate=unitary, T=pulse_length, maxctrl_MHz=maxctrl_MHz, rand_seed=1234) # Optimize with quandary. Check help(quandary.optimize). This runs in the background on cores. t, p, q, infidelity, expectedEnergy, population = quandary.optimize() print(f"\nOptimized Fidelity = {1.0 - infidelity}") # Quandary returns: # - t : a list of time-points where the optimized control pulses are stored (ns) # - p, q : lists of the control pulses p(t) and q(t) for each qubit (list(list)), unit MHz, evaluated at each time point in the list t # - infidelity : 1-fidelity of the realized unitary compared to the target gate # - expectedEnergy : lists of the extected energy level of each qubit evaluated at each time point # - population : lists of the level occupations (population) evaluated at each time point, one list for each initial basis state # In addition, the following other results can be accessed after quandary.optimize() has been called: # - quandary.popt (list) : The optimized B-spline parameters that define the optimized pulses p and q # - quandary.uT (matrix) : The final state at time T of each of the initial states. For gate optimization (basis initial states), this is the solution operator / process matrix / propagator, see below # Plot the results. Other plotting functions are available, see quandary.py plot_results_1osc(quandary, p[0], q[0], expectedEnergy[0], population[0])
Executing ' mpirun -np  3 quandary ./config.cfg --quiet . Runtype:  optimization ...
    Objective             Tikhonov                Penalty-Leakage        Penalty-StateVar       Penalty-TotalEnergy 
0  9.01515106797588e-01 + 7.56688450300555e-07 + 1.70771506137339e-05 + 2.65273939142498e-07 + 1.16596605571299e-05  Fidelity = 9.84848932024121e-02  ||Grad|| = 2.36672188701292e-01
1  7.08824168583751e-01 + 1.08404852794896e-06 + 2.21561270073683e-05 + 2.50365182948080e-06 + 3.15623992210024e-05  Fidelity = 2.91175831416249e-01  ||Grad|| = 5.26715292435151e-01
2  9.49493141894147e-02 + 6.63286504808368e-07 + 1.60111032565354e-05 + 1.38032853363013e-06 + 1.87442082712003e-05  Fidelity = 9.05050685810585e-01  ||Grad|| = 3.75297345696423e-01
3  3.12404587975532e-02 + 6.04409646723860e-07 + 1.40206540893403e-05 + 1.47003934763770e-06 + 1.69019066433091e-05  Fidelity = 9.68759541202447e-01  ||Grad|| = 1.54539816357217e-01
4  2.83151317394603e-03 + 6.65027445079292e-07 + 1.39467689374924e-05 + 1.69247080292643e-06 + 1.89555415407984e-05  Fidelity = 9.97168486826054e-01  ||Grad|| = 3.09729633390628e-01
5  1.26823584570079e-03 + 6.57675048717162e-07 + 1.39879679686560e-05 + 1.67653853597811e-06 + 1.87132839418267e-05  Fidelity = 9.98731764154299e-01  ||Grad|| = 9.10244318014165e-02
6  1.18234709835241e-03 + 6.59543579383871e-07 + 1.39661498504557e-05 + 1.68529023901090e-06 + 1.87748745134650e-05  Fidelity = 9.98817652901648e-01  ||Grad|| = 7.42527272663934e-02
7  1.16645843454033e-03 + 6.60057627864052e-07 + 1.38886816644435e-05 + 1.68748392047214e-06 + 1.87933012601549e-05  Fidelity = 9.98833541565460e-01  ||Grad|| = 8.09197181812548e-02
8  1.15159553355304e-03 + 6.60099360247356e-07 + 1.37503955558618e-05 + 1.68755735022039e-06 + 1.87975267231114e-05  Fidelity = 9.98848404466447e-01  ||Grad|| = 9.45143900273770e-02
9  1.05143518045170e-03 + 6.59546787823143e-07 + 1.26738186203262e-05 + 1.68637110239832e-06 + 1.88065782856016e-05  Fidelity = 9.98948564819548e-01  ||Grad|| = 1.36823877382741e-01
10  7.87573789338469e-04 + 6.57093448958948e-07 + 9.95478771482819e-06 + 1.68152886823486e-06 + 1.88110947177422e-05  Fidelity = 9.99212426210662e-01  ||Grad|| = 1.25642930948482e-01
11  7.61557353561204e-04 + 6.58101033002532e-07 + 8.70257017497064e-06 + 1.67876883680432e-06 + 1.89034475229789e-05  Fidelity = 9.99238442646439e-01  ||Grad|| = 6.69505506460256e-02
12  4.73182456793730e-04 + 6.61426652432430e-07 + 9.42339604980787e-06 + 1.68858804858276e-06 + 1.89643579265023e-05  Fidelity = 9.99526817543206e-01  ||Grad|| = 1.51485434950416e-01
13  3.95575545919513e-04 + 6.60310131993940e-07 + 8.98315321277066e-06 + 1.68465132655927e-06 + 1.89536378461028e-05  Fidelity = 9.99604424454080e-01  ||Grad|| = 8.73867343864188e-02
14  3.48739503493456e-04 + 6.61442439576918e-07 + 9.15510280766156e-06 + 1.68848366215386e-06 + 1.89799128838322e-05  Fidelity = 9.99651260496507e-01  ||Grad|| = 9.87315783385475e-02
15  2.91012935661916e-04 + 6.61201425520449e-07 + 8.84685164671880e-06 + 1.68681653559863e-06 + 1.89909645636093e-05  Fidelity = 9.99708987064338e-01  ||Grad|| = 4.64544419508691e-02
16  1.92657181118050e-04 + 6.63647919434363e-07 + 8.77493563790291e-06 + 1.69364635585138e-06 + 1.90725714681128e-05  Fidelity = 9.99807342818882e-01  ||Grad|| = 5.71261898756015e-02
17  9.91305462106906e-05 + 6.68775328620279e-07 + 8.71996182732835e-06 + 1.71052471222482e-06 + 1.92443923560427e-05  Fidelity = 9.99900869453789e-01  ||Grad|| = 9.35226302043923e-02
Optimization converged with small final time cost.

Optimized Fidelity = 0.999900869453789

Plotting results...
-> Press <enter> to proceed.
/Users/guenther5/Numerics/quandary/quandary.py:1157: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
  plt.waitforbuttonpress(1);

No description has been provided for this image
In [3]:
Copied!
#
# THE LONG STORY...
#
# Quandary uses B-spline basis functions to parameterize control pulses p and q. It then solves Schroedinger's equation (or alternatively Lindblad's master equation, see 6.) for each initial state to compute the solution operator and compares it to the target unitary gate. Quandary then applies a gradient-based optimization method to optimize the B-spline coefficients that yield optimized pulses. 
# Check out help(Quandary) and help(quandary.optimize) to see all options you can set, and their default values.

# Useful optional arguments for Quandary(...) that determine how quandary.optimize operates. Here are some selected ones. See help(Quandary) for a list of all options.
#   * pcof0               (List)  : Initial guess for the optimization variables in terms of the Bspline coefficients. 
#   * pcof0_filename      (String): Load the initial guess for the Bspline coefficients from this file (Format: Text file containing the list of coefficients in one column. 
#   * initctrl_MHz        (Float) : Amplitude of the initial pulse guess (if randomized, the initial pulse amplitude will be smaller than this number). Default: 1 MHz
#   * randomize_init_ctrl (Bool)  : Switch whether initial control pulse should be a randomized pulse, or a constant pulse. Default: True (randomized). Only active if pcof0 is NOT given.
#   * rand_seed           (int)   : Fixed seed for the random number generator (Default: system time, non reproducable)
#   * control_enforce_BC  (Bool)  : Switch whether the control pulses should start and end at zero (p(t)=0 at t=0 and t=T). Default 'False'. 
#   * nsplines            (Int)   : Number of Bspline basis functions used to parameterize the pulse. Default = T/10.0 + 2, gives a 10ns spacing between the bspline basis functions. 
#   * tol_infidelity      (Float) : Targeted accuraty of the pulses in terms of the fidelity. Default '1e-4' gives a 99.99% fidelity gate). 
#   * maxiter             (Int)   : Maximum number of optimization iterations (default 100).
#   * Pmin                (Int)   : Use this many time-points to discretize one period of the solution. The higher the number, the more accurate the solution will be, and the longer the computation will take. (Default: 150)
#   * verbose             (Bool)  : Flag to print more output (default: false). Usefull for debugging.


# Optional arguments for quandary.optimize(...):
#   * pcof0       (List)    : Use this as an initial guess for the Bspline coefficients. Overwrites the field in quandary.pcof0
#   * datadir     (String)  : Output directory where Quandary is executed
#   * ncores.     (Int)     : Number of compute cores (default: <number of essential levels>, here 3 by default)
# # THE LONG STORY... # # Quandary uses B-spline basis functions to parameterize control pulses p and q. It then solves Schroedinger's equation (or alternatively Lindblad's master equation, see 6.) for each initial state to compute the solution operator and compares it to the target unitary gate. Quandary then applies a gradient-based optimization method to optimize the B-spline coefficients that yield optimized pulses. # Check out help(Quandary) and help(quandary.optimize) to see all options you can set, and their default values. # Useful optional arguments for Quandary(...) that determine how quandary.optimize operates. Here are some selected ones. See help(Quandary) for a list of all options. # * pcof0 (List) : Initial guess for the optimization variables in terms of the Bspline coefficients. # * pcof0_filename (String): Load the initial guess for the Bspline coefficients from this file (Format: Text file containing the list of coefficients in one column. # * initctrl_MHz (Float) : Amplitude of the initial pulse guess (if randomized, the initial pulse amplitude will be smaller than this number). Default: 1 MHz # * randomize_init_ctrl (Bool) : Switch whether initial control pulse should be a randomized pulse, or a constant pulse. Default: True (randomized). Only active if pcof0 is NOT given. # * rand_seed (int) : Fixed seed for the random number generator (Default: system time, non reproducable) # * control_enforce_BC (Bool) : Switch whether the control pulses should start and end at zero (p(t)=0 at t=0 and t=T). Default 'False'. # * nsplines (Int) : Number of Bspline basis functions used to parameterize the pulse. Default = T/10.0 + 2, gives a 10ns spacing between the bspline basis functions. # * tol_infidelity (Float) : Targeted accuraty of the pulses in terms of the fidelity. Default '1e-4' gives a 99.99% fidelity gate). # * maxiter (Int) : Maximum number of optimization iterations (default 100). # * Pmin (Int) : Use this many time-points to discretize one period of the solution. The higher the number, the more accurate the solution will be, and the longer the computation will take. (Default: 150) # * verbose (Bool) : Flag to print more output (default: false). Usefull for debugging. # Optional arguments for quandary.optimize(...): # * pcof0 (List) : Use this as an initial guess for the Bspline coefficients. Overwrites the field in quandary.pcof0 # * datadir (String) : Output directory where Quandary is executed # * ncores. (Int) : Number of compute cores (default: , here 3 by default)
In [4]:
Copied!
# The quandary.simulate() function has a similar interface and can be used to *simulate* the dynamics, given Bspline control coefficients that define the pulses:
opt_bspline_params = quandary.popt[:]      # Here, I'm using the previously optimized control parameters.
t, p, q, infidelity, _, _ = quandary.simulate(pcof0=opt_bspline_params)
# The quandary.simulate() function has a similar interface and can be used to *simulate* the dynamics, given Bspline control coefficients that define the pulses: opt_bspline_params = quandary.popt[:] # Here, I'm using the previously optimized control parameters. t, p, q, infidelity, _, _ = quandary.simulate(pcof0=opt_bspline_params)
Executing ' mpirun -np  3 quandary ./config.cfg --quiet . Runtype:  simulation ...
Objective = 9.91305462126890e-05 + 6.68775328620277e-07 + 8.71996182732833e-06 + 1.71052471222484e-06 + 1.92443923560427e-05
Fidelity = 9.99900869453787e-01

2. How to evaluate control pulses on a specific sample rate¶

In [5]:
Copied!
# Quandary.optimize(..) returns control pulses p and q (unit MHz) that are evaluated at each time point in t 
# In oder to evaluate p and q on a different time grid (i.e. to downsample or upsample the pulse), use the following:
samplerate = 64
t_ds, p_ds, q_ds = quandary.evalControls(pcof0=quandary.popt, points_per_ns=samplerate)

plot_pulse(quandary.Ne, t_ds, p_ds, q_ds)

# Note, p and q are lists of lists (one list for each qubit), hence accessing the first qubit's control is in p[0], e.g.
# plt.plot(t_ds, p_ds[0])  
# plt.plot(t_ds, q_ds[0])
# Quandary.optimize(..) returns control pulses p and q (unit MHz) that are evaluated at each time point in t # In oder to evaluate p and q on a different time grid (i.e. to downsample or upsample the pulse), use the following: samplerate = 64 t_ds, p_ds, q_ds = quandary.evalControls(pcof0=quandary.popt, points_per_ns=samplerate) plot_pulse(quandary.Ne, t_ds, p_ds, q_ds) # Note, p and q are lists of lists (one list for each qubit), hence accessing the first qubit's control is in p[0], e.g. # plt.plot(t_ds, p_ds[0]) # plt.plot(t_ds, q_ds[0])
Executing ' quandary ./config.cfg --quiet . Runtype:  evalcontrols ...

Plotting control pulses.
-> Press <enter> to proceed.
/Users/guenther5/Numerics/quandary/quandary.py:1012: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
  plt.waitforbuttonpress(1);

No description has been provided for this image

3. How to get the propagator and/or fidelity for given control parameters from Quandary¶

In [6]:
Copied!
# After a quandary.optimize/simulate(), the field quandary.uT contains the propagated states at the final time, for each of the initial states. 
# If the initial states are the full basis (default), then this is the solution operator / process matrix / propagator
# Here an example, simulating with previously optimized and slighly perturbed Bspline control parameters 
coeffs_perturb = np.zeros(len(quandary.popt))
for i in range(len(coeffs_perturb)):
    coeffs_perturb[i] = quandary.popt[i] + 0.001

t, p, q, infidelity, _, _ = quandary.simulate(pcof0=coeffs_perturb) 
propagator_T = quandary.uT   

# In the example here, uT is a 4x3 matrix (!) because here Quandary was configured with Ne=[3] energy states and Ng=[1] guard level. Hence uT is a 4x3 matrix whose columns are for each of the 3 initial basis states and rows are the 3+1 energy levels). 
# Hopefully, the guard levels are not populated such that you can ignore the last row to extract the 3x3 propagator:
propagator_3x3 = propagator_T[:-1,:] 

print("Propagator = ", propagator_3x3)
print(f"Pulse Fidelity = {1.0 - infidelity}")

# You can also compute the fidelity from the propagator and unitary gate directly, e.g. with
fid = np.abs(np.trace(propagator_3x3.conj().T @ unitary))**2/np.prod(quandary.Ne)**2     # ('@' for numpy matrix mult)
print('Test Pulse Fidelity = ', fid)   
# After a quandary.optimize/simulate(), the field quandary.uT contains the propagated states at the final time, for each of the initial states. # If the initial states are the full basis (default), then this is the solution operator / process matrix / propagator # Here an example, simulating with previously optimized and slighly perturbed Bspline control parameters coeffs_perturb = np.zeros(len(quandary.popt)) for i in range(len(coeffs_perturb)): coeffs_perturb[i] = quandary.popt[i] + 0.001 t, p, q, infidelity, _, _ = quandary.simulate(pcof0=coeffs_perturb) propagator_T = quandary.uT # In the example here, uT is a 4x3 matrix (!) because here Quandary was configured with Ne=[3] energy states and Ng=[1] guard level. Hence uT is a 4x3 matrix whose columns are for each of the 3 initial basis states and rows are the 3+1 energy levels). # Hopefully, the guard levels are not populated such that you can ignore the last row to extract the 3x3 propagator: propagator_3x3 = propagator_T[:-1,:] print("Propagator = ", propagator_3x3) print(f"Pulse Fidelity = {1.0 - infidelity}") # You can also compute the fidelity from the propagator and unitary gate directly, e.g. with fid = np.abs(np.trace(propagator_3x3.conj().T @ unitary))**2/np.prod(quandary.Ne)**2 # ('@' for numpy matrix mult) print('Test Pulse Fidelity = ', fid)
Executing ' mpirun -np  3 quandary ./config.cfg --quiet . Runtype:  simulation ...
Objective = 7.88478427193311e-02 + 6.06093011629377e-07 + 7.01401375410766e-06 + 1.77996722552265e-06 + 1.74331975865308e-05
Fidelity = 9.21152157280669e-01
Propagator =  [[-0.17599657+0.01194077j  0.16676533+0.1019571j  -0.93535995+0.23613982j]
 [-0.15730409+0.12076079j -0.96501199+0.0623861j  -0.10338956+0.12167315j]
 [-0.96410644-0.00675028j  0.13544784+0.09094835j  0.20602539-0.03698267j]]
Pulse Fidelity = 0.921152157280669
Test Pulse Fidelity =  0.9211521572810436

4. How to compare results to QuTIP?¶

In [7]:
Copied!
# Here is an example for comparing Quandary's prediction to simulation with QuTIP.
from qutip import Qobj, propagator

# First, I'm using Quandary to simulate the dynamics driven by the above perturbed Bspline coefficients, while modelling the first 3 energy levels
quandary = Quandary(Ne=[3], Ng=[0], targetgate=unitary, T=pulse_length)
t, p, q, infidelity, _, _ = quandary.simulate(pcof0=coeffs_perturb) 
# Now, I'm extracting the propagator and compute the fidelity with respect to the unitary target defined above
propagator_quandary = quandary.uT
fidelity_quandary = np.abs(np.trace(propagator_quandary.conj().T @ unitary))**2/np.prod(quandary.Ne)**2 
print("Quandary fidelity: ", fidelity_quandary)

# QuTip requires pulses to be defined in rad/ns, so here I convert the output from quandary from MHz to rad / ns
p_radns = np.array(p[0])*1e-3*(2*np.pi)
q_radns = np.array(q[0])*1e-3*(2*np.pi)

# For some reason, QuTIP can not handle the time variable t from Quandary, so we set up a new one here...
samplerate = quandary.nsteps / quandary.T     # Sample rate of controls from Quandary
t_new = np.arange(0, len(p[0]))*1/samplerate

# Define the Hamiltonian model for QuTIP. Here, I'm taking the Hamiltonians as used in Quandary
h0 = quandary.Hsys              # System Hamiltonian
hc_re = quandary.Hc_re[0]       # Control Hamiltonian for first qubit, real part
hc_im = 1.0j*quandary.Hc_im[0]  # Control Hamiltonian for first qubit, imaginary part

# Solve Hamiltonian dynamics with Qutip
H = [Qobj(h0), [Qobj(hc_re), p_radns], [Qobj(hc_im), q_radns]]
U_all = propagator(H, t_new)

# Grab last time-step (propagator)
nstates = np.prod(quandary.Ne)
propagator_qutip = Qobj(U_all[-1][:nstates,:nstates])

# Compute the fidelity with the target unitary 
fidelity_qutip = np.abs(np.trace(propagator_qutip.dag() * Qobj(unitary)))**2/nstates**2
print('QuTIP fidelity: ', fidelity_qutip)

# Check: Both fidelities should be fairly close to each other.
# Here is an example for comparing Quandary's prediction to simulation with QuTIP. from qutip import Qobj, propagator # First, I'm using Quandary to simulate the dynamics driven by the above perturbed Bspline coefficients, while modelling the first 3 energy levels quandary = Quandary(Ne=[3], Ng=[0], targetgate=unitary, T=pulse_length) t, p, q, infidelity, _, _ = quandary.simulate(pcof0=coeffs_perturb) # Now, I'm extracting the propagator and compute the fidelity with respect to the unitary target defined above propagator_quandary = quandary.uT fidelity_quandary = np.abs(np.trace(propagator_quandary.conj().T @ unitary))**2/np.prod(quandary.Ne)**2 print("Quandary fidelity: ", fidelity_quandary) # QuTip requires pulses to be defined in rad/ns, so here I convert the output from quandary from MHz to rad / ns p_radns = np.array(p[0])*1e-3*(2*np.pi) q_radns = np.array(q[0])*1e-3*(2*np.pi) # For some reason, QuTIP can not handle the time variable t from Quandary, so we set up a new one here... samplerate = quandary.nsteps / quandary.T # Sample rate of controls from Quandary t_new = np.arange(0, len(p[0]))*1/samplerate # Define the Hamiltonian model for QuTIP. Here, I'm taking the Hamiltonians as used in Quandary h0 = quandary.Hsys # System Hamiltonian hc_re = quandary.Hc_re[0] # Control Hamiltonian for first qubit, real part hc_im = 1.0j*quandary.Hc_im[0] # Control Hamiltonian for first qubit, imaginary part # Solve Hamiltonian dynamics with Qutip H = [Qobj(h0), [Qobj(hc_re), p_radns], [Qobj(hc_im), q_radns]] U_all = propagator(H, t_new) # Grab last time-step (propagator) nstates = np.prod(quandary.Ne) propagator_qutip = Qobj(U_all[-1][:nstates,:nstates]) # Compute the fidelity with the target unitary fidelity_qutip = np.abs(np.trace(propagator_qutip.dag() * Qobj(unitary)))**2/nstates**2 print('QuTIP fidelity: ', fidelity_qutip) # Check: Both fidelities should be fairly close to each other.
Executing ' mpirun -np  3 quandary ./config.cfg --quiet . Runtype:  simulation ...
Objective = 7.89591322635279e-02 + 6.06093011629377e-07 + 0.00000000000000e+00 + 1.77635445639020e-06 + 1.74332248307182e-05
Fidelity = 9.21040867736472e-01
Quandary fidelity:  0.9210408677312315
QuTIP fidelity:  0.9210922905233985

5. How to use Quandary to generate pulses that realize a state-to-state transfer¶

In [8]:
Copied!
# 
# THE SHORT STORY
# 
# Example how to use Quandary to optimize a state-to-state transfer 

# Optionally: Define the number of essential state and guard levels. This example considers a 2-level qubit with no extra guard level. Default would be 3-level and no guard level, so we change it here
Ne = [2]    # Number of essential states per qubit 

# Define the target and the initial state (in the essential level dimensions)
initialcondition = [1.0, 0.0]
targetstate =  [1.0/np.sqrt(2), 1.0/np.sqrt(2)]   

# Pulse length
T = 100.0

# Optionally: Define desired accuraty of the pulse in terms of the infidelity. 
tol_infidelity = 1e-5

# Optionally: Set the initial control pulse amplitude. Default is 10MHz, leading to larger controls than neccessary here, so we decrease it here.
initctrl_MHz=0.1

# Pass all options to the Quandary configuration
quandary = Quandary(Ne=Ne, T=T, targetstate=targetstate, initialcondition=initialcondition, tol_infidelity=tol_infidelity, initctrl_MHz=initctrl_MHz, rand_seed=1234)

# Execute quandary. Runs in the background on one core. 
t, pt, qt, infidelity, expectedEnergy, population = quandary.optimize() 
print(f"\nFidelity = {1.0 - infidelity}")

# The realized state at the final time is in quandary.uT
realized_state= quandary.uT

# Plot the control pulse and expected energy level evolution.
plot_results_1osc(quandary, pt[0], qt[0], expectedEnergy[0], population[0])
# # THE SHORT STORY # # Example how to use Quandary to optimize a state-to-state transfer # Optionally: Define the number of essential state and guard levels. This example considers a 2-level qubit with no extra guard level. Default would be 3-level and no guard level, so we change it here Ne = [2] # Number of essential states per qubit # Define the target and the initial state (in the essential level dimensions) initialcondition = [1.0, 0.0] targetstate = [1.0/np.sqrt(2), 1.0/np.sqrt(2)] # Pulse length T = 100.0 # Optionally: Define desired accuraty of the pulse in terms of the infidelity. tol_infidelity = 1e-5 # Optionally: Set the initial control pulse amplitude. Default is 10MHz, leading to larger controls than neccessary here, so we decrease it here. initctrl_MHz=0.1 # Pass all options to the Quandary configuration quandary = Quandary(Ne=Ne, T=T, targetstate=targetstate, initialcondition=initialcondition, tol_infidelity=tol_infidelity, initctrl_MHz=initctrl_MHz, rand_seed=1234) # Execute quandary. Runs in the background on one core. t, pt, qt, infidelity, expectedEnergy, population = quandary.optimize() print(f"\nFidelity = {1.0 - infidelity}") # The realized state at the final time is in quandary.uT realized_state= quandary.uT # Plot the control pulse and expected energy level evolution. plot_results_1osc(quandary, pt[0], qt[0], expectedEnergy[0], population[0])
Executing ' quandary ./config.cfg --quiet . Runtype:  optimization ...
    Objective             Tikhonov                Penalty-Leakage        Penalty-StateVar       Penalty-TotalEnergy 
0  4.92896546738189e-01 + 2.21008178502730e-10 + 0.00000000000000e+00 + 5.71613862622368e-15 + 8.27271005826681e-09  Fidelity = 5.07103453261811e-01  ||Grad|| = 1.70078108093121e+01
1  4.24919496932892e-02 + 1.70616509371277e-07 + 0.00000000000000e+00 + 1.50086380611334e-09 + 9.91369542882719e-06  Fidelity = 9.57508050306711e-01  ||Grad|| = 6.86209639801545e+00
2  5.73631878829173e-03 + 8.72278162710343e-08 + 0.00000000000000e+00 + 7.56210130921460e-10 + 5.06663232177590e-06  Fidelity = 9.94263681211708e-01  ||Grad|| = 2.56875562610845e+00
3  2.45814323485138e-06 + 1.07188551686189e-07 + 0.00000000000000e+00 + 9.74194815930997e-10 + 6.22689130835045e-06  Fidelity = 9.99997541856765e-01  ||Grad|| = 5.06614324661478e-02
Optimization converged with small infidelity.

Fidelity = 0.999997541856765

Plotting results...
-> Press <enter> to proceed.
/Users/guenther5/Numerics/quandary/quandary.py:1157: UserWarning: Matplotlib is currently using module://matplotlib_inline.backend_inline, which is a non-GUI backend, so cannot show the figure.
  plt.waitforbuttonpress(1);

No description has been provided for this image
In [9]:
Copied!
#
# THE LONG STORY... compare above long story on gate optimization, here continued and adapted for state-to-state transfer
#

# In contrast to gate optimization, the state-to-state transfer only requires to propagate ONE initial state through the time domain, not the entire basis of initial states. The objective function compares the propagated initial state to the target, and performs an optimization on this measure.
# When setting up a state-to-state problem, the only difference to the gate optimization is therefore that, instead of a "targetgate", one passes the keyword "targetstate" (vector) to the configuration, alongside an "initialcondition" identifier for selecting an arbitrary initial state at t=0.
# All other configuration options as explained above are still available and have the same effect as they do for gate optimization. 

# Important note: After solving for a state-to-state problem (optimization or simulation), the variable quandary.uT consists only of ONE column, being the propagated initial state at final time t. 
# Hence, quandary.uT is NOT THE PROPAGATOR! (Since only one initial state has been propagated, the process matrix is not available yet)
# To compute the propagator /solution matrix / unitary that maps the initial to the target state, one needs to simulate the dynamics for all initial states, using the optimized pulses,  e.g. by doing so:
quandary.initialcondition = "basis"
quandary.update()   # call update() after you have changed quandary member variables directly as above
t, pt, qt, _, _, _ = quandary.simulate(pcof0=quandary.popt, datadir="./test")
# Note: The fidelity that is returned and printed out in this call, is MEANINGLESS. Ignore it. Instead, compute fidelity from the blow code.

# Now grab the propagator
uT = quandary.uT
# print("Propagator: ", uT)

# TEST the fidelity: Check whether uT indeed maps the initial [1,0] state to the desired target state:
test_fid = np.abs(np.array(targetstate).conj() @ uT @ initialcondition)   # Overlap between target state and evolved state
print("Test fidelity = ", test_fid)
# # THE LONG STORY... compare above long story on gate optimization, here continued and adapted for state-to-state transfer # # In contrast to gate optimization, the state-to-state transfer only requires to propagate ONE initial state through the time domain, not the entire basis of initial states. The objective function compares the propagated initial state to the target, and performs an optimization on this measure. # When setting up a state-to-state problem, the only difference to the gate optimization is therefore that, instead of a "targetgate", one passes the keyword "targetstate" (vector) to the configuration, alongside an "initialcondition" identifier for selecting an arbitrary initial state at t=0. # All other configuration options as explained above are still available and have the same effect as they do for gate optimization. # Important note: After solving for a state-to-state problem (optimization or simulation), the variable quandary.uT consists only of ONE column, being the propagated initial state at final time t. # Hence, quandary.uT is NOT THE PROPAGATOR! (Since only one initial state has been propagated, the process matrix is not available yet) # To compute the propagator /solution matrix / unitary that maps the initial to the target state, one needs to simulate the dynamics for all initial states, using the optimized pulses, e.g. by doing so: quandary.initialcondition = "basis" quandary.update() # call update() after you have changed quandary member variables directly as above t, pt, qt, _, _, _ = quandary.simulate(pcof0=quandary.popt, datadir="./test") # Note: The fidelity that is returned and printed out in this call, is MEANINGLESS. Ignore it. Instead, compute fidelity from the blow code. # Now grab the propagator uT = quandary.uT # print("Propagator: ", uT) # TEST the fidelity: Check whether uT indeed maps the initial [1,0] state to the desired target state: test_fid = np.abs(np.array(targetstate).conj() @ uT @ initialcondition) # Overlap between target state and evolved state print("Test fidelity = ", test_fid)
Executing ' mpirun -np  2 quandary ./config.cfg --quiet . Runtype:  simulation ...
Objective = 7.50693232217165e-01 + 1.07188551686190e-07 + 0.00000000000000e+00 + 9.74194815931074e-10 + 6.22689130835046e-06
Fidelity = 2.49306767782835e-01
Test fidelity =  0.999998770928759

6. How to use the open-system solver to model decay and dephasing¶

In [10]:
Copied!
# You can simulate the dynamics under Lindbladian noise operators in Quandary (for decay and dephasing noise).
# To do so, the only thing to change is to pass the decay and dephasing times to the quandary configuration. All other options and runtypes are the same. 
# Note that the Lindblad solver is computationally more expensive, especially for gate optimization. It might be good to pre-optimize on the closed system first and use the optimized parameters as initial guess for the Lindblad solver.
# Here is an example for the state-to-state transfer problem

# State-to-state transfer configuration
Ne = [2]    # Number of essential states
targetstate  =  [1.0/np.sqrt(2), 1.0/np.sqrt(2)]   # target state
initialstate = [1.0, 0.0]      # initial ground state
T = 100.0                      # pulse length
tol_infidelity = 1e-5          # Infidelity tolerance
maxiter = 50                   # Maximum number of iterations

maxctrl_MHz = 4.0              # Maximum allowed control pulse amplitude [MHz]
initctrl_MHz = 0.1             # Initial guess for control pulse amplitude [MHz]

T1 = [100000.0]  # T1 decay time [ns]
T2 = [80000.0]   # T2 dephasing time [ns]
quandary_lblad = Quandary(Ne=Ne, T=T, T1=T1, T2=T2, targetstate=targetstate, initialcondition=initialstate, maxctrl_MHz=maxctrl_MHz, initctrl_MHz=initctrl_MHz, maxiter=maxiter)

# Create and pre-optimize on the closed-system solver:
quandary_closed = quandary_lblad.copy()
quandary_closed.T1=[]   # disable T1 and T2, don't forget to update() quandary
quandary_closed.T2=[]
quandary_closed.update()
t, pt, qt, infidelity, expectedEnergy, population = quandary_closed.optimize() 
print(f"\nOptimized Fidelity = {1.0 - infidelity}")

# Evaluate the optimized pulses using the open system solver:
t, pt, qt, infidelity, expectedEnergy, population = quandary_lblad.simulate(pcof0=quandary_closed.popt) 
print(f"\nFidelity under noise = {1.0 - infidelity}")

# Further optimize on the lindblad solver
t, pt, qt, infidelity, expectedEnergy, population = quandary_lblad.optimize(pcof0=quandary_closed.popt) 
print(f"\nRe-optimized Fidelity under noise = {1.0 - infidelity}")

# # Plot the control pulse and expected energy level evolution.
plot_results_1osc(quandary_lblad, pt[0], qt[0], expectedEnergy[0], population[0])
# You can simulate the dynamics under Lindbladian noise operators in Quandary (for decay and dephasing noise). # To do so, the only thing to change is to pass the decay and dephasing times to the quandary configuration. All other options and runtypes are the same. # Note that the Lindblad solver is computationally more expensive, especially for gate optimization. It might be good to pre-optimize on the closed system first and use the optimized parameters as initial guess for the Lindblad solver. # Here is an example for the state-to-state transfer problem # State-to-state transfer configuration Ne = [2] # Number of essential states targetstate = [1.0/np.sqrt(2), 1.0/np.sqrt(2)] # target state initialstate = [1.0, 0.0] # initial ground state T = 100.0 # pulse length tol_infidelity = 1e-5 # Infidelity tolerance maxiter = 50 # Maximum number of iterations maxctrl_MHz = 4.0 # Maximum allowed control pulse amplitude [MHz] initctrl_MHz = 0.1 # Initial guess for control pulse amplitude [MHz] T1 = [100000.0] # T1 decay time [ns] T2 = [80000.0] # T2 dephasing time [ns] quandary_lblad = Quandary(Ne=Ne, T=T, T1=T1, T2=T2, targetstate=targetstate, initialcondition=initialstate, maxctrl_MHz=maxctrl_MHz, initctrl_MHz=initctrl_MHz, maxiter=maxiter) # Create and pre-optimize on the closed-system solver: quandary_closed = quandary_lblad.copy() quandary_closed.T1=[] # disable T1 and T2, don't forget to update() quandary quandary_closed.T2=[] quandary_closed.update() t, pt, qt, infidelity, expectedEnergy, population = quandary_closed.optimize() print(f"\nOptimized Fidelity = {1.0 - infidelity}") # Evaluate the optimized pulses using the open system solver: t, pt, qt, infidelity, expectedEnergy, population = quandary_lblad.simulate(pcof0=quandary_closed.popt) print(f"\nFidelity under noise = {1.0 - infidelity}") # Further optimize on the lindblad solver t, pt, qt, infidelity, expectedEnergy, population = quandary_lblad.optimize(pcof0=quandary_closed.popt) print(f"\nRe-optimized Fidelity under noise = {1.0 - infidelity}") # # Plot the control pulse and expected energy level evolution. plot_results_1osc(quandary_lblad, pt[0], qt[0], expectedEnergy[0], population[0])
Executing ' quandary ./config.cfg --quiet . Runtype:  optimization ...
    Objective             Tikhonov                Penalty-Leakage        Penalty-StateVar       Penalty-TotalEnergy 
0  4.95093877640180e-01 + 2.37157458641785e-10 + 0.00000000000000e+00 + 8.48886922237297e-15 + 6.18295381973677e-09  Fidelity = 5.04906122359820e-01  ||Grad|| = 1.06050793701288e-01
1  4.33637442278156e-02 + 1.71295862475575e-07 + 0.00000000000000e+00 + 1.18029933501381e-09 + 9.93549045871399e-06  Fidelity = 9.56636255772184e-01  ||Grad|| = 2.91008508681472e-01
2  5.89641442782529e-03 + 8.69492943530913e-08 + 0.00000000000000e+00 + 5.60516124641424e-10 + 5.03812649992486e-06  Fidelity = 9.94103585572175e-01  ||Grad|| = 7.20595410555997e-02
3  2.84392818250545e-06 + 1.07170517944485e-07 + 0.00000000000000e+00 + 7.32340225375347e-10 + 6.21216158866204e-06  Fidelity = 9.99997156071817e-01  ||Grad|| = 5.83831714479165e-02
Optimization converged with small infidelity.

Optimized Fidelity = 0.999997156071817
Executing ' quandary ./config.cfg --quiet . Runtype:  simulation ...
Warning: Disabling DpDm penalty term because it is not implemented for the Lindblad solver.
Objective = 2.14758317001906e-04 + 1.07170517944485e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.21216158866204e-06
Fidelity = 9.99785241682998e-01

Fidelity under noise = 0.999785241682998
Executing ' quandary ./config.cfg --quiet . Runtype:  optimization ...
Warning: Disabling DpDm penalty term because it is not implemented for the Lindblad solver.
    Objective             Tikhonov                Penalty-Leakage        Penalty-StateVar       Penalty-TotalEnergy 
0  2.14758317001906e-04 + 1.07170517944485e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.21216158866204e-06  Fidelity = 9.99785241682998e-01  ||Grad|| = 5.39149435753235e-02
1  2.12423971066578e-04 + 1.06778486013778e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.18940475712374e-06  Fidelity = 9.99787576028933e-01  ||Grad|| = 1.09789419538907e-02
2  2.12250733333974e-04 + 1.06826550568956e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.19219443168842e-06  Fidelity = 9.99787749266666e-01  ||Grad|| = 5.53320435783508e-03
3  2.12213191302935e-04 + 1.06814092112584e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.19147292048699e-06  Fidelity = 9.99787786808697e-01  ||Grad|| = 3.71896904630812e-03
4  2.12183163358581e-04 + 1.06808885834259e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.19117381114947e-06  Fidelity = 9.99787816836641e-01  ||Grad|| = 3.83550407544338e-03
5  2.12026100095852e-04 + 1.06793349804942e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.19029154949504e-06  Fidelity = 9.99787973899904e-01  ||Grad|| = 7.31012487911435e-03
6  2.11682899912358e-04 + 1.06778037657151e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.18944595510475e-06  Fidelity = 9.99788317100088e-01  ||Grad|| = 1.36218077857303e-02
7  2.10698123945918e-04 + 1.06779685549764e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.18962935646316e-06  Fidelity = 9.99789301876054e-01  ||Grad|| = 2.48732199898963e-02
8  2.08221306690293e-04 + 1.06936237527069e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.19862726087915e-06  Fidelity = 9.99791778693310e-01  ||Grad|| = 4.08180516236985e-02
9  2.01906188945111e-04 + 1.08181381313171e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.26847057712236e-06  Fidelity = 9.99798093811055e-01  ||Grad|| = 6.07845381783005e-02
10  1.86962443737282e-04 + 1.15713598532208e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 6.68592417968190e-06  Fidelity = 9.99813037556263e-01  ||Grad|| = 8.43698893019401e-02
11  1.49460540591617e-04 + 1.55608893098280e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 8.88990313459233e-06  Fidelity = 9.99850539459408e-01  ||Grad|| = 9.87028085318270e-02
12  1.48887068897485e-04 + 1.56872913645619e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 8.96005479724974e-06  Fidelity = 9.99851112931103e-01  ||Grad|| = 9.92417984572364e-02
13  1.46530851866222e-04 + 1.52340834573303e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 8.70227265223371e-06  Fidelity = 9.99853469148134e-01  ||Grad|| = 9.05638927310559e-02
14  1.45596334769182e-04 + 1.53121256075052e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 8.74671601964315e-06  Fidelity = 9.99854403665231e-01  ||Grad|| = 9.14051089374446e-02
15  1.39975365522127e-04 + 1.61473050328333e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.21940708389186e-06  Fidelity = 9.99860024634478e-01  ||Grad|| = 6.44023684155498e-02
16  1.19700662958744e-04 + 1.55984017063422e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 8.91013360139265e-06  Fidelity = 9.99880299337041e-01  ||Grad|| = 3.46919842480126e-02
17  1.18007704666634e-04 + 1.56557568418006e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 8.94219211168499e-06  Fidelity = 9.99881992295333e-01  ||Grad|| = 1.58546131312844e-02
18  1.17759738685663e-04 + 1.57088433984895e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 8.97202673826110e-06  Fidelity = 9.99882240261314e-01  ||Grad|| = 1.83491298969505e-02
19  1.16240083980701e-04 + 1.60055296341643e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.13802673733906e-06  Fidelity = 9.99883759916019e-01  ||Grad|| = 2.50123772254469e-02
20  1.14594216023445e-04 + 1.63287735293982e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.31802763286631e-06  Fidelity = 9.99885405783977e-01  ||Grad|| = 2.93134237414353e-02
21  1.14251377695496e-04 + 1.64348856169099e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.37678635636461e-06  Fidelity = 9.99885748622305e-01  ||Grad|| = 3.16787323315978e-02
22  1.13997751412520e-04 + 1.63039097661612e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.30483495198634e-06  Fidelity = 9.99886002248587e-01  ||Grad|| = 2.09093043782508e-02
23  1.13499863252042e-04 + 1.65093571515599e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.41725517469969e-06  Fidelity = 9.99886500136748e-01  ||Grad|| = 2.91873384654756e-02
24  1.13275576079097e-04 + 1.64240334776172e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.37015039555035e-06  Fidelity = 9.99886724423921e-01  ||Grad|| = 2.04023988336894e-02
25  1.13225361271119e-04 + 1.64395756335483e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.37870105107991e-06  Fidelity = 9.99886774638729e-01  ||Grad|| = 2.10860742881605e-02
26  1.12566174633510e-04 + 1.65658826642208e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.44773665734352e-06  Fidelity = 9.99887433825366e-01  ||Grad|| = 2.08002501459426e-02
27  1.10662226557867e-04 + 1.73745908534151e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.88192846117780e-06  Fidelity = 9.99889337773442e-01  ||Grad|| = 3.62056579936783e-02
28  1.09511691009478e-04 + 1.71015180819378e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.73811790299958e-06  Fidelity = 9.99890488308991e-01  ||Grad|| = 1.57254901708267e-02
29  1.08840191136106e-04 + 1.73556278771688e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 9.87061443629078e-06  Fidelity = 9.99891159808864e-01  ||Grad|| = 2.45556822608423e-02
30  1.07366895488847e-04 + 1.78255951117966e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.01101353959995e-05  Fidelity = 9.99892633104511e-01  ||Grad|| = 3.15008906820837e-02
31  1.06244974780068e-04 + 1.80621875096305e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.02334610645912e-05  Fidelity = 9.99893755025220e-01  ||Grad|| = 2.71205315041929e-02
32  1.05435566826739e-04 + 1.82885554944246e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.03502390753119e-05  Fidelity = 9.99894564433173e-01  ||Grad|| = 2.76769977335678e-02
33  1.04186514594651e-04 + 1.86846894915238e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.05503468500324e-05  Fidelity = 9.99895813485405e-01  ||Grad|| = 2.86917473120161e-02
34  1.03785376323384e-04 + 1.85771637060866e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.04969339575101e-05  Fidelity = 9.99896214623677e-01  ||Grad|| = 1.54608914268844e-02
35  1.03639305514491e-04 + 1.86165244796557e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.05161314242468e-05  Fidelity = 9.99896360694486e-01  ||Grad|| = 1.48320607002443e-02
36  1.03437558504171e-04 + 1.86857155750518e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.05498843228338e-05  Fidelity = 9.99896562441496e-01  ||Grad|| = 1.51823436224797e-02
37  1.03200131288617e-04 + 1.88058804437293e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.06080832729608e-05  Fidelity = 9.99896799868711e-01  ||Grad|| = 1.74523313519526e-02
38  1.03167922190117e-04 + 1.87328155811167e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.05720156203587e-05  Fidelity = 9.99896832077810e-01  ||Grad|| = 1.06311586238518e-02
39  1.02867134457174e-04 + 1.88496768565161e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.06284083320156e-05  Fidelity = 9.99897132865543e-01  ||Grad|| = 1.15271871638762e-02
40  1.02137312413153e-04 + 1.91831029122539e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.07899750967027e-05  Fidelity = 9.99897862687587e-01  ||Grad|| = 1.66041154524786e-02
41  1.01350939942124e-04 + 1.99902164023850e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.11699795078446e-05  Fidelity = 9.99898649060058e-01  ||Grad|| = 3.09626408100503e-02
42  1.00699660739112e-04 + 1.97023413650945e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.10400646717979e-05  Fidelity = 9.99899300339261e-01  ||Grad|| = 1.41744273531428e-02
43  1.00476895569135e-04 + 1.97982286255871e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.10825806557360e-05  Fidelity = 9.99899523104431e-01  ||Grad|| = 1.30487974130799e-02
44  1.00453200222228e-04 + 1.97399251827149e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.10561471024034e-05  Fidelity = 9.99899546799778e-01  ||Grad|| = 7.39627616844095e-03
45  1.00369825903557e-04 + 1.98307965379223e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.10964592431733e-05  Fidelity = 9.99899630174096e-01  ||Grad|| = 1.15699285560317e-02
46  1.00315100244996e-04 + 1.97916019967997e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.10782081097793e-05  Fidelity = 9.99899684899755e-01  ||Grad|| = 4.79870371037332e-03
47  9.97161954650050e-05 + 2.01074870645030e-07 + 0.00000000000000e+00 + 0.00000000000000e+00 + 1.12126410445448e-05  Fidelity = 9.99900283804535e-01  ||Grad|| = 4.82581449151624e-03
Optimization converged with small final time cost.
Re-optimized Fidelity under noise = 0.999900283804535

Plotting results...
-> Press <enter> to proceed.

No description has been provided for this image
In [ ]:
Copied!
# ## FAQs

# * What to do if the optimizer does not converge?
#     - Run again with a different initial guess (rand_seed=<different integer>, randomize_ctrl_init=True/False, initctrl_MHz=<double>)
#     - Remove or increase the control bounds (maxctrl_MHz=<larger double or list>)
#     - Increase the time domain (T)
#     - Increase the number of iterations (maxiter)
#     - Increase the number of Bspline basis functions (increase nsplines, or decrease dtau)
#     - Inspect the files written to './run_dir/' (or the user-defined alternative output directory). In particular config.cfg, targetgate/state.dat.

# * What to do if the results don't match to other software, or to my expectations?
#     - Turn on 'verbose=True' and check screen output of quandary.simulate(). Anything suspicious?
#     - Test if the time-step size is small enough (aka if "Pmin" is large enough):
#       Compare the outcome (uT or fidelity) of various simulation runs, for increasing numbers of time-steps (increasing "Pmin"). The fidelity amongst those runs should not vary much. If it does, the time-step size should be decreased (by using a larger "Pmin"). Have a look at the 'timestep_richardson_est' function in quandary.py, which might be helpful to do this test.  
#     - Check if the correct pulses are used (e.g. by plotting the returned p and q after "simulation" runs as well as after calling 'evalControl(..)')

# Any time: Please reach out to guenther5@llnl.gov if you have any questions, concerns, or if you found a bug.  
# ## FAQs # * What to do if the optimizer does not converge? # - Run again with a different initial guess (rand_seed=, randomize_ctrl_init=True/False, initctrl_MHz=) # - Remove or increase the control bounds (maxctrl_MHz=) # - Increase the time domain (T) # - Increase the number of iterations (maxiter) # - Increase the number of Bspline basis functions (increase nsplines, or decrease dtau) # - Inspect the files written to './run_dir/' (or the user-defined alternative output directory). In particular config.cfg, targetgate/state.dat. # * What to do if the results don't match to other software, or to my expectations? # - Turn on 'verbose=True' and check screen output of quandary.simulate(). Anything suspicious? # - Test if the time-step size is small enough (aka if "Pmin" is large enough): # Compare the outcome (uT or fidelity) of various simulation runs, for increasing numbers of time-steps (increasing "Pmin"). The fidelity amongst those runs should not vary much. If it does, the time-step size should be decreased (by using a larger "Pmin"). Have a look at the 'timestep_richardson_est' function in quandary.py, which might be helpful to do this test. # - Check if the correct pulses are used (e.g. by plotting the returned p and q after "simulation" runs as well as after calling 'evalControl(..)') # Any time: Please reach out to guenther5@llnl.gov if you have any questions, concerns, or if you found a bug.
In [ ]:
Copied!


Documentation built with MkDocs.

Search

From here you can search these documents. Enter your search terms below.

Keyboard Shortcuts

Keys Action
? Open this help
n Next page
p Previous page
s Search