Exemplo n.º 1
0
def preamble(objective: Objective,
             compile_args: dict = None,
             input_vars: list = None):
    """
    Helper function for interfaces to ml backends.
    Parameters
    ----------
    objective: Objective:
        the objective to manipulate and compile.
    compile_args: dict, optional:
        a dictionary of args that can be passed as kwargs to tq.compile
    input_vars: list, optional:
        a list of variables of the objective to specify as input, rather than itnernal weights.

    Returns
    -------
    tuple
        the compiled objective, it's compile arguments, its weight variables, dicts for the weight and input gradients,
        and a dictionary that links positions in an array to each variable (parses parameters).
    """
    def var_sorter(e):
        return hash(e.name)

    all_vars = objective.extract_variables()
    all_vars.sort(key=var_sorter)
    compile_args = check_compiler_args(compile_args)

    weight_vars = []
    if input_vars is None:
        input_vars = []
        weight_vars = all_vars
    else:
        input_vars = [assign_variable(v) for v in input_vars]
        for var in all_vars:
            if var not in input_vars:
                weight_vars.append(assign_variable(var))

    init_vals = compile_args['initial_values']
    if init_vals is not None:
        for k in init_vals.keys():
            if assign_variable(k) in input_vars:
                raise TequilaMLException(
                    'initial_values contained key {},'
                    'which is meant to be an input variable.'.format(k))
        compile_args['initial_values'] = format_variable_dictionary(init_vals)

    comped = compile(objective, **compile_args)
    gradients = get_gradients(objective, compile_args)
    w_grad, i_grad = separate_gradients(gradients,
                                        weight_vars=weight_vars,
                                        input_vars=input_vars)
    first, second = get_variable_orders(weight_vars, input_vars)
    return comped, compile_args, input_vars, weight_vars, i_grad, w_grad, first, second
Exemplo n.º 2
0
def _initialize_power_gate(name: str,
                           target: typing.Union[list, int],
                           generator,
                           control: typing.Union[list, int] = None,
                           power=None,
                           angle=None) -> QCircuit:
    target = list_assignment(target)

    if angle is not None:
        angle = assign_variable(angle)
        if power is not None:
            power = power * angle / np.pi
        else:
            power = angle / np.pi

    if power is None or power in [1, 1.0]:
        gates = [
            impl.QGateImpl(name=name,
                           target=q,
                           control=control,
                           generator=generator(q)) for q in target
        ]
    else:
        gates = [
            impl.PowerGateImpl(name=name,
                               power=power,
                               target=q,
                               control=control,
                               generator=generator(q)) for q in target
        ]

    return QCircuit.wrap_gate(gates)
Exemplo n.º 3
0
 def __init__(self,
              name,
              parameter: UnionParam,
              target: UnionList,
              control: UnionList = None):
     super().__init__(name=name, target=target, control=control)
     self._parameter = assign_variable(variable=parameter)
Exemplo n.º 4
0
    def __call__(self, wfn: QubitWaveFunction) -> QCircuit:
        """
        :param coeffs: The QubitWaveFunction you want to initialize
        :return:
        """
        try:
            assert (len(wfn) == len(self._target_space))
            for key in wfn.keys():
                try:
                    assert (key in self._target_space)
                except AssertionError:
                    print("key=", key.binary, " not found in target space")
        except AssertionError:
            raise TequilaException(
                "UnaryStatePrep was not initialized for the basis states in your wavefunction\n"
                "You gave:\n" + str(wfn) + "\n"
                "But the target_space is " +
                str([k.binary for k in self._target_space]) + "\n")

        angles = self._evaluate_angles(wfn=wfn)

        # construct new circuit with evaluated angles
        result = QCircuit()
        for g in self._abstract_circuit.gates:
            g2 = copy.deepcopy(g)
            if hasattr(g, "parameter"):
                symbol = g.parameter
                # the module needs repairing ....
                g2._parameter = assign_variable(
                    -angles[-symbol()]
                )  # the minus follows mahas convention since the circuits are daggered in the end
            result += g2

        return result
Exemplo n.º 5
0
 def angles(self,
            wfn: QubitWaveFunction) -> typing.Dict[typing.Hashable, float]:
     sympy_angles = self._evaluate_angles(wfn=wfn)
     angles = {
         assign_variable(str(key)): value
         for key, value in sympy_angles.items()
     }
     return angles
Exemplo n.º 6
0
def grad(objective: typing.Union[Objective, VectorObjective],
         variable: Variable = None,
         no_compile=False,
         *args,
         **kwargs):
    '''
    wrapper function for getting the gradients of Objectives,ExpectationValues, Unitaries (including single gates), and Transforms.
    :param obj (QCircuit,ParametrizedGateImpl,Objective,ExpectationValue,Transform,Variable): structure to be differentiated
    :param variables (list of Variable): parameter with respect to which obj should be differentiated.
        default None: total gradient.
    return: dictionary of Objectives, if called on gate, circuit, exp.value, or objective; if Variable or Transform, returns number.
    '''

    if variable is None:
        # None means that all components are created
        variables = objective.extract_variables()
        result = {}

        if len(variables) == 0:
            raise TequilaException(
                "Error in gradient: Objective has no variables")

        for k in variables:
            assert (k is not None)
            result[k] = grad(objective, k, no_compile=no_compile)
        return result
    else:
        variable = assign_variable(variable)

    if no_compile:
        compiled = objective
    else:
        compiler = Compiler(multitarget=True,
                            trotterized=True,
                            hadamard_power=True,
                            power=True,
                            controlled_phase=True,
                            controlled_rotation=True,
                            gradient_mode=True)

        compiled = compiler(objective, variables=[variable])

    if variable not in compiled.extract_variables():
        raise TequilaException(
            "Error in taking gradient. Objective does not depend on variable {} "
            .format(variable))

    if isinstance(objective, ExpectationValueImpl):
        return __grad_expectationvalue(E=objective, variable=variable)
    elif objective.is_expectationvalue():
        return __grad_expectationvalue(E=compiled.args[-1], variable=variable)
    elif isinstance(compiled, Objective) or isinstance(compiled,
                                                       VectorObjective):
        return __grad_objective(objective=compiled, variable=variable)
    else:
        raise TequilaException(
            "Gradient not implemented for other types than ExpectationValue and Objective."
        )
