Beispiel #1
0
def subvector_procedure(e_val, initial_values=None, samples=None, device=None,
                        backend=None, noise=None) -> CallableVector:
    """
    take an expectation value and return its (qng style) gradient as a CallableVector.

    Parameters
    ----------
    e_val: ExpectationValueImpl:
        the expectation value whose gradient is to be obtained
    initial_values: dict, optional:
        a dictionary of initial values with which Objectives in the qgt should be compiled
    samples: int, optional:
        the number of samples with which Objectives in the qgt should be compiled
    device: typing depends on backend, but in general, str (Default value = None):
        the device (real, or simulated, and specific to the backend) with which Objectives in the qgt should be compiled
    backend: str, optional:
        the backend with which Objectives in the qgt should be compiled
    noise: str or NoiseModel, optional:
        the noise model  with which Objectives in the qgt should be compiled
    Returns
    -------
    CallableVector, the gradient of an ExpectationValue in callable format.

    """

    vect = qng_circuit_grad(e_val)
    out = []
    for entry in vect:
        out.append(compile_objective(entry, variables=initial_values, samples=samples, device=device,
                                     backend=backend,
                                     noise=noise))
    return CallableVector(out)
Beispiel #2
0
def subvector_procedure(eval,
                        initial_values=None,
                        samples=None,
                        backend=None,
                        noise=None):
    vect = qng_circuit_grad(eval)
    out = []
    for entry in vect:
        out.append(
            compile_objective(entry,
                              variables=initial_values,
                              samples=samples,
                              backend=backend,
                              noise=noise))
    return CallableVector(out)
Beispiel #3
0
def qng_metric_tensor_blocks(expectation,
                             initial_values=None,
                             samples=None,
                             backend=None,
                             backend_options=None,
                             noise=None):

    U = expectation.U
    moments = U.canonical_moments
    sub = [
        QCircuit.from_moments(moments[:i]) for i in range(1, len(moments), 2)
    ]
    parametric_moms = [moments[i] for i in range(1, len(moments) + 1, 2)]
    generators = []
    for pm in parametric_moms:
        set = []
        if len(pm.gates) != 0:
            for gate in pm.gates:
                gen = get_generator(gate)
                set.append(gen)
        if len(set) != 0:
            generators.append(set)
        else:
            generators.append(None)
    blocks = []
    for i, set in enumerate(generators):
        if set is None:
            pass
        else:
            block = [[0 for _ in range(len(set))] for _ in range(len(set))]
            for k, gen1 in enumerate(set):
                for q, gen2 in enumerate(set):
                    if k == q:
                        arg = (ExpectationValue(U=sub[i], H=gen1 * gen1) -
                               ExpectationValue(U=sub[i], H=gen1)**2) / 4
                    else:
                        arg = (ExpectationValue(U=sub[i], H=gen1 * gen2) -
                               ExpectationValue(U=sub[i], H=gen1) *
                               ExpectationValue(U=sub[i], H=gen2)) / 4
                    block[k][q] = compile_objective(
                        arg,
                        variables=initial_values,
                        samples=samples,
                        backend=backend,
                        noise=noise,
                        backend_options=backend_options)
            blocks.append(block)
    return blocks
