import pandas as pd import qutip if '../../' not in sys.path: sys.path.append('../../') import src.optimization as optimization import src.lmg_model as lmg_model import src.protocol_ansatz as protocols from src.utils import ground_state num_spins = 50 tf_list = np.linspace(0, 1, 200) time_ratios_list = np.linspace(0, 1, 400) bound = [-100, 100] lmg = lmg_model.LMGModel(num_spins=num_spins) initial_state = ground_state(lmg.H0) target_state = ground_state(lmg.hamiltonian(g_value=1.)) # ------ set up logger output_file_name = 'scan_{}spins_bound{}.csv'.format(num_spins, bound[1]) logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s]" "[%(levelname)-5.5s] %(message)s") rootLogger = logging.getLogger() rootLogger.setLevel(logging.DEBUG) fileHandler = logging.FileHandler(output_file_name[:-4] + '.log') fileHandler.setFormatter(logFormatter) fileHandler.setLevel(logging.DEBUG) rootLogger.addHandler(fileHandler) logging.info('Output file name will be "{}"'.format(output_file_name))
def find_best_protocol(problem_specification, optimization_specs, other_options={}): """Higher-level interface to optimize_model_parameters. Parameters ---------- problem_specification : dict Mandatory keys: 'model', 'model_parameters', 'task', 'time'. The accepted values for the `model` key are - 'rabi' - 'lmg' If model='rabi', the accepted values for `model_parameters` are - 'N' - 'Omega' - 'omega_0' If model='lmg', the accepted values for `model_parameters` are - 'num_spins' The accepted values for `task` are: - 'critical point state generation' The value of 'time' should be a positive number, representing the evolution time. optimization_specs : dict Accepted keys: - 'protocol' - 'protocol_options' - 'optimization_method' - 'optimization_options' - 'solver_options' - 'initial_parameters' - 'parameters_constraints' The accepted values for `protocol` are - 'doublebang' - 'bangramp' - 'crab' The accepted values for 'protocol_options' depend on 'protocol'. If protocol='crab' then the accepted values are - 'num_frequencies' The accepted values for 'optimization_method' are those accepted by scipy.optim.minimize, and similarly the value of 'optimization_options' is passed over to this function. Other values accepted for optimization_specs are: - 'parameters_constraints' The value of 'initial_parameters' can be either a numpy array with explicit values, or a string. The value of `parameters_constraints` can a list of pairs, with each pair specifying the constraints for one of the optimisation pars. If it instead a single list of two elements, it is instead used to put constraints on the overall height of the protocol (so not directly on the parameters that define it). This is notably necessary for the CRAB protocol. other_options : dict Accepted keys: - 'scan_times' - 'stopping_condition' """ model = problem_specification['model'] model_parameters = problem_specification['model_parameters'] task = problem_specification['task'] protocol_name = optimization_specs['protocol'] optim_method = optimization_specs['optimization_method'] optim_options = optimization_specs.get('optimization_options') solver_options = optimization_specs.get('solver_options') initial_state = None target_state = None if model == 'rabi': hamiltonian = rabi_model.RabiModel(N=model_parameters['N'], Omega=model_parameters['Omega'], omega_0=model_parameters['omega_0']) elif model == 'lmg': hamiltonian = lmg_model.LMGModel( num_spins=model_parameters['num_spins']) elif model == 'lz': hamiltonian = lz_model.LZModel(omega_0=model_parameters['omega_0']) else: raise ValueError("{} isn't a valid value for the model".format(model)) if isinstance(task, str) and task == 'critical point': task = dict(initial_intensity=0, final_intensity=hamiltonian.critical_value) elif not isinstance(task, dict): raise ValueError('`task` must be a dictionary or a string.') initial_state = hamiltonian.ground_state(task['initial_intensity']) target_state = hamiltonian.ground_state(task['final_intensity']) # determine protocol ansatz to use and parse options if needed # if `protocol_name` is NOT a string, then it is assumed to have been given # directly as a protocol_ansatz object if not isinstance(protocol_name, str): logging.info('Using custom protocol ansatz.') protocol = protocol_name elif protocol_name == 'doublebang': logging.info('Using doublebang protocol ansatz.') protocol = DoubleBangProtocolAnsatz() elif protocol_name == 'bangramp': logging.info('Using bangramp protocol ansatz.') protocol = BangRampProtocolAnsatz() elif protocol_name == 'crab': logging.info('Using CRAB protocol ansatz.') protocol_options = optimization_specs.get('protocol_options', {}) # determine number of frequencies to use with CRAB protocol if 'num_frequencies' not in protocol_options: # default value kind of picked at random num_frequencies = 2 logging.info('(CRAB) Default value of {} frequencies chosen for th' 'e optimization'.format(num_frequencies)) else: num_frequencies = protocol_options['num_frequencies'] logging.info('(CRAB) Using {} frequencies.'.format(num_frequencies)) # fix starting and final points for pulse protocol in the case of # critical state generation task protocol = CRABProtocolAnsatz(num_frequencies=num_frequencies) protocol.fill_hyperpar_value(y0=task['initial_intensity'], y1=task['final_intensity']) else: raise ValueError('Unrecognised protocol.') # the scan_times option indicates that we want to perform a series of # optimizations for various values of the time parameter if 'scan_times' in other_options: # parse initial parameters critical_value = hamiltonian.critical_value if 'initial_parameters' not in optimization_specs: # if not explicitly given, use as default double the critical # point for intensities and the timeframe for times if protocol == 'doublebang': init_pars = [[0., 2 * critical_value], 'halftime', [0., 2 * critical_value]] elif protocol == 'bangramp': init_pars = [[0., 2 * critical_value], 'halftime', [0., 2 * critical_value], [0., 2 * critical_value]] elif protocol == 'crab' or protocol == 'crabVarEndpoints': # I don't know, let's just try with amplitudes randomly # sampled in the [-1, 1] interval (why not right?) init_pars = [[-0.1, 0.1]] * (2 * protocol.num_frequencies) else: raise ValueError('For custom protocols the initial parameters ' 'must be given explicitly.') else: # if explicitly given, just assume the values make sense for # _optimize_model_parameters_scan_times init_pars = optimization_specs['initial_parameters'] CRAB_AMPS_BOUNDS = [-200, 200] # parse parameters constraints if 'parameters_constraints' not in optimization_specs: # if not explicitly given, we generate boundaries similar to # those generated for default initial parameters critical_range = [-2 * critical_value, 2 * critical_value] if protocol == 'doublebang' or protocol == 'bangramp': protocol.constrain_intensities(critical_range) elif protocol == 'crab' or protocol == 'crabVarEndpoints': # not sure if this will work well in general, but in this # case we impose very weak constraints on the parameters, # and then the actual constraints are on the overall pulse protocol.constrain_all_amplitudes(CRAB_AMPS_BOUNDS) protocol.set_total_height_constraints(critical_range) else: pars_constraints = optimization_specs['parameters_constraints'] if isinstance(pars_constraints, (tuple, list)): if len(pars_constraints) == 2: if protocol == 'crab' or protocol == 'crabVarEndpoints': protocol.constrain_all_amplitudes(CRAB_AMPS_BOUNDS) protocol.set_total_height_constraints(pars_constraints) else: protocol.constrain_intensities(pars_constraints) else: protocol.add_parameter_constraints(pars_constraints) logging.info('Using parameters constraints: {}'.format( protocol.pars_constraints)) logging.info('Using total height constraint: {}'.format( protocol.total_height_constraint)) hamiltonian.td_protocol = protocol # run optimization stopping_condition = other_options.get('stopping_condition', None) results = _optimize_model_parameters_scan_times( times_to_try=other_options['scan_times'], hamiltonian=hamiltonian, initial_state=initial_state, target_state=target_state, initial_parameters=init_pars, optimization_method=optim_method, optimization_options=optim_options, solver_options=solver_options, stopping_condition=stopping_condition) return results else: # only scan_times works atm raise NotImplementedError('Only scan_times works atm, sorry.')