Exemplo n.º 7
0
def ExpPauli(paulistring: typing.Union[PauliString, str],
             angle,
             control: typing.Union[list, int] = None):
    """Exponentiated Pauligate:
    
    ExpPauli(PauliString, angle) = exp(-i* angle/2* PauliString)

    Parameters
    ----------
    paulistring :
        given as PauliString structure or as string or dict or list
        if given as string: Format should be like X(0)Y(3)Z(2)
        if given as list: Format should be like [(0,'X'),(3,'Y'),(2,'Z')]
        if given as dict: Format should be like { 0:'X', 3:'Y', 2:'Z' }
    angle :
        the angle (will be multiplied by paulistring coefficient if there is one)
    control :
        control qubits
    paulistring: typing.Union[PauliString :
        
    str] :
        
    control: typing.Union[list :
        
    int] :
         (Default value = None)

    Returns
    -------
    type
        Gate wrapped in circuit

    """

    if isinstance(paulistring, str):
        ps = PauliString.from_string(string=paulistring)
    elif isinstance(paulistring, list):
        ps = PauliString.from_openfermion(key=list)
    elif isinstance(paulistring, dict):
        ps = PauliString(data=paulistring)
    else:
        ps = paulistring

    # Failsave: If the paulistring contains just one pauli matrix
    # it is better to initialize a rotational gate due to strange conventions in some simulators
    if len(ps.items()) == 1:
        target, axis = tuple(ps.items())[0]
        return QCircuit.wrap_gate(
            impl.RotationGateImpl(axis=axis,
                                  target=target,
                                  angle=ps.coeff * assign_variable(angle),
                                  control=control))
    else:
        return QCircuit.wrap_gate(
            impl.ExponentialPauliGateImpl(paulistring=ps,
                                          angle=angle,
                                          control=control))
Exemplo n.º 8
0
def Trotterized(generators: typing.List[QubitHamiltonian],
                steps: int,
                angles: typing.Union[
                    typing.List[typing.Hashable], typing.List[numbers.Real], typing.List[Variable]] = None,
                control: typing.Union[list, int] = None,
                parameters: TrotterParameters = None) -> QCircuit:
    """

    Parameters
    ----------
    generators :
        list of generators
    angles :
        coefficients for each generator
    steps :
        trotter steps
    control :
        control qubits
    parameters :
        Additional Trotter parameters, if None then defaults are used
    generators: typing.List[QubitHamiltonian] :
        
    steps: int :
        
    angles: typing.Union[typing.List[typing.Hashable] :
        
    typing.List[numbers.Real] :
        
    typing.List[Variable]] :
         (Default value = None)
    control: typing.Union[list :
        
    int] :
         (Default value = None)
    parameters: TrotterParameters :
         (Default value = None)

    Returns
    -------
    QCircuit

    """

    # convenience
    if not (isinstance(generators, list) or isinstance(generators, tuple)):
        generators = [generators]
    if not (isinstance(angles, list) or isinstance(angles, tuple)):
        angles = [angles]

    if parameters is None:
        parameters = TrotterParameters()

    assigned_angles = [assign_variable(angle) for angle in angles]

    return QCircuit.wrap_gate(
        TrotterizedGateImpl(generators=generators, angles=assigned_angles, steps=steps, control=control,
                            **parameters.__dict__))
Exemplo n.º 9
0
 def extract_angles(self, key: str) -> typing.Dict[numbers.Integral, numbers.Real]:
     """
     :param key: the key specifiying which angle shall be extracted
     :return: dictionary with dictionary_key=iteration, dictionary_value=angle[key]
     """
     angles = {}
     for i, d in enumerate(self.angles):
         if key in d:
             angles[i] = d[assign_variable(key)]
     return angles
Exemplo n.º 10
0
 def extract_gradients(self, key: str) -> typing.Dict[numbers.Integral, numbers.Real]:
     """
     :param key: the key specifiying which gradient shall be extracted
     :return: dictionary with dictionary_key=iteration, dictionary_value=gradient[key]
     """
     gradients = {}
     for i, d in enumerate(self.gradients):
         if key in d:
             gradients[i] = d[assign_variable(key)]
     return gradients
Exemplo n.º 11
0
 def __init__(self,
              name,
              parameter: UnionParam,
              target: UnionList,
              control: UnionList = None):
     super().__init__(name=name, target=target, control=control)
     if isinstance(parameter, VectorObjective):
         raise TequilaException(
             'Received VectorObjective {} as parameter. This is forbidden.'.
             format(parameter))
     self._parameter = assign_variable(variable=parameter)
Exemplo n.º 12
0
    def __init__(self,
                 method: str = "L-BFGS-B",
                 tol: numbers.Real = None,
                 method_options=None,
                 method_bounds=None,
                 method_constraints=None,
                 silent: bool = True,
                 **kwargs):
        """
        Parameters
        ----------
        method: str: Default = 'L-BFGS-B':
            The scipy optimization method passed as string.
        tol: float, optional:
            See scipy documentation for the method you picked
        method_options: optional:
            See scipy documentation for the method you picked
        method_bounds: optional:
            See scipy documentation for the method you picked
        method_constraints: optional:
            See scipy documentation for the method you picked
        silent: bool:
            if False the optimizer prints out all evaluated energies
        """
        super().__init__(**kwargs)
        if hasattr(method, "upper"):
            self.method = method.upper()
        else:
            self.method = method
        self.tol = tol
        self.method_options = method_options

        if method_bounds is not None:
            method_bounds = {
                assign_variable(k): v
                for k, v in method_bounds.items()
            }
        self.method_bounds = method_bounds
        self.silent = silent

        if method_options is None:
            self.method_options = {'maxiter': self.maxiter}
        else:
            self.method_options = method_options
            if 'maxiter' not in method_options:
                self.method_options['maxiter'] = self.maxiter

        self.method_options['disp'] = not silent

        if method_constraints is None:
            self.method_constraints = ()
        else:
            self.method_constraints = method_constraints