Beispiel #4
0
def get_qng_combos(objective,
                   func=stokes_block,
                   initial_values=None,
                   samples=None,
                   backend=None,
                   device=None,
                   noise=None) -> typing.List[typing.Dict]:
    """
    get all the objects needed to evaluate the qng for some objective; return them in a list of dictionaries.

    Parameters
    ----------
    objective: Objective:
        the Objective whose qng is sought.
    func: callable: (Default = stokes_block):
        the function used to obtain the (blocks of) the qgt. Default uses stokes_block, defined above.
    initial_values: dict, optional:
        a dictionary indicating the intial parameters with which to compile all objectives appearing in the qng.
    samples: int, optional:
        the number of samples with which to compile all objectives appearing in the qng. Default: none.
    backend: str, optional:
        the backend with which to compile all objectives appearing in the qng. default: pick for you.
    device: optional:
        the device with which to compile all objectives appearing in the qng. Default: no device use or emulation.
    noise: str or NoiseModel, optional:
        the noise model with which to compile all objectives appearing in the qng. Default: no noise.

    Returns
    -------
    list of dicts:
        a list of dictionaries, each entry corresponding to the qng for 1 argument of objective, in the order
        of said objectives.

    """

    combos = []
    vars = objective.extract_variables()
    compiled = compile_multitarget(gate=objective)
    compiled = compile_trotterized_gate(gate=compiled)
    compiled = compile_h_power(gate=compiled)
    compiled = compile_power_gate(gate=compiled)
    compiled = compile_controlled_phase(gate=compiled)
    compiled = compile_controlled_rotation(gate=compiled)
    for i, arg in enumerate(compiled.args):
        if not isinstance(arg, ExpectationValueImpl):
            ### this is a variable, no QNG involved
            mat = QngMatrix([[[1]]])
            vec = CallableVector([__grad_inner(arg, arg)])
            mapping = {0: {v: __grad_inner(arg, v) for v in vars}}
        else:
            ### if the arg is an expectationvalue, we need to build some qngs and mappings!
            blocks = func(arg,
                          initial_values=initial_values,
                          samples=samples,
                          device=device,
                          backend=backend,
                          noise=noise)
            mat = QngMatrix(blocks)

            vec = subvector_procedure(arg,
                                      initial_values=initial_values,
                                      samples=samples,
                                      device=device,
                                      backend=backend,
                                      noise=noise)

            mapping = {}
            self_pars = get_self_pars(arg.U)
            for j, p in enumerate(self_pars):
                indict = {}
                for v in p.extract_variables():
                    gi = __grad_inner(p, v)
                    if isinstance(gi, Objective):
                        g = compile_objective(gi,
                                              variables=initial_values,
                                              samples=samples,
                                              device=device,
                                              backend=backend,
                                              noise=noise)
                    else:
                        g = gi
                    indict[v] = g
                mapping[j] = indict

        posarg = jax.grad(compiled.transformation, i)

        p = Objective(compiled.args, transformation=posarg)

        pos = compile_objective(p,
                                variables=initial_values,
                                samples=samples,
                                device=device,
                                backend=backend,
                                noise=noise)
        combos.append(qng_dict(arg, mat, vec, mapping, pos))
    return combos
