Exemple #1
0
def test_strip_slap():
    import numpy as np
    from qoc.models.dummy import Dummy
    from qoc.core.common import (
        slap_controls,
        strip_controls,
    )

    big = 100
    pstate = Dummy()
    pstate.complex_controls = True
    shape_range = np.arange(big) + 1
    for step_count in shape_range:
        for control_count in shape_range:
            pstate.controls_shape = controls_shape = (step_count,
                                                      control_count)
            pstate.max_control_norms = np.ones(control_count) * 2
            controls = np.random.rand(
                *controls_shape) + 1j * np.random.rand(*controls_shape)
            stripped_controls = strip_controls(pstate, controls)
            assert (stripped_controls.ndim == 1)
            assert (not (stripped_controls.dtype
                         in (np.complex64, np.complex128)))
            transformed_controls = slap_controls(pstate.complex_controls,
                                                 stripped_controls,
                                                 pstate.controls_shape)
            assert (np.allclose(controls, transformed_controls))
            assert (controls.shape == transformed_controls.shape)
        #ENDFOR
    #ENDFOR

    pstate.complex_controls = False
    for step_count in shape_range:
        for control_count in shape_range:
            pstate.controls_shape = controls_shape = (step_count,
                                                      control_count)
            pstate.max_control_norms = np.ones(control_count)
            controls = np.random.rand(*controls_shape)
            stripped_controls = strip_controls(pstate.complex_controls,
                                               controls)
            assert (stripped_controls.ndim == 1)
            assert (not (stripped_controls.dtype
                         in (np.complex64, np.complex128)))
            transformed_controls = slap_controls(pstate.complex_controls,
                                                 stripped_controls,
                                                 pstate.controls_shape)
            assert (np.allclose(controls, transformed_controls))
            assert (controls.shape == transformed_controls.shape)
Exemple #2
0
def _esdj_wrap(controls, pstate, reporter, result):
    """
    Do intermediary work between the optimizer feeding controls to 
    the jacobian of _evaluate_schroedinger_discrete.

    Args:
    controls
    pstate
    reporter
    result

    Returns:
    grads
    """
    # Convert the controls from optimizer format to cost function format.
    controls = slap_controls(pstate.complex_controls, controls,
                             pstate.controls_shape)
    # Rescale the controls to their maximum norm.
    clip_control_norms(controls, pstate.max_control_norms)
    # Impose user boundary conditions.
    if pstate.impose_control_conditions is not None:
        controls = pstate.impose_control_conditions(controls)

    # Evaluate the jacobian.
    error, grads = (ans_jacobian(_evaluate_schroedinger_discrete,
                                 0)(controls, pstate, reporter))
    # Autograd defines the derivative of a function of complex inputs as
    # df_dz = du_dx - i * du_dy for z = x + iy, f(z) = u(x, y) + iv(x, y).
    # For optimization, we care about df_dz = du_dx + i * du_dy.
    if pstate.complex_controls:
        grads = np.conjugate(grads)

    # The states need to be unwrapped from their autograd box.
    if isinstance(reporter.final_states, Box):
        final_states = reporter.final_states._value
    else:
        final_states = reporter.final_states

    # Update best configuration.
    if error < result.best_error:
        result.best_controls = controls
        result.best_error = error
        result.best_final_states = final_states
        result.best_iteration = reporter.iteration

    # Save and log optimization progress.
    pstate.log_and_save(controls, error, final_states, grads,
                        reporter.iteration)
    reporter.iteration += 1

    # Convert the gradients from cost function to optimizer format.
    grads = strip_controls(pstate.complex_controls, grads)

    # Determine if optimization should terminate.
    if error <= pstate.min_error:
        terminate = True
    else:
        terminate = False

    return grads, terminate
def _grape_schroedinger_discrete_time(pstate):
    """
    Perform GRAPE for the schroedinger equation with time discrete parameters.
    Use autograd to compute evolution gradients.

    Args:
    pstate :: qoc.models.GrapeSchroedingerDiscreteState - information required to
         perform the optimization

    Returns: 
    result :: qoc.models.GrapeResult - an object that tracks important information
        about the optimization
    """
    # Autograd does not allow multiple return values from
    # a differentiable function.
    # Scipy's minimization algorithms require us to provide
    # functions that they evaluate on their own schedule.
    # The best solution to track mutable objects, that I can think of,
    # is to use a reporter object.
    reporter = Dummy()
    reporter.iteration = 0
    result = GrapeSchroedingerResult()
    # Convert the controls from cost function format to optimizer format.
    initial_controls = strip_controls(pstate.complex_controls,
                                      pstate.initial_controls)
    # Run the optimization.
    pstate.optimizer.run(_esd_wrap,
                         pstate.iteration_count,
                         initial_controls,
                         _esdj_wrap,
                         args=(pstate, reporter, result))
    return result
