Exemplo n.º 1
0
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.')