def _create_computers(self): """ Create the default timeslot, fidelity and propagator computers """ # The time slot computer. By default it is set to _UpdateAll # can be set to _DynUpdate in the configuration # (see class file for details) if self.config.amp_update_mode == 'DYNAMIC': self.tslot_computer = tslotcomp.TSlotCompDynUpdate(self) else: self.tslot_computer = tslotcomp.TSlotCompUpdateAll(self) self.prop_computer = propcomp.PropCompFrechet(self) self.fid_computer = fidcomp.FidCompTraceDiff(self)
def create_pulse_optimizer(drift, ctrls, initial, target, num_tslots=None, evo_time=None, tau=None, amp_lbound=-np.Inf, amp_ubound=np.Inf, fid_err_targ=1e-10, min_grad=1e-10, max_iter=500, max_wall_time=180, optim_alg='LBFGSB', max_metric_corr=10, accuracy_factor=1e7, dyn_type='GEN_MAT', prop_type='DEF', fid_type='DEF', phase_option=None, fid_err_scale_factor=None, amp_update_mode='ALL', init_pulse_type='RND', pulse_scaling=1.0, pulse_offset=0.0, log_level=logging.NOTSET, gen_stats=False): """ Generate the objects of the appropriate subclasses required for the pulse optmisation based on the parameters given Note this method may be preferable to calling optimize_pulse if more detailed configuration is required before running the optmisation algorthim, or the algorithm will be run many times, for instances when trying to finding global the optimum or minimum time optimisation Parameters ---------- drift : Qobj the underlying dynamics generator of the system ctrls : List of Qobj a list of control dynamics generators. These are scaled by the amplitudes to alter the overall dynamics initial : Qobj starting point for the evolution. Typically the identity matrix target : Qobj target transformation, e.g. gate or state, for the time evolution num_tslots : integer or None number of timeslots. None implies that timeslots will be given in the tau array evo_time : float or None total time for the evolution None implies that timeslots will be given in the tau array tau : array[num_tslots] of floats or None durations for the timeslots. if this is given then num_tslots and evo_time are dervived from it None implies that timeslot durations will be equal and calculated as evo_time/num_tslots amp_lbound : float or list of floats lower boundaries for the control amplitudes Can be a scalar value applied to all controls or a list of bounds for each control amp_ubound : float or list of floats upper boundaries for the control amplitudes Can be a scalar value applied to all controls or a list of bounds for each control fid_err_targ : float Fidelity error target. Pulse optimisation will terminate when the fidelity error falls below this value mim_grad : float Minimum gradient. When the sum of the squares of the gradients wrt to the control amplitudes falls below this value, the optimisation terminates, assuming local minima max_iter : integer Maximum number of iterations of the optimisation algorithm max_wall_time : float Maximum allowed elapsed time for the optimisation algorithm optim_alg : string Multi-variable optimisation algorithm options are BFGS, LBFGSB (see Optimizer classes for details) max_metric_corr : integer The maximum number of variable metric corrections used to define the limited memory matrix. That is the number of previous gradient values that are used to approximate the Hessian see the scipy.optimize.fmin_l_bfgs_b documentation for description of m argument (used only in L-BFGS-B) accuracy_factor : float Determines the accuracy of the result. Typical values for accuracy_factor are: 1e12 for low accuracy; 1e7 for moderate accuracy; 10.0 for extremely high accuracy scipy.optimize.fmin_l_bfgs_b factr argument. (used only in L-BFGS-B) dyn_type : string Dynamics type, i.e. the type of matrix used to describe the dynamics. Options are UNIT, GEN_MAT, SYMPL (see Dynamics classes for details) prop_type : string Propagator type i.e. the method used to calculate the propagtors and propagtor gradient for each timeslot options are DEF, APPROX, DIAG, FRECHET, AUG_MAT DEF will use the default for the specific dyn_type (see PropagatorComputer classes for details) fid_type : string Fidelity error (and fidelity error gradient) computation method Options are DEF, UNIT, TRACEDIFF, TD_APPROX DEF will use the default for the specific dyn_type (See FideliyComputer classes for details) phase_option : string determines how global phase is treated in fidelity calculations (fid_type='UNIT' only). Options: PSU - global phase ignored SU - global phase included fid_err_scale_factor : float (used in TRACEDIFF FidelityComputer and subclasses only) The fidelity error calculated is of some arbitary scale. This factor can be used to scale the fidelity error such that it may represent some physical measure If None is given then it is caculated as 1/2N, where N is the dimension of the drift. amp_update_mode : string determines whether propagators are calculated Options: DEF, ALL, DYNAMIC (needs work) DEF will use the default for the specific dyn_type (See TimeslotComputer classes for details) init_pulse_type : string type / shape of pulse(s) used to initialise the the control amplitudes. Options include: RND, LIN, ZERO, SINE, SQUARE, TRIANGLE, SAW (see PulseGen classes for details) pulse_scaling : float Linear scale factor for generated pulses By default initial pulses are generated with amplitudes in the range (-1.0, 1.0). These will be scaled by this parameter pulse_offset : float Line offset for the pulse. That is this value will be added to any initial pulses generated. log_level : integer level of messaging output from the logger. Options are attributes of qutip.logging, in decreasing levels of messaging, are: DEBUG_INTENSE, DEBUG_VERBOSE, DEBUG, INFO, WARN, ERROR, CRITICAL Anything WARN or above is effectively 'quiet' execution, assuming everything runs as expected. The default NOTSET implies that the level will be taken from the QuTiP settings file, which by default is WARN Note value should be set using set_log_level gen_stats : boolean if set to True then statistics for the optimisation run will be generated - accessible through attributes of the stats object Returns ------- Instance of an Optimizer, through which the Config, Dynamics, PulseGen, and TerminationConditions objects can be accessed as attributes. The PropagatorComputer, FidelityComputer and TimeslotComputer objects can be accessed as attributes of the Dynamics object, e.g. optimizer.dynamics.fid_computer The optimisation can be run through the optimizer.run_optimization """ # check parameters if not isinstance(drift, Qobj): raise TypeError("drift must be a Qobj") else: drift = drift.full() if not isinstance(ctrls, (list, tuple)): raise TypeError("ctrls should be a list of Qobj") else: j = 0 for ctrl in ctrls: if not isinstance(ctrl, Qobj): raise TypeError("ctrls should be a list of Qobj") else: ctrls[j] = ctrl.full() j += 1 if not isinstance(initial, Qobj): raise TypeError("initial must be a Qobj") else: initial = initial.full() if not isinstance(target, Qobj): raise TypeError("target must be a Qobj") else: target = target.full() cfg = optimconfig.OptimConfig() cfg.optim_alg = optim_alg cfg.max_metric_corr = max_metric_corr cfg.accuracy_factor = accuracy_factor cfg.amp_update_mode = amp_update_mode cfg.dyn_type = dyn_type cfg.prop_type = prop_type cfg.fid_type = fid_type cfg.pulse_type = init_pulse_type cfg.phase_option = phase_option cfg.amp_lbound = amp_lbound cfg.amp_ubound = amp_ubound if log_level == logging.NOTSET: log_level = logger.getEffectiveLevel() else: logger.setLevel(log_level) cfg.log_level = log_level # Create the Dynamics instance if dyn_type == 'GEN_MAT' or dyn_type is None or dyn_type == '': dyn = dynamics.DynamicsGenMat(cfg) elif dyn_type == 'UNIT': dyn = dynamics.DynamicsUnitary(cfg) elif dyn_type == 'SYMPL': dyn = dynamics.DynamicsSymplectic(cfg) else: raise errors.UsageError("No option for dyn_type: " + dyn_type) # Create the PropagatorComputer instance # The default will be typically be the best option if prop_type == 'DEF' or prop_type is None or prop_type == '': # Do nothing use the default for the Dynamics pass elif prop_type == 'APPROX': if not isinstance(dyn.prop_computer, propcomp.PropCompApproxGrad): dyn.prop_computer = propcomp.PropCompApproxGrad(dyn) elif prop_type == 'DIAG': if not isinstance(dyn.prop_computer, propcomp.PropCompDiag): dyn.prop_computer = propcomp.PropCompDiag(dyn) elif prop_type == 'AUG_MAT': if not isinstance(dyn.prop_computer, propcomp.PropCompAugMat): dyn.prop_computer = propcomp.PropCompAugMat(dyn) elif prop_type == 'FRECHET': if not isinstance(dyn.prop_computer, propcomp.PropCompFrechet): dyn.prop_computer = propcomp.PropCompFrechet(dyn) else: raise errors.UsageError("No option for prop_type: " + prop_type) # Create the FideliyComputer instance # The default will be typically be the best option # Note: the FidCompTraceDiffApprox is a subclass of FidCompTraceDiff # so need to check this type first if fid_type == 'DEF' or fid_type is None or fid_type == '': # None given, use the default for the Dynamics pass elif fid_type == 'TDAPPROX': if not isinstance(dyn.fid_computer, fidcomp.FidCompTraceDiffApprox): dyn.fid_computer = fidcomp.FidCompTraceDiffApprox(dyn) elif fid_type == 'TRACEDIFF': if not isinstance(dyn.fid_computer, fidcomp.FidCompTraceDiff): dyn.fid_computer = fidcomp.FidCompTraceDiff(dyn) elif fid_type == 'UNIT': if not isinstance(dyn.fid_computer, fidcomp.FidCompUnitary): dyn.fid_computer = fidcomp.FidCompUnitary(dyn) else: raise errors.UsageError("No option for fid_type: " + fid_type) if isinstance(dyn.fid_computer, fidcomp.FidCompUnitary): dyn.fid_computer.set_phase_option(phase_option) if isinstance(dyn.fid_computer, fidcomp.FidCompTraceDiff): dyn.fid_computer.scale_factor = fid_err_scale_factor # Create the Optimiser instance # The class of the object will determine which multivar optimisation # algorithm is used if optim_alg == 'BFGS': optim = optimizer.OptimizerBFGS(cfg, dyn) elif optim_alg == 'LBFGSB': optim = optimizer.OptimizerLBFGSB(cfg, dyn) elif optim_alg is None: raise errors.UsageError("Optimisation algorithm must be specified " "via 'optim_alg' parameter") else: raise errors.UsageError("No option for optim_alg: " + optim_alg) # Create the TerminationConditions instance tc = termcond.TerminationConditions() tc.fid_err_targ = fid_err_targ tc.min_gradient_norm = min_grad tc.max_iterations = max_iter tc.max_wall_time = max_wall_time optim.termination_conditions = tc if 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 if amp_update_mode == 'DYNAMIC': sts = stats.StatsDynTsUpdate() else: sts = stats.Stats() dyn.stats = sts optim.stats = sts # Configure the dynamics dyn.drift_dyn_gen = drift dyn.ctrl_dyn_gen = ctrls dyn.initial = initial dyn.target = target if tau is None: # Check that parameters have been supplied to generate the # timeslot durations try: evo_time / num_tslots except: raise errors.UsageError( "Either the timeslot durations should be supplied as an " "array 'tau' or the number of timeslots 'num_tslots' " "and the evolution time 'evo_time' must be given.") dyn.num_tslots = num_tslots dyn.evo_time = evo_time else: dyn.tau = tau # this function is called, so that the num_ctrls attribute will be set dyn.get_num_ctrls() # Create a pulse generator of the type specified p_gen = pulsegen.create_pulse_gen(pulse_type=init_pulse_type, dyn=dyn) p_gen.scaling = pulse_scaling p_gen.offset = pulse_offset p_gen.lbound = amp_lbound p_gen.ubound = amp_ubound # If the pulse is a periodic type, then set the pulse to be one complete # wave if isinstance(p_gen, pulsegen.PulseGenPeriodic): p_gen.num_waves = 1.0 optim.pulse_generator = p_gen if log_level <= logging.DEBUG: logger.debug("Optimisation config summary...\n" " object classes:\n" " optimizer: " + optim.__class__.__name__ + "\n dynamics: " + dyn.__class__.__name__ + "\n tslotcomp: " + dyn.tslot_computer.__class__.__name__ + "\n fidcomp: " + dyn.fid_computer.__class__.__name__ + "\n propcomp: " + dyn.prop_computer.__class__.__name__ + "\n pulsegen: " + p_gen.__class__.__name__) return optim