Beispiel #5
0
def stokes_block(
    expectation,
    initial_values=None,
    samples=None,
    device=None,
    backend=None,
    noise=None
) -> typing.List[typing.List[typing.List[typing.Union[float, Objective]]]]:
    """
    returns the blocks of the layerwise block-diagonal approximation to the qgt.
    The default for all qng-based optimizations, as a method for obtaining the qgt.
    See: Stokes et. al, https://arxiv.org/abs/1909.02108

    Parameters
    ----------
    expectation: ExpectationValueImpl:
        the expectation value whose qgt is to be built.
    initial_values: dict, optional:
        a dictionary of initial values with which Objectives in the qgt should be compiled
    samples: int, optional:
        the number of samples with which Objectives in the qgt should be compiled
    device: optional:
        the device (real, or simulated, and specific to the backend) with which Objectives in the qgt
        should be compiled. Generally, a string, but backend specific types also accepted.
    backend: str, optional:
        the backend with which Objectives in the qgt should be compiled
    noise: str or NoiseModel, optional:
        the noise model  with which Objectives in the qgt should be compiled

    Returns
    -------
    list of list of lists:
        list of list of lists representing the blocks of the block diagonal layerwise approx to the qgt.

    """

    U = expectation.U
    ### orders the circuit into alternating layer ansatz, where a moment is all simultaneous gates
    moments = U.canonical_moments
    ### rebuild the sub circuits used in the expectation values that populate the QGT
    sub = [
        QCircuit.from_moments(moments[:i]) for i in range(1, len(moments), 2)
    ]
    ### this is the list of just the moments which are parametrized.
    parametric_moms = [moments[i] for i in range(1, len(moments) + 1, 2)]
    generators = []
    ### generators is a list of lists, ultimately, where each sublist is all the generators in order
    ### for a given parametric layer (if said layer is
    ### occupied, which it might not be! a layer can be nothing, I.E, the identity.)
    for pm in parametric_moms:
        set = []
        if len(pm.gates) != 0:
            for gate in pm.gates:
                ### get_generator takes a gaussian gate, and returns the pauli that is its generator.
                ### See that function for detail.
                gen = get_generator(gate)
                set.append(gen)
        if len(set) != 0:
            generators.append(set)
        else:
            ### blank sets get passed over
            generators.append(None)
    blocks = []
    for i, set in enumerate(generators):
        if set is None:
            pass
        else:
            ### a block is a list of lists, and indexing it should correspond to indexing a matrix in A[row][column] fashion.
            ### alternate functions could have the whole QGT be a single block, but you need to have as a return a List of (List of Lists)!!!!
            block = [[0 for _ in range(len(set))] for _ in range(len(set))]
            for k, gen1 in enumerate(set):
                for q, gen2 in enumerate(set):
                    ### make sure you compile the objectives! otherwise this bad boy will not run
                    if k == q:
                        arg = (ExpectationValue(U=sub[i], H=gen1 * gen1) -
                               ExpectationValue(U=sub[i], H=gen1)**2) / 4
                    else:
                        arg = (ExpectationValue(U=sub[i], H=gen1 * gen2) -
                               ExpectationValue(U=sub[i], H=gen1) *
                               ExpectationValue(U=sub[i], H=gen2)) / 4
                    block[k][q] = compile_objective(arg,
                                                    variables=initial_values,
                                                    samples=samples,
                                                    backend=backend,
                                                    device=device,
                                                    noise=noise)
            blocks.append(block)
    return blocks
    def __call__(self, objective: Objective,
                 maxiter=None,
                 variables: typing.List[Variable] = None,
                 initial_values: typing.Dict[Variable, numbers.Real] = None,
                 previous=None,
                 phoenics_config=None,
                 save_to_file=False,
                 file_name=None,
                 *args,
                 **kwargs):

        active_angles, passive_angles, variables = self.initialize_variables(objective,
                                                               initial_values=initial_values,
                                                               variables=variables)

        if maxiter is None:
            maxiter = 10

        obs = []
        bird = self._make_phoenics_object(objective, passive_angles, phoenics_config, *args, **kwargs)
        if previous is not None:
            if type(previous) is str:
                try:
                    obs = pickle.load(open(previous, 'rb'))
                except:
                    print(
                        'failed to load previous observations, which are meant to be a pickle file. Starting fresh.')
            elif type(previous) is list:
                if all([type(k) == dict for k in previous]):
                    obs = previous
                else:
                    print('previous observations were not in the correct format (list of dicts). Starting fresh.')



        if not (type(file_name) == str or file_name == None):
            raise TequilaException('file_name must be a string, or None!')

        best = None
        best_angles = None

        # avoid multiple compilations
        compiled_objective = compile_objective(objective=objective, backend=self.backend,
                                               backend_options=self.backend_options,
                                               samples=self.samples, noise=self.noise)

        if not self.silent:
            print('phoenics has recieved')
            print("objective: \n")
            print(objective)
            print("noise model : {}".format(self.noise))
            print("samples     : {}".format(self.samples))
            print("maxiter     : {}".format(maxiter))
            print("variables   : {}".format(objective.extract_variables()))
            print("passive var : {}".format(passive_angles))
            print("backend options {} ".format(self.backend), self.backend_options)
            print('now lets begin')
        for i in range(0, maxiter):
            with warnings.catch_warnings():
                np.testing.suppress_warnings()
                warnings.simplefilter("ignore")
                warnings.filterwarnings("ignore", category=FutureWarning)

            if len(obs) >= 1:
                precs = bird.recommend(observations=obs)
            else:
                precs = bird.recommend()

            runs = []
            recs = self._process_for_sim(precs, passive_angles=passive_angles)

            start = time.time()
            for j, rec in enumerate(recs):
                En = compiled_objective(variables=rec, samples=self.samples, noise=self.noise,
                                        backend_options=self.backend_options)
                runs.append((rec, En))
                if not self.silent:
                    if self.print_level > 2:
                        print("energy = {:+2.8f} , angles=".format(En), rec)
                    else:
                        print("energy = {:+2.8f}".format(En))
            stop = time.time()
            if not self.silent:
                print("Quantum Objective evaluations: {}s Wall-Time".format(stop-start))

            for run in runs:
                angles = run[0]
                E = run[1]
                if best is None:
                    best = E
                    best_angles = angles
                else:
                    if self._minimize:
                        if E < best:
                            best = E
                            best_angles = angles
                    else:
                        if E > best:
                            best = E
                            best_angles = angles

                if self.save_history:
                    self.history.energies.append(E)
                    self.history.angles.append(angles)
                obs.append(self._process_for_phoenics(angles, E, passive_angles=passive_angles))

        if file_name is not None:
            with open(file_name, 'wb') as file:
                pickle.dump(obs, file)

        if not self.silent:
            print("best energy after {} iterations : {:+2.8f}".format(self.maxiter, best))
        return PhoenicsReturnType(energy=best, angles=best_angles, history=self.history, observations=obs,object=bird)