Exemple #4
0
def test_adam():
    import numpy as np

    from qoc.core.common import (strip_controls, slap_controls)
    from qoc.models.dummy import Dummy
    from qoc.standard.optimizers.adam import Adam

    # Check that the update method was implemented correctly
    # using hand-checked values.
    adam = Adam()
    grads = np.array([[0, 1], [2, 3]])
    params = np.array([[0, 1], [2, 3]], dtype=np.float64)
    params1 = np.array([[0, 0.999], [1.999, 2.999]])
    params2 = np.array([[0, 0.99900003], [1.99900001, 2.99900001]])

    adam.run(None, 0, params, None, None)
    params1_test = adam.update(params, grads)
    params2_test = adam.update(params1, grads)

    assert (np.allclose(params1_test, params1))
    assert (np.allclose(params2_test, params2))

    # Check that complex mapping works and params
    # without gradients are unaffected.
    gstate = Dummy()
    gstate.complex_controls = True
    grads = np.array([[1 + 1j, 0 + 0j], [0 + 0j, -1 - 1j]])
    params = np.array([[1 + 2j, 3 + 4j], [5 + 6j, 7 + 8j]])
    gstate.controls_shape = params.shape
    gstate.max_param_norms = np.ones(gstate.controls_shape[0]) * 10

    flat_controls = strip_controls(gstate.complex_controls, params)
    flat_grads = strip_controls(gstate.complex_controls, grads)

    adam.run(None, 0, flat_controls, None, None)
    params1 = adam.update(flat_grads, flat_controls)
    params1 = slap_controls(gstate.complex_controls, params1,
                            gstate.controls_shape)

    assert (np.allclose(params1[0][1], params[0][1]))
    assert (np.allclose(params1[1][0], params[1][0]))
Exemple #5
0
def _eldj_wrap(controls, pstate, reporter, result):
    """
    Do intermediary work between the optimizer feeding controls to 
    the jacobian of _evaluate_indblad_discrete.

    Args:
    controls
    pstate
    reporter
    result

    Returns:
    grads
    """
    # Convert the controls from optimizer format to cost function format.
    controls = slap_controls(pstate.complex_controls, controls,
                             pstate.controls_shape)
    clip_control_norms(pstate.max_control_norms, controls)

    # Evaluate the jacobian.
    total_error, grads = (ans_jacobian(_evaluate_lindblad_discrete,
                                       0)(controls, pstate, reporter))
    # Autograd defines the derivative of a function of complex inputs as
    # df_dz = du_dx - i * du_dy for z = x + iy, f(z) = u(x, y) + iv(x, y).
    # For optimization, we care about df_dz = du_dx + i * du_dy.
    if pstate.complex_controls:
        grads = conjugate(grads)

    # The states need to be unwrapped from their autograd box.
    if isinstance(reporter.final_densities, Box):
        final_densities = reporter.final_densities._value

    # Update best configuration.
    if total_error < result.best_total_error:
        result.best_controls = controls
        result.best_final_densities = final_densities
        result.best_iteration = reporter.iteration
        result.best_total_error = total_error

    # Save and log optimization progress.
    pstate.log_and_save(
        controls,
        final_densities,
        total_error,
        grads,
        reporter.iteration,
    )
    reporter.iteration += 1

    # Convert the gradients from cost function to optimizer format.
    grads = strip_controls(pstate.complex_controls, grads)

    return grads
