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))
Example #2
0
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
Example #3
0
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