Beispiel #7
0
    def __call__(self, objective: Objective,
                 maxiter: int = None,
                 passives: typing.Dict[Variable, numbers.Real] = None,
                 samples: int = None,
                 backend: str = None,
                 noise=None,
                 previous=None,
                 phoenics_config=None,
                 save_to_file=False,
                 file_name=None,
                 *args,
                 **kwargs):

        backend_options = {}
        if 'backend_options' in kwargs:
            backend_options = kwargs['backend_options']

        if maxiter is None:
            maxiter = 10

        bird = self._make_phoenics_object(objective, passives, phoenics_config, *args, **kwargs)
        if previous is not None:
            if type(previous) is str:
                try:
                    obs = pickle.load(open(previous, 'rb'))
                except:
                    print(
                        'failed to load previous observations, which are meant to be a pickle file. Please try again or seek assistance. Starting fresh.')
                    obs = []
            elif type(previous) is list:
                if all([type(k) == dict for k in previous]):
                    obs = previous
                else:
                    print(
                        'previous observations were not in the correct format (list of dicts). Are you sure you gave me the right info? Starting fresh.')
                    obs = []

        else:
            obs = []

        if save_to_file is True:
            if type(file_name) is str:
                pass
            elif file_name is None:
                raise TequilaException(
                    'You have asked me to save phoenics observations without telling me where to do so! please provide a file_name')
            else:
                raise TequilaException('file_name must be a string!')

        ### this line below just gets the damn compiler to run, since that argument is necessary
        init = {key: np.pi for key in objective.extract_variables()}

        best = None
        best_angles = None

        # avoid multiple compilations
        compiled_objective = compile_objective(objective=objective, backend=backend, samples=samples, noise=noise)

        if not self.silent:
            print('phoenics has recieved')
            print("objective: \n")
            print(objective)
            print("noise model : {}".format(noise))
            print("samples     : {}".format(samples))
            print("maxiter     : {}".format(maxiter))
            print("variables   : {}".format(objective.extract_variables()))
            print("passive var : {}".format(passives))
            print("backend options {} ".format(backend), backend_options)
            print('now lets begin')
        for i in range(0, maxiter):
            with warnings.catch_warnings():
                np.testing.suppress_warnings()
                warnings.simplefilter("ignore")
                warnings.filterwarnings("ignore", category=FutureWarning)

            if len(obs) >= 1:
                precs = bird.recommend(observations=obs)
            else:
                precs = bird.recommend()

            runs = []
            recs = self._process_for_sim(precs, passives=passives)

            start = time.time()
            for i, rec in enumerate(recs):
                En = compiled_objective(variables=rec, samples=samples, noise=noise, **backend_options)
                runs.append((rec, En))
                if not self.silent:
                    print("energy = {:+2.8f} , angles=".format(En), rec)
            stop = time.time()
            if not self.silent:
                print("Quantum Objective evaluations: {}s Wall-Time".format(stop-start))

            for run in runs:
                angles = run[0]
                E = run[1]
                if best is None:
                    best = E
                    best_angles = angles
                else:
                    if self._minimize:
                        if E < best:
                            best = E
                            best_angles = angles
                    else:
                        if E > best:
                            best = E
                            best_angles = angles

                if self.save_history:
                    self.history.energies.append(E)
                    self.history.angles.append(angles)
                obs.append(self._process_for_phoenics(angles, E, passives=passives))

        if save_to_file is True:
            with open(file_name, 'wb') as file:
                pickle.dump(obs, file)

        if not self.silent:
            print("best energy after {} iterations : {:+2.8f}".format(self.maxiter, best))
        return PhoenicsReturnType(energy=best, angles=best_angles, history=self.history, observations=obs)
