def config_dynamics(dyn, verbosity=None): """ Configure the dynamics generators and targets etc """ if verbosity is None: verbosity = dyn.config.verbosity def printv(msg, verb_tresh=1): if verbosity >= verb_tresh: print(msg) nq = dyn.num_qubits # If the 5 qubit specific configurator is available # then run cross checks cross_check = False if nq == 5: try: import qsonq5 cross_check = True except: printv("No qsonq5. Cross checks will be skipped") # **************************************************************** # Define the physics of the problem fid_comp = dyn.fid_computer # ***** Drift ***** printv("Configuring drift for {} qubits".format(nq)) # If automatic generation of the hspace order is specified # and there are no random elements, then generate it now as it is # is used in constructing the drift. # It there are random elements, then it is regenerated, along with the # drift, for each repetition, so pointless doing it now. if dyn.auto_hspace and (dyn.hspace_0_idx >= 0 and dyn.hspace_01_sep >= 0): dyn.hspace_order = qso.get_coupling_hspace(dyn.num_qubits, dyn.hspace_0_idx, dyn.hspace_01_sep) printv("using hspace_order = {}".format(dyn.hspace_order)) H_d = qso.get_drift(dyn, verbosity=verbosity) printv("Drift dims {}".format(H_d.dims)) if cross_check: #Check drift printv("Cross-checking drift") H_d_check = qsonq5.get_drift(dyn) if H_d_check is None: printv("Cannot compare drift") else: assert_equal(H_d.dims, H_d_check.dims) printv("Drift dims checked") assert_equal(H_d.full(), H_d_check.full()) printv("Drift elements checked") # Normalise based on ising chain printv("Getting Ising chain drift for normalisation") H_d_ising_chain = qso.get_drift(dyn, topology='chain', interact='ising', coup_const=1.0, verbosity=verbosity) norm_fact = H_d_ising_chain.norm() / H_d.norm() printv("Normalising drift with factor {}".format(norm_fact)) H_d = H_d*norm_fact # **** Controls **** H_c, Sx_cidx, Sy_cidx, Sz_cidx = qso.get_ctrls(dyn) if cross_check: #Check controls printv("Cross-checking controls") H_c_check, xc, yc, zc = qsonq5.get_ctrls(dyn) for j, H in enumerate(H_c): assert_equal(H.full(), H_c_check[j].full(), "Control {} is not equal".format(j)) printv("Control {} checked".format(j+1)) assert_equal(Sx_cidx, xc, "Sx indexes not matching") assert_equal(Sy_cidx, yc, "Sy indexes not matching") assert_equal(Sz_cidx, zc, "Sz indexes not matching") printv("Control indexes checked") #t0 evo U_0 = qso.get_initial_op(dyn) if cross_check: #Check initial printv("Cross-checking initial") U_0_check = qsonq5.get_initial_op(dyn) assert_equal(U_0.dims, U_0_check.dims) printv("Initial op dims checked") assert_equal(U_0.full(), U_0_check.full()) printv("Initial op elements checked") #*** Target **** U_targ, U_local_targs = qso.get_target(dyn) if cross_check: printv("Cross-checking target") U_targ_check = qsonq5.get_target(dyn) assert_equal(U_targ.dims, U_targ_check.dims) printv("Target dims checked") assert_equal(U_targ.full(), U_targ_check.full()) printv("Target elements checked") sub_dims = [] for k in range(len(U_local_targs)): sub_dims.append(U_local_targs[k].dims[0][0]) printv("target set with subsys dims: {}".format(sub_dims)) #Enforcing all dimensions are correct for the qBranch code #Not all of these are needed, but this is safer for k in range(len(H_c)): H_c[k].dims = [sub_dims, sub_dims] H_d.dims = [sub_dims, sub_dims] U_0.dims = [sub_dims, sub_dims] U_targ.dims = [sub_dims, sub_dims] dyn.drift_dyn_gen = H_d dyn.ctrl_dyn_gen = H_c dyn.initial = U_0 dyn.target = U_targ # These are the indexes to the controls on the three axes dyn.Sx_cidx = Sx_cidx dyn.Sy_cidx = Sy_cidx dyn.Sz_cidx = Sz_cidx fid_comp.U_local_targs = U_local_targs fid_comp.sub_dims = sub_dims fid_comp.num_sub_sys = len(U_local_targs) printv("Num acc: {}".format(fid_comp.numer_acc))
def run_qso_sims(optim, num_reps=None, verbosity=None, report_phys_params=None, save_results=None, scen_idx=None, reps_idx=None, num_threads=None, num_tslots=None, evo_time=None, fid_err_targ=None, numer_acc=None): """ Attempts a pulse optimisation for specified number of repititions (num_reps). Where kwargs are not passed the value is taken from the configuration, except scen_idx and reps_idx which are only used in output file names. This function is called from within the main top-level functions of this module. Returns ------- multires : MultiRepResult Containing RepResult object for each repetition. The analysis is run on multires, so the averaged statics are available as attributes. """ #print("run_qso_sims\nnum_reps {}, job_idx {}".format(num_reps, job_idx)) cfg = optim.config dyn = optim.dynamics tc = optim.termination_conditions fid_comp = dyn.fid_computer pgen = optim.pulse_generator cfg_str = qso.get_cfg_str(optim, num_tslots=num_tslots, evo_time=evo_time, fid_err_targ=fid_err_targ, numer_acc=numer_acc) out_file_ext = qso.get_out_file_ext(cfg.data_file_ext, job_id=cfg.job_id, scen_idx=scen_idx, reps_idx=reps_idx) if num_reps is None: num_reps = cfg.num_reps if verbosity is None: verbosity = cfg.verbosity if report_phys_params is None: report_phys_params = cfg.report_phys_params if save_results is None: save_results = cfg.save_results if num_threads is None: num_threads = cfg.num_threads if num_tslots is not None: dyn.num_tslots = num_tslots pgen.num_tslots = num_tslots pgen.tau = None if evo_time is not None: dyn.evo_time = evo_time pgen.pulse_time = evo_time pgen.tau = None if fid_err_targ is not None: tc.fid_err_targ = fid_err_targ if numer_acc is not None: fid_comp.numer_acc = numer_acc # Only use stdout for logging messages when first process # (which is true when the idx vars are both None or 0) base_log = True if scen_idx is not None or reps_idx is not None: datetimestamp = datetime.datetime.now().strftime('%d%b_%H-%M') script_name = "{}-{}.{}".format(cfg_str, datetimestamp, out_file_ext) script_path = os.path.join(cfg.output_dir, script_name) lfh = open(script_path, 'a') base_log = False else: lfh = sys.stdout if verbosity > 0: lfh.write("want {} threads per rep\n".format(num_threads)) try: import mkl use_mkl = True except: use_mkl = False if use_mkl: mkl.set_num_threads(num_threads) if verbosity > 0: lfh.write("Number of threads set as {}\n".format( mkl.get_max_threads())) else: if verbosity > 0: lfh.write("mkl unavailable\n") if verbosity > 0: lfh.write("Running {} reps under scen_idx {}, reps_idx {}\n".format( num_reps, scen_idx, reps_idx)) multires = qsoresult.MultiRepResult(tc.fid_err_targ, fid_comp.local, num_tslots=num_tslots, evo_time=evo_time, numer_acc=numer_acc) if verbosity > 2: lfh.write("multires optional attribs: num_tslots={}, evo_time={}, " "fid_err_targ={}, numer_acc={}\n".format( multires.num_tslots, multires.evo_time, multires.fid_err_targ, multires.numer_acc)) # Repetition paramaters and results arrays # force the random number generator to reseed, as would cause issue # when using multiprocessing np.random.seed() # set up the decoupling slots # dyn.num_decoup_tslots implies that a specific decoup tslot has been given if dyn.num_decoup_tslots is not None: if dyn.num_decoup_tslots == 0: # assume all timeslots dyn.decoup_tslots = np.ones([dyn.num_tslots]) else: dyn.decoup_tslots = np.zeros([dyn.num_tslots]) dyn.decoup_tslots[:dyn.num_decoup_tslots] = 1 if verbosity > 2: lfh.write("Decoup timeslots: {}\n".format(dyn.decoup_tslots)) if len(dyn.decoup_tslots) != dyn.num_tslots: raise RuntimeError("Number of decoupling tslots {} not equal to " "number of timeslots {}".format( len(dyn.decoup_tslots, num_tslots))) try: for k in range(num_reps): # If hspace_order has random 0 index or 01 separation # (relating to the position and separation of the qubits # which the 2-qubit gate acts upon) then regenerate the # the hspace_order for each repetition if dyn.auto_hspace and (dyn.hspace_0_idx < 0 or dyn.hspace_01_sep < 0): dyn.hspace_order = qso.get_coupling_hspace( dyn.num_qubits, dyn.hspace_0_idx, dyn.hspace_01_sep) if verbosity > 0: lfh.write("reconfiguring drift with hspace_order " "= {}\n".format(dyn.hspace_order)) dyn.drift_dyn_gen = qso.get_drift(dyn) # Generate# pulses for each control init_amps = np.zeros([dyn.num_tslots, dyn.num_ctrls]) pgen = optim.pulse_generator for j in range(dyn.num_ctrls): init_amps[:, j] = pgen.gen_pulse() if dyn.decoup_x > 0: for i in dyn.Sx_cidx: init_amps[:, i] += dyn.decoup_tslots * dyn.decoup_x if dyn.decoup_y > 0: for i in dyn.Sy_cidx: init_amps[:, i] += dyn.decoup_tslots * dyn.decoup_y if dyn.decoup_z > 0: for i in dyn.Sz_cidx: init_amps[:, i] += dyn.decoup_tslots * dyn.decoup_z dyn.initialize_controls(init_amps) if cfg.save_initial_amps: pulsefile = "init_amps_{}_rep{}.{}".format( cfg_str, k + 1, out_file_ext) pfpath = os.path.join(cfg.output_dir, pulsefile) dyn.save_amps(pfpath, times="exclude") if verbosity > 1: lfh.write("Initial amps saved\n") if optim.dump: optim.dump.clear() optim.dump.dump_file_ext = out_file_ext optim.dump.fname_base = "optim_dump_rep{}_{}".format( k + 1, cfg_str) if dyn.dump: dyn.dump.clear() dyn.dump.dump_file_ext = out_file_ext dyn.dump.fname_base = "dyn_dump_rep{}_{}".format( k + 1, cfg_str) if verbosity > 0: lfh.write("\nStarting pulse optimisation {} of {}\n".format( k + 1, num_reps)) if verbosity > 1: lfh.write("Max wall time {}\n".format( optim.termination_conditions.max_wall_time)) optres = optim.run_optimization() optres.optim_dump = optim.dump optres.dyn_dump = dyn.dump repres = multires.add_optim_result(optres) if cfg.save_final_amps: pulsefile = "final_amps_{}_rep{}.{}".format( cfg_str, k + 1, out_file_ext) pfpath = os.path.join(cfg.output_dir, pulsefile) dyn.save_amps(pfpath, times="exclude") if verbosity > 1: lfh.write("Final amps saved\n") if verbosity > 0 and cfg.report_stats: lfh.write("Optimising complete. Stats follow:\n") optres.stats.report() if verbosity > 0: lfh.write("********* Summary *****************\n") lfh.write("Initial fidelity error {}\n".format( optres.initial_fid_err)) lfh.write("Final fidelity error {}\n".format(optres.fid_err)) if fid_comp.local: lfh.write("Final TRUE choi fidelity error {}\n".format( 1 - dyn.fid_computer.compute_global_choi_fid())) lfh.write("Terminated due to {}\n".format( optres.termination_reason)) lfh.write("Number of iterations {}\n".format(optres.num_iter)) lfh.write("Completed in {} HH:MM:SS.US\n".format( datetime.timedelta(seconds=optres.wall_time))) lfh.write("Final gradient normal {}\n".format( optres.grad_norm_final)) lfh.write("***********************************\n") if optres.optim_dump: if verbosity > 0: lfh.write("Optim dump saved\n") optres.optim_dump.writeout() if optres.dyn_dump: if verbosity > 0: lfh.write("Dynamics dump saved\n") optres.dyn_dump.writeout() if cfg.keep_optim_result: repres.optim_result = optres else: del (optres) except KeyboardInterrupt as e: lfh.write("\nProcessing interrupted\n") if not base_log: lfh.close() raise e if verbosity > 0: lfh.write("\n***** ALL SEARCHING FINISHED *****\n\n") multires.analyse_results() if save_results: fname = "results_{}.{}".format(cfg_str, out_file_ext) fpath = os.path.join(cfg.output_dir, fname) with open(fpath, 'w') as fh: multires.write_results(fh) if verbosity > 0: lfh.write("Results saved to:\n{}\n".format(fpath)) if verbosity > 0: lfh.write("\nFull results\n") multires.write_results(lfh) # Print very short summary multires.report_analysis(f=lfh) if report_phys_params: qso.print_phys_params(optim, f=lfh) if not base_log: lfh.close() return multires
def gen_config(param_fname='unctrlsympl_params.ini'): """ Generate the configuration for the oscillator In general values from the parameter file overwrite those hardcoded and these in turn are overwritten by cmdline args Returns ------- Optimizer """ parser = argparse.ArgumentParser( description="Command line argument parser") parser.add_argument('-p', '--param_file', type=str, default="", help="Parameters file name") parser.add_argument('-o', '--output_dir', type=str, default="", help="output sub directory") parser.add_argument('-j', '--job_id', type=int, default=0, help="Job id (from bsub)") parser.add_argument('-i', '--job_idx', type=int, default=0, help="Job index (from bsub batch array)") parser.add_argument('-I', '--idx_opt', type=str, default='', help="Job index option (what to do with it)") #parser.add_argument('-S', '--job_size', type=int, default=0, # help="Number of jobs in array") parser.add_argument('-N', '--num_qubits', type=int, default=0, help="Number of qubits") parser.add_argument('-u', '--init_amps', type=str, default=0, help="File name of initial amplitudes") parser.add_argument('-m', '--num_tslots', type=int, default=0, help="Number of timeslots") parser.add_argument('-T', '--evo_time', type=float, default=0, help="Total evolution time") parser.add_argument('--evo_time_npi', type=int, default=0, help="Total evolution time in mulitples of pi") parser.add_argument('--evo_time_npi_g0s', type=int, default=0, help="Total evolution time in mulitples of pi " "scaled by first coupling constant") parser.add_argument('-a', '--numer_acc', type=float, default=-1.0, help="Numerical accuracy") parser.add_argument('-e', '--fid_err_targ', type=float, default=0.0, help="Fidelity error target") parser.add_argument('-n', '--num_cpus', type=int, default=0, help="Fidelity error target") parser.add_argument('-M', '--mp_opt', type=str, default='', help="Multiprocessing option") parser.add_argument('--mem_opt', type=int, default=0, help="Memory optimising level") parser.add_argument('--pulse_scaling', type=int, default=0.0, help="Initial pulse scaling") parser.add_argument('--max_wall_time', type=int, default=0.0, help="Maximum simulation wall time") args = vars(parser.parse_args()) cfg = optimconfig.OptimConfig() cfg.log_level = log_level cfg.use_param_file = True if len(args['param_file']) > 0: param_fname = args['param_file'] cfg.param_fpath = os.path.join(os.getcwd(), param_fname) if not os.path.isfile(cfg.param_fpath): raise ValueError( "Commandline argument parameter " "file name '{}' does not exist.".format(param_fname)) else: print("Parameters will be read from:\n{}".format(cfg.param_fpath)) elif os.path.abspath(param_fname): cfg.param_fname = os.path.basename(param_fname) cfg.param_fpath = param_fname param_fname = cfg.param_fname else: cfg.param_fpath = os.path.join(os.getcwd(), param_fname) if (not os.path.isfile(cfg.param_fpath)): print("Default parameter file {} not present. " "Using defaults in code.").format(param_fname) cfg.use_param_file = False cfg.param_fname = param_fname # Script operational parameters cfg.job_id = 0 cfg.job_idx = 0 cfg.verbosity = 0 cfg.output_dir = None cfg.output_base_name = 'general' cfg.double_print = True cfg.output_files = True cfg.plot_result = False cfg.plot_file_type = 'PNG' cfg.save_plots = False cfg.save_results = True cfg.data_file_ext = "txt" cfg.out_file_ext = "txt" # Optimizer config cfg.optim_method = 'LBFGSB' cfg.p_type = 'RND' cfg.check_grad = False cfg.gen_stats = True cfg.stats_type = 'standard' cfg.report_stats = True cfg.save_initial_amps = False cfg.save_final_amps = False cfg.fid_type = 'pure_choi_local' cfg.amp_lbound = -np.Inf cfg.amp_ubound = np.Inf cfg.num_reps = 2 cfg.num_cpus = 1 cfg.mp_opt = "" cfg.max_mp_scens = np.inf cfg.ext_mp = False cfg.num_threads = 1 if cfg.use_param_file: # load the config parameters # note these will overide those above if present in the file print("Loading config parameters from {}".format(cfg.param_fpath)) loadparams.load_parameters(cfg.param_fpath, config=cfg) # override with command line params (if any) if args['num_qubits'] > 0: cfg.num_qubits = args['num_qubits'] print("Using num_qubits={} from command line".format(cfg.num_qubits)) if len(args['output_dir']) > 0: cfg.output_dir = args['output_dir'] print("Using output_dir '{}' from command line".format(cfg.output_dir)) if args['num_cpus'] > 0: cfg.num_cpus = args['num_cpus'] print("Using num_cpus={} from command line".format(cfg.num_cpus)) if len(args['mp_opt']) > 0: cfg.mp_opt = args['mp_opt'] print("Using mp_opt={} from command line".format(cfg.mp_opt)) cfg.plot_file_ext = cfg.plot_file_type.lower() if args['job_id'] > 0: cfg.job_id = args['job_id'] if cfg.job_id: print("Processing job ID: {}".format(cfg.job_id)) if args['job_idx'] > 0: cfg.job_idx = args['job_idx'] if cfg.job_idx: print("Processing job array index: {}".format(cfg.job_idx)) cfg.out_file_ext = "{}.{}.{}".format(cfg.job_id, cfg.job_idx, cfg.data_file_ext) cfg.plot_file_ext = "{}.{}.{}".format(cfg.job_id, cfg.job_idx, cfg.plot_file_type) else: cfg.out_file_ext = "{}.{}".format(cfg.job_id, cfg.data_file_ext) cfg.plot_file_ext = "{}.{}".format(cfg.job_id, cfg.plot_file_type) logger.setLevel(cfg.log_level) if not cfg.output_dir: cfg.output_dir = cfg.target_id_text dyn = dynamics.DynamicsUnitary(cfg) dyn.dense_oper = True dyn.num_tslots_list = [] dyn.st_num_tslots = 10 dyn.d_num_tslots = 5 dyn.num_num_tslots = 10 dyn.evo_time = 10.0 # For a specific list of evo_time values based on job_idx dyn.evo_time_list = [7, 10, 12, 20] # For a range of evo_time based on job_idx (idx_opt='evo_time') # Start evo_time dyn.st_evo_time = 0.2 # Evo time step size dyn.d_evo_time = 0.2 # Number of evo_time dyn.num_evo_time = 50 dyn.num_qubits = 6 dyn.target_type = 'CNOT' dyn.interact = 'Ising' dyn.topology = 'chain' dyn.ctrls_type = 'XY' dyn.coup_const = [1.0] # Used to change the order of the qubits in the Hilbert space # so that the two qubit gate will always be between 0 and 1, # but the couplings will be permuted based on hspace_order dyn.hspace_order = [] # When True the hspace_order will be automatically generated # based on the other attributes dyn.auto_hspace = False # Separation of the first and second qubits in the coupling Hilbert space # 0 implies adjacent, -1 implies random dyn.hspace_01_sep = 0 # Index of the first qubit in the coupling Hilbert space # -1 implies random dyn.hspace_0_idx = 0 # If True then same coupling constant will be used for # all interactions between qubit pairs dyn.iso_coup = True # dynamical decoup attribs # These are the decoup amplitudes in the three axes dyn.decoup_x = 0.0 dyn.decoup_y = 0.0 dyn.decoup_z = 0.0 # num_decoup_tslots = None means use the specific mask # num_decoup_tslots = 0 use all # num_decoup_tslots +ve is the number of tslots from start # num_decoup_tslots -ve is the number of zeros at the end dyn.num_decoup_tslots = 0 dyn.decoup_tslots = [] #dyn.decoup_tslot_mask = [] if cfg.use_param_file: # load the dynamics parameters # note these will overide those above if present in the file print("Loading dynamics parameters from {}".format(cfg.param_fpath)) loadparams.load_parameters(cfg.param_fpath, dynamics=dyn) if args['num_qubits'] > 0: dyn.num_qubits = args['num_qubits'] print("num_qubits = {} from command line".format(dyn.num_qubits)) # If job_idx is given, then use it to calculate some other parameter # (which supersedes all other settings) if cfg.job_idx and len(args['idx_opt']) > 0: cfg.ext_mp = True opt_str = args['idx_opt'].lower() if opt_str == 'evo_time': print("basing evo_time on job_idx... ") num_evo_time = len(dyn.evo_time_list) if num_evo_time > 0: print("...using evo_time_list") # get the evo time from the list T_idx = (cfg.job_idx - 1) % num_evo_time dyn.evo_time = dyn.evo_time_list[T_idx] else: print("...using start and increment") # calculate the evo_time from job_idx T_idx = (cfg.job_idx - 1) % dyn.num_evo_time dyn.evo_time = dyn.st_evo_time + dyn.d_evo_time * float(T_idx) print("evo_time={} for job idx {}".format(dyn.evo_time, cfg.job_idx)) elif opt_str == 'num_tslots': print("basing num_tslots on job_idx... ") num_nts = len(dyn.num_tslots_list) if num_nts > 0: print("...using num_tslots_list") nts_idx = (cfg.job_idx - 1) % num_nts dyn.num_tslots = dyn.num_tslots_list[nts_idx] else: print("...using start and increment") # calculate the num_tslots from job_idx nts_idx = (cfg.job_idx - 1) % dyn.num_num_tslots dyn.num_tslots = dyn.st_num_tslots + dyn.d_num_tslots * nts_idx print("num_tslots={} for job idx {}".format( dyn.num_tslots, cfg.job_idx)) else: raise ValueError("No option for idx_opt '{}' " "in command line".format(opt_str)) if args['evo_time'] > 0: dyn.evo_time = args['evo_time'] print("Using evo_time={} from command line".format(dyn.evo_time)) if args['evo_time_npi'] > 0: dyn.evo_time = np.pi * args['evo_time_npi'] print("Using evo_time={} from command line evo_time_npi".format( dyn.evo_time)) if args['evo_time_npi_g0s'] > 0: dyn.evo_time = np.pi * args['evo_time_npi_g0s'] / dyn.coup_const[0] print("Using evo_time={} from command line evo_time_npi_g0s".format( dyn.evo_time)) if args['num_tslots'] > 0: dyn.num_tslots = args['num_tslots'] print("Using num_tslots={} from command line".format(dyn.num_tslots)) if args['mem_opt'] > 0: dyn.memory_optimization = args['mem_opt'] print("Using mem_opt={} from command line".format( dyn.memory_optimization)) print("evo_time={}".format(dyn.evo_time)) print("num_tslots={}".format(dyn.num_tslots)) if dyn.num_tslots == 0: dyn.num_tslots = dyn.num_qubits * 4 + 8 print("num_tslots calculated and set to be {}".format(dyn.num_tslots)) if len(dyn.coup_const) == 1: dyn.coup_const = dyn.coup_const[0] dyn.prop_computer = propcomp.PropCompFrechet(dyn) if dyn.dense_oper: dyn.oper_dtype = np.ndarray else: dyn.oper_dtype = Qobj # Create the TerminationConditions instance tc = termcond.TerminationConditions() tc.fid_err_targ = 1e-3 tc.min_gradient_norm = 1e-30 tc.max_iter = 2400 tc.max_wall_time = 120 * 60.0 tc.accuracy_factor = 1e5 if cfg.use_param_file: # load the termination condition parameters # note these will overide those above if present in the file print("Loading termination condition parameters from {}".format( cfg.param_fpath)) loadparams.load_parameters(cfg.param_fpath, term_conds=tc) if args['fid_err_targ'] > 0.0: tc.fid_err_targ = args['fid_err_targ'] print("fid_err_targ = {} from command line".format(tc.fid_err_targ)) if args['max_wall_time'] > 0.0: tc.max_wall_time = args['max_wall_time'] print("max_wall_time = {} from command line".format(tc.max_wall_time)) # Fidelity oomputer ft = cfg.fid_type.lower() if ft == 'pure_choi_local': dyn.fid_computer = FidCompPureChoiLocal(dyn) elif ft == 'pure_choi_global': dyn.fid_computer = FidCompPureChoiGlobal(dyn) elif ft == 'unit_global': dyn.fid_computer = fidcomp.FidCompUnitary(dyn) dyn.fid_computer.local = False else: raise errors.UsageError("Unknown fid type {}".format(cfg.fid_type)) fid_comp = dyn.fid_computer fid_comp.numer_acc = 0.0 fid_comp.numer_acc_exact = False fid_comp.st_numer_acc = 0.01 fid_comp.end_numer_acc = 0.2 fid_comp.success_prop_uthresh = 0.95 fid_comp.success_prop_lthresh = 0.01 if cfg.use_param_file: # load the pulse generator parameters # note these will overide those above if present in the file print("Loading fidcomp parameters from {}".format(cfg.param_fpath)) loadparams.load_parameters(cfg.param_fpath, obj=dyn.fid_computer, section='fidcomp') if args['numer_acc'] >= 0.0: fid_comp.numer_acc = args['numer_acc'] print("numer_acc = {} from command line".format(fid_comp.numer_acc)) if not fid_comp.numer_acc_exact: fid_comp.numer_acc = round_sigfigs( fid_comp.numer_acc * tc.fid_err_targ, 6) fid_comp.st_numer_acc = round_sigfigs( fid_comp.st_numer_acc * tc.fid_err_targ, 6) fid_comp.end_numer_acc = round_sigfigs( fid_comp.end_numer_acc * tc.fid_err_targ, 6) # Pulse generator p_gen = pulsegen.create_pulse_gen(pulse_type=cfg.p_type, dyn=dyn) p_gen.all_ctrls_in_one = False p_gen.lbound = cfg.amp_lbound p_gen.ubound = cfg.amp_ubound if cfg.use_param_file: # load the pulse generator parameters # note these will overide those above if present in the file print("Loading pulsegen parameters from {}".format(cfg.param_fpath)) loadparams.load_parameters(cfg.param_fpath, pulsegen=p_gen) if not isinstance(p_gen, pulsegen.PulseGenCrab): if not (np.isinf(cfg.amp_lbound) or np.isinf(cfg.amp_ubound)): p_gen.scaling = cfg.amp_ubound - cfg.amp_lbound p_gen.offset = (cfg.amp_ubound + cfg.amp_lbound) / 2.0 if args['pulse_scaling'] > 0.0: p_gen.scaling = args['pulse_scaling'] print("p_gen.scaling = {} from command line".format(p_gen.scaling)) # Create the Optimiser instance if cfg.optim_method is None: raise errors.UsageError("Optimisation method must be specified " "via 'optim_method' parameter") om = cfg.optim_method.lower() if om == 'bfgs': optim = optimizer.OptimizerBFGS(cfg, dyn) elif om == 'lbfgsb': optim = optimizer.OptimizerLBFGSB(cfg, dyn) else: # Assume that the optim_method is valid optim = optimizer.Optimizer(cfg, dyn) optim.method = cfg.optim_method if cfg.verbosity > 1: print("Created optimiser of type {}".format(type(optim))) optim.config = cfg optim.dynamics = dyn optim.termination_conditions = tc optim.pulse_generator = p_gen optim.method_approach = 'DEF' optim.dumping = 'SUMMARY' if cfg.use_param_file: # load the optimiser parameters # note these will overide those above if present in the file print("Loading optimiser parameters from {}".format(cfg.param_fpath)) loadparams.load_parameters(cfg.param_fpath, optim=optim) optim.termination_conditions = tc if cfg.gen_stats: # Create a stats object # Note that stats object is optional # if the Dynamics and Optimizer stats attribute is not set # then no stats will be collected, which could improve performance stats_type = 'standard' try: stats_type = cfg.stats_type.lower() except: pass if stats_type == 'local': sts = qsostats.StatsFidCompLocal() else: sts = stats.Stats() dyn.stats = sts optim.stats = sts # **************************************************************** # Define the physics of the problem print("Configuring drift...") #The 4 below are all Qobj # Sx = sigmax() # Sy = sigmay() # Sz = sigmaz() # Si = identity(2) nq = dyn.num_qubits # ***** Drift ***** print("... for {} qubits".format(nq)) H_d = qso.get_drift(dyn) print("Drift dims {}".format(H_d.dims)) # Normalise based on ising chain H_d_ising_chain = qso.get_drift(dyn, topology='chain', interact='ising', coup_const=1.0) norm_fact = H_d_ising_chain.norm() / H_d.norm() print("Normalising drift with factor {}".format(norm_fact)) H_d = H_d * norm_fact # **** Controls **** H_c, Sx_cidx, Sy_cidx, Sz_cidx = qso.get_ctrls(dyn) n_ctrls = len(H_c) #t0 evo U_0 = qso.get_initial_op(dyn) #*** Target **** U_targ, U_local_targs = qso.get_target(dyn) sub_dims = [] for k in range(len(U_local_targs)): sub_dims.append(U_local_targs[k].dims[0][0]) #Enforcing all dimensions are correct for the qBranch code #Not all of these are needed, but this is safer for k in range(len(H_c)): H_c[k].dims = [sub_dims, sub_dims] H_d.dims = [sub_dims, sub_dims] U_0.dims = [sub_dims, sub_dims] U_targ.dims = [sub_dims, sub_dims] dyn.drift_dyn_gen = H_d dyn.ctrl_dyn_gen = H_c dyn.initial = U_0 dyn.target = U_targ # These are the indexes to the controls on the three axes dyn.Sx_cidx = Sx_cidx dyn.Sy_cidx = Sy_cidx dyn.Sz_cidx = Sz_cidx fid_comp.U_local_targs = U_local_targs fid_comp.sub_dims = sub_dims fid_comp.num_sub_sys = len(U_local_targs) print("Num acc: {}".format(fid_comp.numer_acc)) return optim