Exemple #6
0
def grape_schroedinger_discrete(
    control_count,
    control_eval_count,
    costs,
    evolution_time,
    hamiltonian,
    initial_states,
    system_eval_count,
    complex_controls=False,
    cost_eval_step=1,
    impose_control_conditions=None,
    initial_controls=None,
    interpolation_policy=InterpolationPolicy.LINEAR,
    iteration_count=1000,
    log_iteration_step=10,
    magnus_policy=MagnusPolicy.M2,
    max_control_norms=None,
    min_error=0,
    optimizer=Adam(),
    save_file_path=None,
    save_intermediate_states=False,
    save_iteration_step=0,
):
    """
    This method optimizes the evolution of a set of states under the schroedinger
    equation for time-discrete control parameters.

    Args:
    control_count :: int - This is the number of control parameters that qoc should
        optimize over. I.e. it is the length of the `controls` array passed
        to the hamiltonian.
    control_eval_count :: int >= 2 - This value determines where definite values
        of the control parameters are evaluated. This value is used as:
        `control_eval_times`= numpy.linspace(0, `evolution_time`, `control_eval_count`).
    costs :: iterable(qoc.models.cost.Cost) - This list specifies all
        the cost functions that the optimizer should evaluate. This list
        defines the criteria for an "optimal" control set.
    evolution_time :: float - This value specifies the duration of the
        system's evolution.
    hamiltonian :: (controls :: ndarray (control_count), time :: float)
                   -> hamiltonian_matrix :: ndarray (hilbert_size x hilbert_size)
        - This function provides the system's hamiltonian given a set
        of control parameters and a time value.
    initial_states :: ndarray (state_count x hilbert_size x 1)
        - This array specifies the states that should be evolved under the
        specified system. These are the states at the beginning of the evolution.
    system_eval_count :: int >= 2 - This value determines how many times
        during the evolution the system is evaluated, including the
        initial value of the system. For the schroedinger evolution,
        this value determines the time step of integration.
        This value is used as:
        `system_eval_times` = numpy.linspace(0, `evolution_time`, `system_eval_count`).

    complex_controls :: bool - This value determines if the control parameters
        are complex-valued. If some controls are real only or imaginary only
        while others are complex, real only and imaginary only controls
        can be simulated by taking the real or imaginary part of a complex control.
    cost_eval_step :: int >= 1- This value determines how often step-costs are evaluated.
         The units of this value are in system_eval steps. E.g. if this value is 2,
         step-costs will be computed every 2 system_eval steps.
    impose_control_conditions :: (controls :: (control_eval_count x control_count))
                                 -> (controls :: (control_eval_count x control_count))
        - This function is called after every optimization update. Example uses
        include setting boundary conditions on the control parameters.                             
    initial_controls :: ndarray (control_step_count x control_count)
        - This array specifies the control parameters at each
        control step. These values will be used to determine the `controls`
        argument passed to the `hamiltonian` function at each time step for
        the first iteration of optimization.
    interpolation_policy :: qoc.models.interpolationpolicy.InterpolationPolicy
        - This value specifies how control parameters should be
        interpreted at points where they are not defined.
    iteration_count :: int - This value determines how many total system
        evolutions the optimizer will perform to determine the
        optimal control set.
    log_iteration_step :: int - This value determines how often qoc logs
        progress to stdout. This value is specified in units of system steps,
        of which there are `control_step_count` * `system_step_multiplier`.
        Set this value to 0 to disable logging.
    magnus_policy :: qoc.models.magnuspolicy.MagnusPolicy - This value
        specifies what method should be used to perform the magnus expansion
        of the system matrix for ode integration. Choosing a higher order
        magnus expansion will yield more accuracy, but it will
        result in a longer compute time.
    max_control_norms :: ndarray (control_count) - This array
        specifies the element-wise maximum norm that each control is
        allowed to achieve. If, in optimization, the value of a control
        exceeds its maximum norm, the control will be rescaled to
        its maximum norm. Note that for non-complex values, this
        feature acts exactly as absolute value clipping.
    min_error :: float - This value is the threshold below which
        optimization will terminate.
    optimizer :: class instance - This optimizer object defines the
        gradient-based procedure for minimizing the total contribution
        of all cost functions with respect to the control parameters.
    save_file_path :: str - This is the full path to the file where
        information about program execution will be stored.
        E.g. "./out/foo.h5"
    save_intermediate_densities :: bool - If this value is set to True,
        qoc will write the densities to the save file after every
        system_eval step.
    save_intermediate_states :: bool - If this value is set to True,
        qoc will write the states to the save file after every
        system_eval step.
    save_iteration_step :: int - This value determines how often qoc
        saves progress to the save file specified by `save_file_path`.
        This value is specified in units of system steps, of which
        there are `control_step_count` * `system_step_multiplier`.
        Set this value to 0 to disable saving.

    Returns:
    result :: qoc.models.schroedingermodels.GrapeSchroedingerResult
    """
    # Initialize the controls.
    initial_controls, max_control_norms = initialize_controls(
        complex_controls, control_count, control_eval_count, evolution_time,
        initial_controls, max_control_norms)
    # Construct the program state.
    pstate = GrapeSchroedingerDiscreteState(
        complex_controls,
        control_count,
        control_eval_count,
        cost_eval_step,
        costs,
        evolution_time,
        hamiltonian,
        impose_control_conditions,
        initial_controls,
        initial_states,
        interpolation_policy,
        iteration_count,
        log_iteration_step,
        max_control_norms,
        magnus_policy,
        min_error,
        optimizer,
        save_file_path,
        save_intermediate_states,
        save_iteration_step,
        system_eval_count,
    )
    pstate.log_and_save_initial()

    # Autograd does not allow multiple return values from
    # a differentiable function.
    # Scipy's minimization algorithms require us to provide
    # functions that they evaluate on their own schedule.
    # The best solution to track mutable objects, that I can think of,
    # is to use a reporter object.
    reporter = Dummy()
    reporter.iteration = 0
    result = GrapeSchroedingerResult()
    # Convert the controls from cost function format to optimizer format.
    initial_controls = strip_controls(pstate.complex_controls,
                                      pstate.initial_controls)
    # Run the optimization.
    pstate.optimizer.run(_esd_wrap,
                         pstate.iteration_count,
                         initial_controls,
                         _esdj_wrap,
                         args=(pstate, reporter, result))

    return result