Exemplo n.º 13
0
def GeneralizedRotation(angle: typing.Union[typing.List[typing.Hashable],
                                            typing.List[numbers.Real]],
                        generator: QubitHamiltonian,
                        control: typing.Union[list, int] = None,
                        eigenvalues_magnitude: float = 0.5,
                        steps: int = 1,
                        assume_real=False) -> QCircuit:
    """

    Notes
    --------
    
    A gates which is shift-rule differentiable
     - its generator only has two distinguishable eigenvalues
     - it is then differentiable by the shift rule
     - eigenvalues_magnitude needs to be given upon initialization (this is "r" from Schuld et. al. and the default is r=1/2)
     - the generator will not (!) be verified to fullfill the properties
     Compiling will be done in analogy to a trotterized gate with steps=1 as default

    The gate will act in the same way as rotations and exppauli gates

    .. math::
        U_{G}(\\text{angle}) = e^{-i\\frac{\\text{angle}}{2} G}
    
    Parameters
    ----------
    angle
        numeric type or hashable symbol or tequila objective
    generator
        tequila QubitHamiltonian or any other structure with paulistrings
    control
        list of control qubits
    eigenvalues_magnitude
        magnitude of eigenvalues, in most papers referred to as "r" (default 0.5)
    steps
        possible Trotterization steps (default 1)

    Returns
    -------
    The gate wrapped in a circuit
    """

    return QCircuit.wrap_gate(
        impl.GeneralizedRotationImpl(
            angle=assign_variable(angle),
            generator=generator,
            control=control,
            eigenvalues_magnitude=eigenvalues_magnitude,
            steps=steps,
            assume_real=assume_real))
Exemplo n.º 14
0
    def get_circuit(self):
        """
        :return: Return the abstract circuit with tequila parameters
        """
        result = QCircuit()
        for g in self._abstract_circuit.gates:
            g2 = copy.deepcopy(g)
            if hasattr(g, "parameter"):
                symbol = g.parameter
                name = str(-symbol)  # kill the minus from the dagger
                g2._parameter = assign_variable(name)
            result += g2

        return result
Exemplo n.º 15
0
    def __init__(self,
                 method: str = "L-BFGS-B",
                 tol: numbers.Real = None,
                 method_options=None,
                 method_bounds=None,
                 method_constraints=None,
                 silent: bool = True,
                 **kwargs):
        """
        Optimize a circuit to minimize a given objective using scipy
        See the Optimizer class for all other parameters to initialize
        :param method: The scipy method passed as string
        :param use_gradient: do gradient based optimization
        :param tol: See scipy documentation for the method you picked
        :param method_options: See scipy documentation for the method you picked
        :param method_bounds: See scipy documentation for the method you picked
        :param method_constraints: See scipy documentation for the method you picked
        :param silent: if False the optimizer print out all evaluated energies
        :param use_gradient: select if gradients shall be used. Can be done automatically for most methods
        """
        super().__init__(**kwargs)
        if hasattr(method, "upper"):
            self.method = method.upper()
        else:
            self.method = method
        self.tol = tol
        self.method_options = method_options

        if method_bounds is not None:
            method_bounds = {
                assign_variable(k): v
                for k, v in method_bounds.items()
            }
        self.method_bounds = method_bounds
        self.silent = silent

        if method_options is None:
            self.method_options = {'maxiter': self.maxiter}
        else:
            self.method_options = method_options
            if 'maxiter' not in method_options:
                self.method_options['maxiter'] = self.maxiter

        self.method_options['disp'] = not silent

        if method_constraints is None:
            self.method_constraints = ()
        else:
            self.method_constraints = method_constraints
Exemplo n.º 16
0
    def extract_gradients(self, key: str) -> typing.Dict[numbers.Integral, numbers.Real]:
        """
        convenience function to get the gradients of some variable out of the history.
        Parameters
        ----------
        key: str:
            the name of the variable whose gradients are sought

        Returns
        -------
        dict:
            a dictionary, representing the gradient of variable 'key' over time.
        """
        gradients = {}
        for i, d in enumerate(self.gradients):
            if key in d:
                gradients[i] = d[assign_variable(key)]
        return gradients