Beispiel #8
0
def get_qng_combos(objective,
                   initial_values=None,
                   samples=None,
                   backend=None,
                   backend_options=None,
                   noise=None):
    combos = []
    vars = objective.extract_variables()
    compiled = compile_multitarget(gate=objective)
    compiled = compile_trotterized_gate(gate=compiled)
    compiled = compile_h_power(gate=compiled)
    compiled = compile_power_gate(gate=compiled)
    compiled = compile_controlled_phase(gate=compiled)
    compiled = compile_controlled_rotation(gate=compiled)
    for i, arg in enumerate(compiled.args):
        if not isinstance(arg, ExpectationValueImpl):
            ### this is a variable, no QNG involved
            mat = QngMatrix([[[1]]])
            vec = CallableVector([__grad_inner(arg, arg)])
            mapping = {0: {v: __grad_inner(arg, v) for v in vars}}
        else:
            ### if the arg is an expectationvalue, we need to build some qngs and mappings!
            blocks = qng_metric_tensor_blocks(arg,
                                              initial_values=initial_values,
                                              samples=samples,
                                              backend=backend,
                                              noise=noise,
                                              backend_options=backend_options)
            mat = QngMatrix(blocks)

            vec = subvector_procedure(arg,
                                      initial_values=initial_values,
                                      samples=samples,
                                      backend=backend,
                                      noise=noise,
                                      backend_options=backend_options)

            mapping = {}
            self_pars = get_self_pars(arg.U)
            for j, p in enumerate(self_pars):
                indict = {}
                for v in p.extract_variables():
                    gi = __grad_inner(p, v)
                    if isinstance(gi, Objective):
                        g = compile_objective(gi,
                                              variables=initial_values,
                                              samples=samples,
                                              backend=backend,
                                              noise=noise,
                                              backend_options=backend_options)
                    else:
                        g = gi
                    indict[v] = g
                mapping[j] = indict

        posarg = jax.grad(compiled.transformation, argnums=i)
        p = Objective(compiled.args, transformation=posarg)

        pos = compile_objective(p,
                                variables=initial_values,
                                samples=samples,
                                backend=backend,
                                noise=noise,
                                backend_options=backend_options)
        combos.append(qng_dict(arg, mat, vec, mapping, pos))
    return combos
    def __call__(self,
                 objective: Objective,
                 maxiter=None,
                 variables: typing.List[Variable] = None,
                 initial_values: typing.Dict[Variable, numbers.Real] = None,
                 previous=None,
                 phoenics_config=None,
                 file_name=None,
                 *args,
                 **kwargs):
        """
        Perform optimization with phoenics.

        Parameters
        ----------
        objective: Objective
            the objective to optimize.
        maxiter: int:
            (Default value = None)
            if not None, overwrite the init maxiter with new number.
        variables: list:
            (Default value = None)
            which variables to optimize over. If None: all of the variables in objective are used.
        initial_values: dict:
            (Default value = None)
            an initial point to begin optimization from. Random, if None.
        previous:
            previous observations, formatted for phoenics, to use in optimization. For use by advanced users.
        phoenics_config:
            a config for a phoenics object.
        file_name:
            a file
        args
        kwargs

        Returns
        -------
        PhoenicsResults:
            the results of optimization by phoenics.

        """

        objective = objective.contract()
        active_angles, passive_angles, variables = self.initialize_variables(
            objective, initial_values=initial_values, variables=variables)

        if maxiter is None:
            maxiter = 10

        obs = []
        bird = self._make_phoenics_object(objective, passive_angles,
                                          phoenics_config, *args, **kwargs)
        if previous is not None:
            if type(previous) is str:
                try:
                    obs = pickle.load(open(previous, 'rb'))
                except:
                    print(
                        'failed to load previous observations, which are meant to be a pickle file. Starting fresh.'
                    )
            elif type(previous) is list:
                if all([type(k) == dict for k in previous]):
                    obs = previous
                else:
                    print(
                        'previous observations were not in the correct format (list of dicts). Starting fresh.'
                    )

        if not (type(file_name) == str or file_name == None):
            raise TequilaException(
                'file_name must be a string, or None. Recieved {}'.format(
                    type(file_name)))

        best = None
        best_angles = None

        # avoid multiple compilations
        compiled_objective = compile_objective(objective=objective,
                                               backend=self.backend,
                                               device=self.device,
                                               samples=self.samples,
                                               noise=self.noise)

        if not self.silent:
            print('phoenics has recieved')
            print("objective: \n")
            print(objective)
            print("noise model : {}".format(self.noise))
            print("samples     : {}".format(self.samples))
            print("maxiter     : {}".format(maxiter))
            print("variables   : {}".format(objective.extract_variables()))
            print("passive var : {}".format(passive_angles))
            print('now lets begin')
        for i in range(0, maxiter):
            with warnings.catch_warnings():
                np.testing.suppress_warnings()
                warnings.simplefilter("ignore")
                warnings.filterwarnings("ignore", category=FutureWarning)

            precs = bird.recommend(observations=obs)

            runs = []
            recs = self._process_for_sim(precs, passive_angles=passive_angles)

            start = time.time()
            for j, rec in enumerate(recs):
                En = compiled_objective(variables=rec,
                                        samples=self.samples,
                                        noise=self.noise)
                runs.append((rec, En))
                if not self.silent:
                    if self.print_level > 2:
                        print("energy = {:+2.8f} , angles=".format(En), rec)
                    else:
                        print("energy = {:+2.8f}".format(En))
            stop = time.time()
            if not self.silent:
                print("Quantum Objective evaluations: {}s Wall-Time".format(
                    stop - start))

            for run in runs:
                angles = run[0]
                E = run[1]
                if best is None:
                    best = E
                    best_angles = angles
                else:
                    if self._minimize:
                        if E < best:
                            best = E
                            best_angles = angles
                    else:
                        if E > best:
                            best = E
                            best_angles = angles

                if self.save_history:
                    self.history.energies.append(E)
                    self.history.angles.append(angles)
                obs.append(
                    self._process_for_phoenics(angles,
                                               E,
                                               passive_angles=passive_angles))

        if file_name is not None:
            with open(file_name, 'wb') as file:
                pickle.dump(obs, file)

        if not self.silent:
            print("best energy after {} iterations : {:+2.8f}".format(
                self.maxiter, best))
        return PhoenicsResults(energy=best,
                               variables=best_angles,
                               history=self.history,
                               observations=obs,
                               phoenics_instance=bird)