Exemple #7
0
def grape_lindblad_discrete(
    control_count,
    control_step_count,
    costs,
    evolution_time,
    initial_densities,
    complex_controls=False,
    hamiltonian=None,
    initial_controls=None,
    interpolation_policy=InterpolationPolicy.LINEAR,
    iteration_count=1000,
    lindblad_data=None,
    log_iteration_step=10,
    max_control_norms=None,
    minimum_error=0,
    operation_policy=OperationPolicy.CPU,
    optimizer=Adam(),
    save_file_path=None,
    save_iteration_step=0,
    system_step_multiplier=1,
):
    """
    This method optimizes the evolution of a set of states under the lindblad
    equation for time-discrete control parameters.

    Args:
    complex_controls
    control_count
    control_step_count
    costs
    evolution_time
    hamiltonian
    initial_controls
    initial_densities
    interpolation_policy
    iteration_count
    lindblad_data
    log_iteration_step
    max_control_norms
    minimum_error
    operation_policy
    optimizer
    save_file_path
    save_iteration_step
    system_step_multiplier

    Returns:
    result
    """
    # Initialize the controls.
    initial_controls, max_control_norms = initialize_controls(
        complex_controls,
        control_count,
        control_step_count,
        evolution_time,
        initial_controls,
        max_control_norms,
    )
    # Construct the program state.
    pstate = GrapeLindbladDiscreteState(
        complex_controls,
        control_count,
        control_step_count,
        costs,
        evolution_time,
        hamiltonian,
        initial_controls,
        initial_densities,
        interpolation_policy,
        iteration_count,
        lindblad_data,
        log_iteration_step,
        max_control_norms,
        minimum_error,
        operation_policy,
        optimizer,
        save_file_path,
        save_iteration_step,
        system_step_multiplier,
    )
    pstate.log_and_save_initial()

    # Autograd does not allow multiple return values from
    # a differentiable function.
    # Scipy's minimization algorithms require us to provide
    # functions that they evaluate on their own schedule.
    # The best solution to track mutable objects, that I can think of,
    # is to use a reporter object.
    reporter = Dummy()
    reporter.iteration = 0
    result = GrapeLindbladResult()

    # Convert the controls from cost function format to optimizer format.
    initial_controls = strip_controls(pstate.complex_controls,
                                      pstate.initial_controls)

    # Run the optimization.
    pstate.optimizer.run(_eld_wrap,
                         pstate.iteration_count,
                         initial_controls,
                         _eldj_wrap,
                         args=(pstate, reporter, result))

    return result