Exemplo n.º 17
0
    def __init__(self,
                 angle,
                 target=None,
                 generator=None,
                 p0=None,
                 assume_real=True,
                 control=None,
                 compile_options=None):
        angle = assign_variable(angle)

        if generator is None:
            assert target is not None
            assert p0 is None
            generator = paulis.I()
            p0a = paulis.I()
            p0b = paulis.I()

            for i in range(len(target) // 2):
                generator *= paulis.Sp(target[2 * i]) * paulis.Sm(
                    target[2 * i + 1])
                p0a *= paulis.Qp(target[2 * i]) * paulis.Qm(target[2 * i + 1])
                p0b *= paulis.Qm(target[2 * i]) * paulis.Qp(target[2 * i + 1])
            generator = (1.0j * (generator - generator.dagger())).simplify()
            p0 = paulis.I() - p0a - p0b
        else:
            assert generator is not None
            assert p0 is not None

        super().__init__(name="QubitExcitation",
                         parameter=angle,
                         target=self.extract_targets(generator),
                         control=control)
        self.generator = generator
        if control is not None:
            # augment p0 for control qubits
            # Qp = 1/2(1+Z) = |0><0|
            p0 = p0 * paulis.Qp(control)
        self.p0 = p0
        self.assume_real = assume_real
        if compile_options is None:
            self.compile_options = "optimize"
        else:
            self.compile_options = compile_options
Exemplo n.º 18
0
def GeneralizedRotation(angle: typing.Union[typing.List[typing.Hashable],
                                            typing.List[numbers.Real]],
                        generator: QubitHamiltonian,
                        control: typing.Union[list, int] = None,
                        shift: float = 0.5,
                        steps: int = 1) -> QCircuit:
    """

    Notes
    --------
    
    A gates which is shift-rule differentiable
     - its generator only has two distinguishable eigenvalues
     - it is then differentiable by the shift rule
     - shift needs to be given upon initialization (otherwise its default is 1/2)
     - the generator will not be verified to fullfill the properties
     Compiling will be done in analogy to a trotterized gate with steps=1 as default

    The gate will act in the same way as rotations and exppauli gates

    .. math::
        U_{G}(\\text{angle}) = e^{-i\\frac{\\text{angle}}{2} G}
    
    Parameters
    ----------
    angle
    generator
    control
    shift
    steps

    Returns
    -------
    The gate wrapped in a circuit
    """
    return QCircuit.wrap_gate(
        GeneralizedRotationImpl(angle=assign_variable(angle),
                                generator=generator,
                                control=control,
                                shift=shift,
                                steps=steps))
Exemplo n.º 19
0
def _initialize_power_gate(name: str,
                           target: typing.Union[list, int],
                           generator,
                           control: typing.Union[list, int] = None,
                           power=None,
                           angle=None,
                           *args,
                           **kwargs) -> QCircuit:
    target = list_assignment(target)

    # allow angle instead of power in initialization for more consistency
    # if angle is given we just convert it
    if angle is not None:
        angle = assign_variable(angle)
        if power is not None:
            power = power * angle / np.pi
        else:
            power = angle / np.pi

    if power is None or power in [1, 1.0]:
        gates = [
            impl.QGateImpl(name=name,
                           target=q,
                           control=control,
                           generator=generator(q)) for q in target
        ]
    else:
        gates = [
            impl.PowerGateImpl(name=name,
                               power=power,
                               target=q,
                               control=control,
                               generator=generator(q),
                               *args,
                               **kwargs) for q in target
        ]

    return QCircuit.wrap_gate(gates)
Exemplo n.º 20
0
    def plot(self,
             property: typing.Union[str, typing.List[str]] = 'energies',
             key: str = None,
             filename=None,
             baselines: typing.Dict[str, float] = None,
             *args,
             **kwargs):
        """
        Convenience function to plot the progress of the optimizer over time.
        Parameters
        ----------
        property: (list of) str: Default = 'energies'
            which property (eg angles, energies, gradients) to plot.
            Default: plot energies over time.
        key: str, optional:
            if property is 'angles' or 'gradients', key allows you to plot just an individual variables' property.
            Default: plot everything
        filename, optional:
            if give, plot to this file; else, plot to terminal.
            Default: plot to terminal.
        baselines: dict, optional:
            dictionary of plotting axis baseline information.
            Default: use whatever matplotlib auto-generates.

        args:
            args.
        kwargs:
            kwargs.

        Returns
        -------
        None
        """

        from matplotlib import pyplot as plt
        from matplotlib.ticker import MaxNLocator
        fig = plt.figure()
        fig.gca().xaxis.set_major_locator(MaxNLocator(integer=True))
        import pickle

        if baselines is not None:
            for k, v in baselines.items():
                plt.axhline(y=v, label=k)

        if hasattr(property, "lower"):
            properties = [property.lower()]
        else:
            properties = property

        labels = None
        if 'labels' in kwargs:
            labels = kwargs['labels']
        elif 'label' in kwargs:
            labels = kwargs['label']

        if hasattr(labels, "lower"):
            labels = [labels] * len(properties)

        for k, v in kwargs.items():
            if hasattr(plt, k):
                f = getattr(plt, k)
                if callable(f):
                    f(v)
                else:
                    f = v

        if key is None:
            keys = [[k for k in self.angles[-1].keys()]] * len(properties)
        elif isinstance(key, typing.Hashable):
            keys = [[assign_variable(key)]] * len(properties)
        else:
            key = [assign_variable(k) for k in key]
            keys = [key] * len(properties)

        for i, p in enumerate(properties):
            try:
                label = labels[i]
            except:
                label = p

            if p == "energies":
                data = getattr(self, "extract_" + p)()
                plt.plot(list(data.keys()),
                         list(data.values()),
                         label=str(label),
                         marker='o',
                         linestyle='--')
            else:
                for k in keys[i]:
                    data = getattr(self, "extract_" + p)(key=k)
                    plt.plot(list(data.keys()),
                             list(data.values()),
                             label=str(label) + " " + str(k),
                             marker='o',
                             linestyle='--')

        loc = 'best'
        if 'loc' in kwargs:
            loc = kwargs['loc']
        plt.legend(loc=loc)
        if filename is None:
            plt.show()
        else:
            pickle.dump(fig, open(filename + ".pickle", "wb"))
            plt.savefig(fname=filename + ".pdf", **kwargs)
Exemplo n.º 21
0
 def parameter(self, other):
     self.parameter = assign_variable(variable=other)
Exemplo n.º 22
0
 def __init__(self, angle, generator, p0, assume_real=True, control=None):
     angle = assign_variable(angle)
     super().__init__(name="QubitExcitation", parameter=angle, target=self.extract_targets(generator), control=control, eigenvalues_magnitude = 0.25)
     self.generator = generator
     self.p0 = p0
     self.assume_real = assume_real
Exemplo n.º 23
0
def U(theta,
      phi,
      lambd,
      target: typing.Union[list, int],
      control: typing.Union[list, int] = None) -> QCircuit:
    """
    Notes
    ----------
    Convenient gate, one of the abstract gates defined by OpenQASM.

    .. math::
        U(\\theta, \\phi, \\lambda) = R_z(\\phi)R_x(-\\pi/2)R_z(\\theta)R_x(\\pi/2)R_z(\\lambda)
        U(\\theta, \\phi, \\lambda) = \\begin{pmatrix}
                                            e^{-i \\frac{\\phi}{2}} & 0 \\\\
                                            0 & e^{i \\frac{\\phi}{2}}
                                        \\end{pmatrix}
                                        \\begin{pmatrix}
                                            \\cos{-\\frac{\\pi}{4}} & -i \\sin{-\\frac{\\pi}{4}} \\\\
                                            -i \\sin{-\\frac{\\pi}{4}} & \\cos{-\\frac{\\pi}{4}}
                                        \\end{pmatrix}
                                        \\begin{pmatrix}
                                            e^{-i \\frac{\\theta}{2}} & 0 \\\\
                                            0 & e^{i \\frac{\\theta}{2}}
                                        \\end{pmatrix}
                                        \\begin{pmatrix}
                                            \\cos{\\frac{\\pi}{4}} & -i \\sin{\\frac{\\pi}{4}} \\\\
                                            -i \\sin{\\frac{\\pi}{4}} & \\cos{\\frac{\\pi}{4}}
                                        \\end{pmatrix}
                                        \\begin{pmatrix}
                                            e^{-i \\frac{\\lambda}{2}} & 0 \\\\
                                            0 & e^{i \\frac{\\lambda}{2}}
                                        \\end{pmatrix}

        U(\\theta, \\phi, \\lambda) = \\begin{pmatrix}
                                        \\cos{\\frac{\\theta}{2}} &
                                        -e^{i \\lambda} \\sin{\\frac{\\theta}{2}} \\\\
                                        e^{i \\phi} \\sin{\\frac{\\theta}{2}} &
                                        e^{i (\\phi+\\lambda)} \\cos{\\frac{\\theta}{2}}
                                      \\end{pmatrix}

    Parameters
    ----------
    theta
        first parameter angle
    phi
        second parameter angle
    lamnd
        third parameter angle
    target
        int or list of int
    control
        int or list of int

    Returns
    -------
    QCircuit object
    """

    theta = assign_variable(theta)
    phi = assign_variable(phi)
    lambd = assign_variable(lambd)
    pi_half = assign_variable(np.pi / 2)

    return Rz(angle=lambd, target=target, control=control) + \
           Rx(angle=pi_half, target=target, control=control) + \
           Rz(angle=theta, target=target, control=control) + \
           Rx(angle=-pi_half, target=target, control=control) + \
           Rz(angle=phi, target=target, control=control)
Exemplo n.º 24
0
def Trotterized(generator: QubitHamiltonian = None,
                steps: int = 1,
                angle: typing.Union[typing.Hashable, numbers.Real,
                                    Variable] = None,
                control: typing.Union[list, int] = None,
                randomize=False,
                *args,
                **kwargs) -> QCircuit:
    """

    Parameters
    ----------
    generator :
        generator of the gate U = e^{-i\frac{angle}{2} G }
    angles :
        coefficients for each generator
    steps :
        trotter steps
    control :
        control qubits
    generators: QubitHamiltonian :
        The generator of the gate
    steps: int :
        Trotter Steps
    angle: typing.Hashable :
        A symbol that will be converted to a tq.Variable
    numbers.Real :
        A fixed real number
    Variable :
        A tequila Variable
    control: control qubits
    Returns
    -------
    QCircuit

    """

    #  downward compatibility
    if "generators" in kwargs:
        if generator is None:
            if len(kwargs["generators"]) > 1:
                if "angles" not in kwargs:
                    angles = [angle] * len(kwargs["generators"])
                else:
                    angles = kwargs["angles"]
                result = QCircuit()
                for angle, g in zip(angles, kwargs["generators"]):
                    result += Trotterized(generator=g,
                                          angle=angle,
                                          steps=steps,
                                          control=control,
                                          randomize=randomize)
                    return result
            else:
                generator = kwargs["generators"][0]
        else:
            raise Exception(
                "Trotterized: You gave generators={} and generator={}".format(
                    generator, kwargs["generators"]))

    if "angles" in kwargs:
        if angle is None:
            if len(kwargs["angles"]) > 1:
                raise Exception(
                    "multiple angles given, but only one generator")
            angle = kwargs["angles"][0]
        else:
            raise Exception(
                "Trotterized: You gave angles={} and angle={}".format(
                    angle, kwargs["angles"]))

    angle = assign_variable(angle)

    return QCircuit.wrap_gate(
        impl.TrotterizedGateImpl(generator=generator,
                                 angle=angle,
                                 steps=steps,
                                 control=control,
                                 randomize=randomize,
                                 **kwargs))
Exemplo n.º 25
0
def minimize(objective: Objective,
             gradient: typing.Union[str, typing.Dict[Variable, Objective]] = None,
             hessian: typing.Union[str, typing.Dict[typing.Tuple[Variable, Variable], Objective]] = None,
             qng: bool = None,
             initial_values: typing.Dict[typing.Hashable, numbers.Real] = None,
             variables: typing.List[typing.Hashable] = None,
             samples: int = None,
             maxiter: int = 100,
             backend: str = None,
             backend_options: dict = None,
             noise: NoiseModel = None,
             method: str = "BFGS",
             tol: float = 1.e-3,
             method_options: dict = None,
             method_bounds: typing.Dict[typing.Hashable, numbers.Real] = None,
             method_constraints=None,
             silent: bool = False,
             save_history: bool = True,
             *args,
             **kwargs) -> SciPyReturnType:
    """

    Parameters
    ----------
    objective: Objective :
        The tequila objective to optimize
    gradient: typing.Union[str, typing.Dict[Variable, Objective], None] : (Default value = None) :
        '2-point', 'cs' or '3-point' for numerical gradient evaluation (does not work in combination with all optimizers),
        dictionary of variables and tequila objective to define own gradient,
        None for automatic construction (default)
    hessian: typing.Union[str, typing.Dict[Variable, Objective], None] : (Default value = None) :
        '2-point', 'cs' or '3-point' for numerical gradient evaluation (does not work in combination with all optimizers),
        dictionary (keys:tuple of variables, values:tequila objective) to define own gradient,
        None for automatic construction (default)
    qng: bool : (Default value = False) :
        whether or not, in the event that a gradient-based method is to be used, the qng, rather than the standard gradient,
        should be employed. NOTE: throws an error for anything but a single expectationvalue with no passive angles.
    initial_values: typing.Dict[typing.Hashable, numbers.Real]: (Default value = None):
        Initial values as dictionary of Hashable types (variable keys) and floating point numbers. If given None they will all be set to zero
    variables: typing.List[typing.Hashable] :
         (Default value = None)
         List of Variables to optimize
    samples: int :
         (Default value = None)
         samples/shots to take in every run of the quantum circuits (None activates full wavefunction simulation)
    maxiter: int :
         (Default value = 100)
    backend: str :
         (Default value = None)
         Simulator backend, will be automatically chosen if set to None
    backend_options: dict:
         (Default value = None)
         Additional options for the backend
         Will be unpacked and passed to the compiled objective in every call
    noise: NoiseModel:
         (Default value =None)
         a NoiseModel to apply to all expectation values in the objective.
    method: str :
         (Default value = "BFGS")
         Optimization method (see scipy documentation, or 'available methods')
    tol: float :
         (Default value = 1.e-3)
         Convergence tolerance for optimization (see scipy documentation)
    method_options: dict :
         (Default value = None)
         Dictionary of options
         (see scipy documentation)
    method_bounds: typing.Dict[typing.Hashable, typing.Tuple[float, float]]:
        (Default value = None)
        bounds for the variables (see scipy documentation)
    method_constraints :
         (Default value = None)
         (see scipy documentation
    silent: bool :
         (Default value = False)
         No printout if True
    save_history: bool:
        (Default value = True)
        Save the history throughout the optimization

    Returns
    -------

    """

    # bring into right format
    variables = format_variable_list(variables)
    initial_values = format_variable_dictionary(initial_values)
    if isinstance(gradient, dict) or hasattr(gradient, "items"):
        gradient = format_variable_dictionary(gradient)
    if isinstance(hessian, dict) or hasattr(hessian, "items"):
        hessian = {(assign_variable(k[0]), assign_variable([k[1]])): v for k, v in hessian.items()}
    method_bounds = format_variable_dictionary(method_bounds)

    # set defaults
    all_variables = objective.extract_variables()
    if variables is None:
        variables = all_variables
    if initial_values is None:
        initial_values = {k: numpy.random.uniform(0, 2 * numpy.pi) for k in all_variables}
    else:
        # autocomplete initial values, warn if you did
        detected = False
        for k in all_variables:
            if k not in initial_values:
                initial_values[k] = numpy.random.uniform(0, 2 * numpy.pi)
                detected = True
        if detected and not silent:
            print("WARNING: initial_variables given but not complete: Autocomplete with random number")

    optimizer = OptimizerSciPy(save_history=save_history,
                               maxiter=maxiter,
                               method=method,
                               method_options=method_options,
                               method_bounds=method_bounds,
                               method_constraints=method_constraints,
                               silent=silent,
                               tol=tol)
    if initial_values is not None:
        initial_values = {assign_variable(k): v for k, v in initial_values.items()}
    return optimizer(objective=objective, qng=qng,
                     backend=backend, backend_options=backend_options, gradient=gradient, hessian=hessian, initial_values=initial_values,
                     variables=variables, noise=noise,
                     samples=samples, *args, **kwargs)
Exemplo n.º 26
0
 def dagger(self):
     result = copy.deepcopy(self)
     result._parameter = assign_variable(-self.parameter)
     return result
Exemplo n.º 27
0
def minimize(objective,
             method: str = "bfgs",
             variables: list = None,
             initial_values: typing.Union[dict, numbers.Number] = 0.0,
             maxiter: int = None,
             *args,
             **kwargs):
    """

    Parameters
    ----------
    method: str:
       The optimization method (e.g. bfgs, cobyla, nelder-mead, ...)
       see 'tq.optimizers.show_available_methods()' for an overview
    objective: tq.Objective:
       The abstract tequila objective to be optimized
    variables: list of names:
       The variables which shall be optimized given as list
       Can be passed as list of names or list of tq variables
    initial_values: dict:
       Initial values for the optimization, passed as dictionary
       with the variable names as keys.
       Alternatively `zero`, `random` or a single number are accepted
    maxiter:
       maximum number of iterations
    kwargs:
       further keyword arguments for the actual minimization functions
       can also be called directly as tq.minimize_modulename
       e.g. tq.minimize_scipy
       See their documentation for more details

       example: gradient keyword:
       gradient (Default Value: None):
       instructions for gradient compilation
       can be a dictionary of tequila objectives representing the gradients
       or a string/dictionary giving instructions for numerical gradients
       examples are
            gradient = '2-point'
            gradient = {'method':'2-point', 'stepsize': 1.e-4}
            gradient = {'method':Callable, 'stepsize': 1.e-4}
            see optimizer_base.py for method examples

        gradient = None: analytical gradients are compiled


    Returns
    -------

    """
    for k, v in INSTALLED_OPTIMIZERS.items():
        if method.lower() in v.methods or method.upper() in v.methods:
            if hasattr(initial_values,
                       "lower") and initial_values.lower() == "zero":
                initial_values = {
                    assign_variable(k): 0.0
                    for k in objective.extract_variables()
                }
            elif isinstance(initial_values, numbers.Number):
                initial_values = {
                    assign_variable(k): initial_values
                    for k in objective.extract_variables()
                }
            if initial_values is not None:
                initial_values = {
                    assign_variable(k): v
                    for k, v in initial_values.items()
                }
            return v.minimize(objective=objective,
                              method=method,
                              variables=variables,
                              initial_values=initial_values,
                              maxiter=maxiter,
                              *args,
                              **kwargs)

    raise TequilaOptimizerException(
        "Could not find optimization method {} in tequila optimizers. You might miss dependencies"
    )
Exemplo n.º 28
0
def minimize(objective: Objective,
             gradient: typing.Union[str, typing.Dict[Variable,
                                                     Objective]] = None,
             hessian: typing.Union[str, typing.Dict[typing.Tuple[Variable,
                                                                 Variable],
                                                    Objective]] = None,
             initial_values: typing.Dict[typing.Hashable, numbers.Real] = None,
             variables: typing.List[typing.Hashable] = None,
             samples: int = None,
             maxiter: int = 100,
             backend: str = None,
             backend_options: dict = None,
             noise: NoiseModel = None,
             device: str = None,
             method: str = "BFGS",
             tol: float = 1.e-3,
             method_options: dict = None,
             method_bounds: typing.Dict[typing.Hashable, numbers.Real] = None,
             method_constraints=None,
             silent: bool = False,
             save_history: bool = True,
             *args,
             **kwargs) -> SciPyResults:
    """

    Parameters
    ----------
    objective: Objective :
        The tequila objective to optimize
    gradient: typing.Union[str, typing.Dict[Variable, Objective], None] : Default value = None):
        '2-point', 'cs' or '3-point' for numerical gradient evaluation (does not work in combination with all optimizers),
        dictionary of variables and tequila objective to define own gradient,
        None for automatic construction (default)
        Other options include 'qng' to use the quantum natural gradient.
    hessian: typing.Union[str, typing.Dict[Variable, Objective], None], optional:
        '2-point', 'cs' or '3-point' for numerical gradient evaluation (does not work in combination with all optimizers),
        dictionary (keys:tuple of variables, values:tequila objective) to define own gradient,
        None for automatic construction (default)
    initial_values: typing.Dict[typing.Hashable, numbers.Real], optional:
        Initial values as dictionary of Hashable types (variable keys) and floating point numbers. If given None they will all be set to zero
    variables: typing.List[typing.Hashable], optional:
         List of Variables to optimize
    samples: int, optional:
         samples/shots to take in every run of the quantum circuits (None activates full wavefunction simulation)
    maxiter: int : (Default value = 100):
         max iters to use.
    backend: str, optional:
         Simulator backend, will be automatically chosen if set to None
    backend_options: dict, optional:
         Additional options for the backend
         Will be unpacked and passed to the compiled objective in every call
    noise: NoiseModel, optional:
         a NoiseModel to apply to all expectation values in the objective.
    method: str : (Default = "BFGS"):
         Optimization method (see scipy documentation, or 'available methods')
    tol: float : (Default = 1.e-3):
         Convergence tolerance for optimization (see scipy documentation)
    method_options: dict, optional:
         Dictionary of options
         (see scipy documentation)
    method_bounds: typing.Dict[typing.Hashable, typing.Tuple[float, float]], optional:
        bounds for the variables (see scipy documentation)
    method_constraints: optional:
         (see scipy documentation
    silent: bool :
         No printout if True
    save_history: bool:
        Save the history throughout the optimization

    Returns
    -------
    SciPyReturnType:
        the results of optimization
    """

    if isinstance(gradient, dict) or hasattr(gradient, "items"):
        if all([isinstance(x, Objective) for x in gradient.values()]):
            gradient = format_variable_dictionary(gradient)
    if isinstance(hessian, dict) or hasattr(hessian, "items"):
        if all([isinstance(x, Objective) for x in hessian.values()]):
            hessian = {(assign_variable(k[0]), assign_variable([k[1]])): v
                       for k, v in hessian.items()}
    method_bounds = format_variable_dictionary(method_bounds)

    # set defaults

    optimizer = OptimizerSciPy(save_history=save_history,
                               maxiter=maxiter,
                               method=method,
                               method_options=method_options,
                               method_bounds=method_bounds,
                               method_constraints=method_constraints,
                               silent=silent,
                               backend=backend,
                               backend_options=backend_options,
                               device=device,
                               samples=samples,
                               noise=noise,
                               tol=tol,
                               *args,
                               **kwargs)
    if initial_values is not None:
        initial_values = {
            assign_variable(k): v
            for k, v in initial_values.items()
        }
    return optimizer(objective=objective,
                     gradient=gradient,
                     hessian=hessian,
                     initial_values=initial_values,
                     variables=variables,
                     *args,
                     **kwargs)
Exemplo n.º 29
0
    def __call__(self, objective: Objective,
                 initial_values: typing.Dict[Variable, numbers.Real],
                 variables: typing.List[Variable],
                 gradient: typing.Dict[Variable, Objective] = None,
                 qng: bool = False,
                 hessian: typing.Dict[typing.Tuple[Variable, Variable], Objective] = None,
                 samples: int = None,
                 backend: str = None,
                 backend_options: dict = None,
                 noise: NoiseModel = None,
                 reset_history: bool = True,
                 *args,
                 **kwargs) -> SciPyReturnType:
        """
        Optimizes with scipy and gives back the optimized angles
        Get the optimized energies over the history
        :param objective: The tequila Objective to minimize
        :param initial_valuesxx: initial values for the objective
        :param return_scipy_output: chose if the full scipy output shall be returned
        :param reset_history: reset the history before optimization starts (has no effect if self.save_history is False)
        :return: tuple of optimized energy ,optimized angles and scipy output
        """

        infostring = "Starting {method} optimization\n".format(method=self.method)
        infostring += "Objective: {} expectationvalues\n".format(objective.count_expectationvalues())

        if self.save_history and reset_history:
            self.reset_history()

        active_angles = {}
        for v in variables:
            active_angles[v] = initial_values[v]

        passive_angles = {}
        for k, v in initial_values.items():
            if k not in active_angles.keys():
                passive_angles[k] = v

        # Transform the initial value directory into (ordered) arrays
        param_keys, param_values = zip(*active_angles.items())
        param_values = numpy.array(param_values)

        bounds = None
        if self.method_bounds is not None:
            bounds = {k: None for k in active_angles}
            for k, v in self.method_bounds.items():
                if k in bounds:
                    bounds[k] = v
            infostring += "bounds : {}\n".format(self.method_bounds)
            names, bounds = zip(*bounds.items())
            assert (names == param_keys)  # make sure the bounds are not shuffled

        # do the compilation here to avoid costly recompilation during the optimization
        compiled_objective = compile(objective=objective, variables=initial_values, backend=backend, noise=noise,
                                     samples=samples, *args, **kwargs)

        E = _EvalContainer(objective=compiled_objective,
                           param_keys=param_keys,
                           samples=samples,
                           passive_angles=passive_angles,
                           save_history=self.save_history,
                           backend_options = backend_options,
                           silent=self.silent)

        # compile gradients
        if self.method in self.gradient_based_methods + self.hessian_based_methods and not isinstance(gradient, str):
            compiled_grad_objectives = dict()
            if gradient is None:
                gradient = {assign_variable(k): grad(objective=objective, variable=k) for k in active_angles.keys()}
            else:
                gradient = {assign_variable(k): v for k, v in gradient.items()}

            grad_exval = []
            for k in active_angles.keys():
                if k not in gradient:
                    raise Exception("No gradient for variable {}".format(k))
                grad_exval.append(gradient[k].count_expectationvalues())
                compiled_grad_objectives[k] = compile(objective=gradient[k], variables=initial_values,
                                                      samples=samples, noise=noise, backend=backend, *args, **kwargs)

            if qng:
                combos = get_qng_combos(objective, samples=samples, backend=backend,
                                        noise=noise, initial_values=initial_values)

                dE = _QngContainer(combos=combos,
                                   param_keys=param_keys,
                                   samples=samples,
                                   passive_angles=passive_angles,
                                   save_history=self.save_history,
                                   silent=self.silent,
                                   backend_options=backend_options)
            else:

                dE = _GradContainer(objective=compiled_grad_objectives,
                                    param_keys=param_keys,
                                    samples=samples,
                                    passive_angles=passive_angles,
                                    save_history=self.save_history,
                                    silent=self.silent,
                                    backend_options=backend_options)

                infostring += "Gradients: {} expectationvalues (min={}, max={})\n".format(sum(grad_exval),
                                                                                          min(grad_exval),
                                                                                          max(grad_exval))
        else:
            # use numerical gradient
            dE = gradient
            infostring += "Gradients: {}\n".format(gradient)

        # compile hessian

        if self.method in self.hessian_based_methods and not isinstance(hessian, str):

            if isinstance(gradient, str):
                raise TequilaScipyException("Can not use numerical gradients for Hessian based methods")
            if qng is True:
                raise TequilaScipyException('Quantum Natural Hessian not yet well-defined, sorry!')
            compiled_hess_objectives = dict()
            hess_exval = []
            for i, k in enumerate(active_angles.keys()):
                for j, l in enumerate(active_angles.keys()):
                    if j > i: continue
                    hess = grad(gradient[k], l)
                    compiled_hess = compile(objective=hess, variables=initial_values, samples=samples,
                                            noise=noise,
                                            backend=backend, *args, **kwargs)
                    compiled_hess_objectives[(k, l)] = compiled_hess
                    compiled_hess_objectives[(l, k)] = compiled_hess
                    hess_exval.append(compiled_hess.count_expectationvalues())

            ddE = _HessContainer(objective=compiled_hess_objectives,
                                 param_keys=param_keys,
                                 samples=samples,
                                 passive_angles=passive_angles,
                                 save_history=self.save_history,
                                 silent=self.silent)

            infostring += "Hessian: {} expectationvalues (min={}, max={})\n".format(sum(hess_exval), min(hess_exval),
                                                                                    max(hess_exval))

        else:
            infostring += "Hessian: {}\n".format(hessian)
            if self.method != "TRUST-CONSTR" and hessian is not None:
                raise TequilaScipyException("numerical hessians only for trust-constr method")
            ddE = hessian

        if not self.silent:
            print("ObjectiveType is {}".format(type(compiled_objective)))
            print(infostring)
            print("backend: {}".format(compiled_objective.backend))
            print("samples: {}".format(samples))
            print("{} active variables".format(len(active_angles)))

        # get the number of real scipy iterations for better histories
        real_iterations = []

        Es = []
        callback = lambda x, *args: real_iterations.append(len(E.history) - 1)
        res = scipy.optimize.minimize(E, x0=param_values, jac=dE, hess=ddE,
                                      args=(Es,),
                                      method=self.method, tol=self.tol,
                                      bounds=bounds,
                                      constraints=self.method_constraints,
                                      options=self.method_options,
                                      callback=callback)

        # failsafe since callback is not implemented everywhere
        if len(real_iterations) == 0:
            real_iterations = range(len(E.history))
        else:
            real_iterations = [0] + real_iterations
        if self.save_history:
            self.history.energies = [E.history[i] for i in real_iterations]
            self.history.energy_evaluations = E.history
            self.history.angles = [E.history_angles[i] for i in real_iterations]
            self.history.angles_evaluations = E.history_angles
            if dE is not None and not isinstance(dE, str):
                # can currently only save gradients if explicitly evaluated
                # and will fail for hessian based approaches
                # need better callback functions
                try:
                    if self.method not in self.hessian_based_methods:
                        self.history.gradients = [dE.history[i] for i in real_iterations]
                except:
                    print("WARNING: History could not assign the stored gradients")
                self.history.gradients_evaluations = dE.history
            if ddE is not None and not isinstance(ddE, str):
                # hessians are not evaluated in the same frequencies as energies
                # therefore we can not store the "real" iterations currently
                self.history.hessians_evaluations = ddE.history

        E_final = res.fun
        angles_final = dict((param_keys[i], res.x[i]) for i in range(len(param_keys)))
        angles_final = {**angles_final, **passive_angles}

        return SciPyReturnType(energy=E_final, angles=format_variable_dictionary(angles_final), history=self.history,
                               scipy_output=res)
Exemplo n.º 30
0
def Trotterized(generators: typing.List[QubitHamiltonian] = None,
                steps: int = 1,
                angles: typing.Union[typing.List[typing.Hashable],
                                     typing.List[numbers.Real],
                                     typing.List[Variable]] = None,
                control: typing.Union[list, int] = None,
                parameters: TrotterParameters = None,
                *args,
                **kwargs) -> QCircuit:
    """

    Parameters
    ----------
    generators :
        list of generators
    angles :
        coefficients for each generator
    steps :
        trotter steps
    control :
        control qubits
    parameters :
        Additional Trotter parameters, if None then defaults are used
    generators: typing.List[QubitHamiltonian] :
        
    steps: int :
        
    angles: typing.Union[typing.List[typing.Hashable] :
        
    typing.List[numbers.Real] :
        
    typing.List[Variable]] :
         (Default value = None)
    control: typing.Union[list :
        
    int] :
         (Default value = None)
    parameters: TrotterParameters :
         (Default value = None)

    Returns
    -------
    QCircuit

    """

    # convenience
    if "generator" in kwargs:
        if generators is None:
            generators = [kwargs["generator"]]
        else:
            raise Exception(
                "Trotterized: You gave generators={} and generator={}".format(
                    angles, kwargs["generator"]))

    if "angle" in kwargs:
        if angles is None:
            angles = [kwargs["angle"]] * len(generators)
        else:
            raise Exception(
                "Trotterized: You gave angles={} and angle={}".format(
                    angles, kwargs["angle"]))

    # more convenience
    if not (isinstance(generators, list) or isinstance(generators, tuple)):
        generators = [generators]
    if not (isinstance(angles, list) or isinstance(angles, tuple)):
        angles = [angles]

    if parameters is None:
        parameters = TrotterParameters()

    assigned_angles = [assign_variable(angle) for angle in angles]

    return QCircuit.wrap_gate(
        impl.TrotterizedGateImpl(generators=generators,
                                 angles=assigned_angles,
                                 steps=steps,
                                 control=control,
                                 **parameters.__dict__))