Exemple #1
0
def __grad_expectationvalue(E: ExpectationValueImpl, variable: Variable):
    '''
    implements the analytic partial derivative of a unitary as it would appear in an expectation value. See the paper.
    :param unitary: the unitary whose gradient should be obtained
    :param variables (list, dict, str): the variables with respect to which differentiation should be performed.
    :return: vector (as dict) of dU/dpi as Objective (without hamiltonian)
    '''
    hamiltonian = E.H
    unitary = E.U
    assert (unitary.verify())

    # fast return if possible
    if variable not in unitary.extract_variables():
        return 0.0

    param_gates = unitary._parameter_map[variable]

    dO = Objective()
    for idx_g in param_gates:
        idx, g = idx_g
        # failsafe
        if g.is_controlled():
            raise TequilaException(
                "controlled gate in gradient: Compiler was not called. Gate is {}"
                .format(g))
        if not hasattr(g, "eigenvalues_magnitude"):
            raise TequilaException('No shift found for gate {}'.format(g))

        dOinc = __grad_shift_rule(unitary, g, idx, variable, hamiltonian)

        dO += dOinc

    assert dO is not None
    return dO
Exemple #2
0
def qng_circuit_grad(E: ExpectationValueImpl):
    '''
    implements the analytic partial derivatives of a unitary as it would appear in an expectation value, taking
    all parameters as final (only useful for qng, invalid method otherwise!)
    :param E: the Excpectation whose gradient should be obtained
    :return: vector (as dict) of dU/dpi as Objectives.
    '''
    hamiltonian = E.H
    unitary = E.U

    # fast return if possible
    out = []
    for i, g in enumerate(unitary.gates):
        if g.is_parametrized():
            if g.is_controlled():
                raise TequilaException(
                    "controlled gate in qng circuit gradient: Compiler was not called"
                )
            if hasattr(g, "shift"):
                if hasattr(g._parameter, 'extract_variables'):
                    shifter = qng_grad_gaussian(unitary, g, i, hamiltonian)
                    out.append(shifter)
            else:
                print(g, type(g))
                raise TequilaException('No shift found for gate {}'.format(g))
    if out is None:
        raise TequilaException("caught a dead circuit in qng gradient")
    return out
Exemple #3
0
    def binary(self, n_qubits: int = None):
        if len(self._data.keys()) == 0:
            maxq = 1
        else:
            maxq = max(self._data.keys()) + 1

        if n_qubits is None:
            n_qubits = maxq

        if n_qubits < maxq:
            raise TequilaException(
                "PauliString acts on qubit number larger than n_qubits given\n PauliString="
                + self.__repr__() + ", n_qubits=" + str(n_qubits))

        binary = numpy.zeros(2 * n_qubits)
        for k, v in self._data.items():
            if v.upper() == "X":
                binary[k] = 1
            elif v.upper() == "Y":
                binary[k] = 1
                binary[n_qubits + k] = 1
            elif v.upper() == "Z":
                binary[n_qubits + k] = 1
            else:
                raise TequilaException("Unknown Pauli: %" + str(v))
        return BinaryPauli(coeff=self.coeff, binary=binary)
Exemple #4
0
    def make_excitation_generator(
            self,
            indices: typing.Iterable[typing.Tuple[int,
                                                  int]]) -> QubitHamiltonian:
        """
        Notes
        ----------
        Creates the transformed hermitian generator of UCC type unitaries:
              M(a^\dagger_{a_0} a_{i_0} a^\dagger{a_1}a_{i_1} ... - h.c.)
              where the qubit map M depends is self.transformation

        Parameters
        ----------
        indices : typing.Iterable[typing.Tuple[int, int]] :
            List of tuples [(a_0, i_0), (a_1, i_1), ... ] - recommended format, in spin-orbital notation (alpha odd numbers, beta even numbers)
            can also be given as one big list: [a_0, i_0, a_1, i_1 ...]
        Returns
        -------
        type
            1j*Transformed qubit excitation operator, depends on self.transformation
        """
        # check indices and convert to list of tuples if necessary
        if len(indices) == 0:
            raise TequilaException(
                "make_excitation_operator: no indices given")
        elif not isinstance(indices[0], typing.Iterable):
            if len(indices) % 2 != 0:
                raise TequilaException(
                    "make_excitation_generator: unexpected input format of indices\n"
                    "use list of tuples as [(a_0, i_0),(a_1, i_1) ...]\n"
                    "or list as [a_0, i_0, a_1, i_1, ... ]\n"
                    "you gave: {}".format(indices))
            converted = [(indices[2 * i], indices[2 * i + 1])
                         for i in range(len(indices) // 2)]
        else:
            converted = indices

        # convert to openfermion input format
        ofi = []
        dag = []
        for pair in converted:
            assert (len(pair) == 2)
            ofi += [
                (int(pair[0]), 1), (int(pair[1]), 0)
            ]  # openfermion does not take other types of integers like numpy.int64
            dag += [(int(pair[0]), 0), (int(pair[1]), 1)]

        op = openfermion.FermionOperator(tuple(ofi),
                                         1.j)  # 1j makes it hermitian
        op += openfermion.FermionOperator(tuple(reversed(dag)), -1.j)
        qop = QubitHamiltonian(qubit_hamiltonian=self.transformation(op))

        # check if the operator is hermitian and cast coefficients to floats
        # in order to avoid trouble with the simulation backends
        assert qop.is_hermitian()
        for k, v in qop.qubit_operator.terms.items():
            qop.qubit_operator.terms[k] = to_float(v)

        qop = qop.simplify()
        return qop
Exemple #5
0
def get_angle(name: str) -> list:
    i = name.find('(')
    j = name.find(')')
    if j == -1:
        raise TequilaException("Invalid specification {}".format(name))
    angles_str = name[i + 1:j].split(',')
    angles = []
    for angle in angles_str:
        try:
            phase = float(angle)
        except ValueError:
            if angle.find('pi') == -1:
                raise TequilaException("Invalid specification {}".format(name))
            angle = angle.replace('pi', '')
            try:
                if angle.find('*') != -1:
                    angle = angle.replace('*', '')
                    phase = float(angle) * pi
                elif angle.find('/') != -1:
                    angle = angle.replace('/', '')
                    phase = pi / float(angle)
                elif len(angle) == 0:
                    phase = pi
                else:
                    phase = float(angle)
            except ValueError:
                raise TequilaException("Invalid specification {}".format(name))
        angles.append(phase)
    return angles
Exemple #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."
        )
Exemple #7
0
 def is_binary(self):
     if not isinstance(self.binary, np.ndarray):
         raise TequilaException(
             'Unknown representation of binary vector. Got ' +
             str(self.binary) + ' with type ' + type(self.binary))
     if not all([x == 1 or x == 0 for x in self.binary]):
         raise TequilaException(
             'Not all number in the binary vector is 0 or 1. Got ' +
             str(self.binary))
    def build_device_circuit(self, ignore_failures=False):
        """
        Attempts to configure a cirq circuit to run on a device
        Parameters
        ----------
        ignore_failures: bool:
            whether or not to include gates in the circuit that fail to compile. Ignore; currently under construction.
        Returns
        -------
        cirq.Circuit
            the circuit, reconfigured for the device.

        """
        c = self.circuit
        device = self.device
        line = None
        circuit = None
        if isinstance(device, cirq.Device):
            if isinstance(device,
                          cirq.google.devices.XmonDevice) or isinstance(
                              device, cirq.google.devices.serializable_device.
                              SerializableDevice):
                options = ['xmon', 'xmon_partial_cz', 'sqrt_iswap', 'sycamore']
                if device in [cirq.google.Sycamore, cirq.google.Sycamore23]:
                    options = [
                        'sycamore', 'sqrt_iswap', 'xmon', 'xmon_partial_cz'
                    ]
                for option in options:
                    try:
                        line = cirq.google.line_on_device(
                            device, length=len(self.abstract_circuit.qubits))

                        circuit = cirq.google.optimized_for_sycamore(
                            circuit=c,
                            new_device=device,
                            optimizer_type=option,
                            qubit_map=lambda q: line[q.x])
                    except:
                        line = None
                        pass
                if circuit is None:
                    raise TequilaCirqException(
                        'could not optimize for device={}'.format(device))

            else:
                ### under construction (potentially on other branches)
                raise TequilaException(
                    'Only known and Xmon devices currently functional. Sorry!')
        else:
            raise TequilaException(
                'build_device_circuit demands a cirq.Device object; received {}, of type {}'
                .format(str(device), type(device)))

        if line is not None:
            for k in self.qubit_map.keys():
                self.qubit_map[k].instance = line[self.qubit_map[k].instance.x]
        return circuit
Exemple #9
0
def parse_from_open_qasm_2(qasm_code: str, rigorous: bool = True) -> QCircuit:

    lines = qasm_code.splitlines()
    clean_code = []
    # ignore comments
    for line in lines:
        if line.find("//") != -1:
            clean_line = line[0:line.find("//")].strip()
        else:
            clean_line = line.strip()
        if clean_line:
            clean_code.append(clean_line)

    if clean_code[0].startswith("OPENQASM"):
        clean_code.pop(0)
    elif rigorous:
        raise TequilaException("File must start with the 'OPENQASM' directive")

    if clean_code[0].startswith('include "qelib1.inc";'):
        clean_code.pop(0)
    elif rigorous:
        raise TequilaException(
            "File must import standard library (qelib1.inc)")

    code_circuit = "\n".join(clean_code)
    # separate the custom command definitions from the normal commands
    custom_gates_map: Dict[str, QCircuit] = {}
    while True:
        i = code_circuit.find("gate ")
        if i == -1:
            break
        j = code_circuit.find("}", i)
        custom_name, custom_circuit = parse_custom_gate(
            code_circuit[i:j + 1], custom_gates_map=custom_gates_map)
        custom_gates_map[custom_name] = custom_circuit
        code_circuit = code_circuit[:i] + code_circuit[j + 1:]

    # parse regular commands
    commands = [s.strip() for s in code_circuit.split(";") if s.strip()]
    qregisters: Dict[str, int] = {}

    circuit = QCircuit()
    for c in commands:
        partial_circuit = parse_command(command=c,
                                        custom_gates_map=custom_gates_map,
                                        qregisters=qregisters)
        if partial_circuit is not None:
            circuit += partial_circuit

    return circuit
Exemple #10
0
def __grad_objective(objective: typing.Union[Objective, VectorObjective],
                     variable: Variable):
    if isinstance(objective, VectorObjective):
        return __grad_vector_objective(objective, variable)
    else:
        args = objective.args
        transformation = objective.transformation
        dO = None

        processed_expectationvalues = {}
        for i, arg in enumerate(args):
            if __AUTOGRAD__BACKEND__ == "jax":
                df = jax.grad(transformation, argnums=i)
            elif __AUTOGRAD__BACKEND__ == "autograd":
                df = jax.grad(transformation, argnum=i)
            else:
                raise TequilaException(
                    "Can't differentiate without autograd or jax")

            # We can detect one simple case where the outer derivative is const=1
            if transformation is None or transformation == identity:
                outer = 1.0
            else:
                outer = Objective(args=args, transformation=df)

            if hasattr(arg, "U"):
                # save redundancies
                if arg in processed_expectationvalues:
                    inner = processed_expectationvalues[arg]
                else:
                    inner = __grad_inner(arg=arg, variable=variable)
                    processed_expectationvalues[arg] = inner
            else:
                # this means this inner derivative is purely variable dependent
                inner = __grad_inner(arg=arg, variable=variable)

            if inner == 0.0:
                # don't pile up zero expectationvalues
                continue

            if dO is None:
                dO = outer * inner
            else:
                dO = dO + outer * inner

        if dO is None:
            raise TequilaException("caught None in __grad_objective")
        return dO
Exemple #11
0
def __grad_gaussian(unitary, g, i, variable, hamiltonian):
    '''
    function for getting the gradients of gaussian gates. NOTE: you had better compile first.
    :param unitary: QCircuit: the QCircuit object containing the gate to be differentiated
    :param g: a parametrized: the gate being differentiated
    :param i: Int: the position in unitary at which g appears
    :param variable: Variable or String: the variable with respect to which gate g is being differentiated
    :param hamiltonian: the hamiltonian with respect to which unitary is to be measured, in the case that unitary
        is contained within an ExpectationValue
    :return: an Objective, whose calculation yields the gradient of g w.r.t variable
    '''

    if not hasattr(g, "shift"):
        raise TequilaException("No shift found for gate {}".format(g))

    # neo_a and neo_b are the shifted versions of gate g needed to evaluate its gradient
    shift_a = g._parameter + np.pi / (4 * g.shift)
    shift_b = g._parameter - np.pi / (4 * g.shift)
    neo_a = copy.deepcopy(g)
    neo_a._parameter = shift_a
    neo_b = copy.deepcopy(g)
    neo_b._parameter = shift_b

    U1 = unitary.replace_gates(positions=[i], circuits=[neo_a])
    w1 = g.shift * __grad_inner(g.parameter, variable)

    U2 = unitary.replace_gates(positions=[i], circuits=[neo_b])
    w2 = -g.shift * __grad_inner(g.parameter, variable)

    Oplus = ExpectationValueImpl(U=U1, H=hamiltonian)
    Ominus = ExpectationValueImpl(U=U2, H=hamiltonian)
    dOinc = w1 * Objective(args=[Oplus]) + w2 * Objective(args=[Ominus])
    return dOinc
Exemple #12
0
    def __add__(self, other):
        if isinstance(other, Moment):
            gates = [g.copy() for g in (self.gates + other.gates)]
            result = QCircuit(gates=gates)
            result._min_n_qubits = max(self.as_circuit()._min_n_qubits, other._min_n_qubits)
            if result.depth == 1:
                result = Moment(gates=result.gates)
                result._min_n_qubits = max(self.as_circuit()._min_n_qubits, other._min_n_qubits)
        elif isinstance(other, QCircuit) and not isinstance(other, Moment):
            if not other.is_primitive():
                gates = [g.copy() for g in (self.gates + other.gates)]
                result = QCircuit(gates=gates)
                result._min_n_qubits = max(self.as_circuit()._min_n_qubits, other._min_n_qubits)
            else:
                try:
                    result = self.add_gate(other.gates[0])
                    result._min_n_qubits += len(other.qubits)
                except:
                    result = self.as_circuit() + QCircuit.wrap_gate(other)
                    result._min_n_qubits = max(self.as_circuit()._min_n_qubits, QCircuit.wrap_gate(other)._min_n_qubits)

        else:
            if isinstance(other, QGateImpl):
                try:
                    result = self.add_gate(other)
                    result._min_n_qubits += len(other.qubits)
                except:
                    result = self.as_circuit() + QCircuit.wrap_gate(other)
                    result._min_n_qubits = max(self.as_circuit()._min_n_qubits, QCircuit.wrap_gate(other)._min_n_qubits)
            else:
                raise TequilaException(
                    'cannot add moments to types other than QCircuit,Moment,or Gate; recieved summand of type {}'.format(
                        str(type(other))))
        return result
Exemple #13
0
 def do_make_molecule(self,
                      molecule=None,
                      nuclear_repulsion=None,
                      one_body_integrals=None,
                      two_body_integrals=None,
                      *args,
                      **kwargs) -> MolecularData:
     if molecule is None:
         if one_body_integrals is not None and two_body_integrals is not None:
             if nuclear_repulsion is None:
                 warnings.warn(
                     "PySCF Interface: No constant part (nuclear repulsion)",
                     TequilaWarning)
                 nuclear_repulsion = 0.0
             molecule = super().do_make_molecule(
                 nuclear_repulsion=nuclear_repulsion,
                 one_body_integrals=one_body_integrals,
                 two_body_integrals=two_body_integrals,
                 *args,
                 **kwargs)
         else:
             raise TequilaException(
                 "not here yet, use openfermionpyscf and feed the integrals to the init"
             )
     return molecule
Exemple #14
0
 def n_qubits(self, other):
     self._min_n_qubits = other
     if other < self.max_qubit() + 1:
         raise TequilaException(
             "You are trying to set n_qubits to " + str(other) + " but your circuit needs at least: " + str(
                 self.max_qubit() + 1))
     return self
Exemple #15
0
def qng_grad_gaussian(unitary, g, i, hamiltonian):
    '''
    qng function for getting the gradients of gaussian gates.
    THIS variant of the function does not seek out underlying gate parameters; it treats each variable 'as is'.
    This treatment is necessary for the QNG but is incorrect elsewhere.
    :param unitary: QCircuit: the QCircuit object containing the gate to be differentiated
    :param g: a parametrized: the gate being differentiated
    :param i: Int: the position in unitary at which g appears
    :param variable: Variable or String: the variable with respect to which gate g is being differentiated
    :param hamiltonian: the hamiltonian with respect to which unitary is to be measured, in the case that unitary
        is contained within an ExpectationValue
    :return: a list of objectives; the gradient of the Exp. with respect to each of its (internal) parameters
    '''

    if not hasattr(g, "shift"):
        raise TequilaException("No shift found for gate {}".format(g))

    # neo_a and neo_b are the shifted versions of gate g needed to evaluate its gradient
    shift_a = g._parameter + numpy.pi / (4 * g.shift)
    shift_b = g._parameter - numpy.pi / (4 * g.shift)
    neo_a = copy.deepcopy(g)
    neo_a._parameter = shift_a
    neo_b = copy.deepcopy(g)
    neo_b._parameter = shift_b

    U1 = unitary.replace_gates(positions=[i], circuits=[neo_a])
    w1 = g.shift

    U2 = unitary.replace_gates(positions=[i], circuits=[neo_b])
    w2 = -g.shift

    Oplus = ExpectationValueImpl(U=U1, H=hamiltonian)
    Ominus = ExpectationValueImpl(U=U2, H=hamiltonian)
    dOinc = w1 * Objective(args=[Oplus]) + w2 * Objective(args=[Ominus])
    return dOinc
Exemple #16
0
def __grad_expectationvalue(E: ExpectationValueImpl, variable: Variable):
    '''
    implements the analytic partial derivative of a unitary as it would appear in an expectation value. See the paper.
    :param unitary: the unitary whose gradient should be obtained
    :param variables (list, dict, str): the variables with respect to which differentiation should be performed.
    :return: vector (as dict) of dU/dpi as Objective (without hamiltonian)
    '''

    hamiltonian = E.H
    unitary = E.U
    if not (unitary.verify()):
        raise TequilaException("error in grad_expectationvalue unitary is {}".format(unitary))

    # fast return if possible
    if variable not in unitary.extract_variables():
        return 0.0

    param_gates = unitary._parameter_map[variable]

    dO = Objective()
    for idx_g in param_gates:
        idx, g = idx_g
        dOinc = __grad_shift_rule(unitary, g, idx, variable, hamiltonian)
        dO += dOinc

    assert dO is not None
    return dO
Exemple #17
0
def __grad_shift_rule(unitary, g, i, variable, hamiltonian):
    '''
    function for getting the gradients of directly differentiable gates. Expects precompiled circuits.
    :param unitary: QCircuit: the QCircuit object containing the gate to be differentiated
    :param g: a parametrized: the gate being differentiated
    :param i: Int: the position in unitary at which g appears
    :param variable: Variable or String: the variable with respect to which gate g is being differentiated
    :param hamiltonian: the hamiltonian with respect to which unitary is to be measured, in the case that unitary
        is contained within an ExpectationValue
    :return: an Objective, whose calculation yields the gradient of g w.r.t variable
    '''


    # possibility for overwride in custom gate construction
    if hasattr(g, "shifted_gates"):
        inner_grad=__grad_inner(g.parameter, variable)
        shifted = g.shifted_gates()
        dOinc = Objective()
        for x in shifted:
            w,g = x
            Ux = unitary.replace_gates(positions=[i], circuits=[g])
            wx = w*inner_grad
            Ex = Objective.ExpectationValue(U=Ux, H=hamiltonian)
            dOinc += wx*Ex
        return dOinc
    else:
        raise TequilaException('No shift found for gate {}\nWas the compiler called?'.format(g))
Exemple #18
0
def compile_to_cc(gate) -> QCircuit:
    if not gate.is_controlled:
        return QCircuit.wrap_gate(gate)
    cl = len(gate.control)
    target = gate.target
    control = gate.control
    if cl <= 2:
        return QCircuit.wrap_gate(gate)
    name = gate.name
    back = QCircuit()
    if name in ['X', 'x', 'Y', 'y', 'Z', 'z', 'H', 'h']:
        if isinstance(gate, PowerGateImpl):
            power = gate.parameter
        else:
            power = 1.0
        new = PowerGateImpl(name=name,
                            power=power,
                            target=target,
                            control=control)
        back += compile_power_gate(gate=new, cut=True)
    elif isinstance(gate, RotationGateImpl):
        partial = compile_controlled_rotation(gate=gate)
        back += compile_to_cc(gate=partial)
    elif isinstance(gate, PhaseGateImpl):
        partial = compile_controlled_phase(gate=gate)
        back += compile_to_cc(gate=partial)
    else:
        print(gate)
        raise TequilaException('frankly, what the f**k is this gate?')
    return back
Exemple #19
0
 def assign_axis(axis):
     if axis in QubitHamiltonian.axis_to_string:
         return QubitHamiltonian.axis_to_string[axis]
     elif hasattr(axis, "upper"):
         return axis.upper()
     else:
         raise TequilaException("unknown initialization for pauli operator: {}".format(axis))
Exemple #20
0
    def __init__(self, molecule, indices:str):
        """

        Parameters
        ----------
        molecule:
            a tequila molecule object
        indices
            a list of indices defining UCC operations
            indices refer to spin-orbitals
            e.g. indices = [[(0,2),(1,3)], [(0,2)], [(1,3)]]
            can be a string for predefined pools supported are UpCCD, UpCCSD, UpCCGD, and UpCCGSD
        """
        self.molecule = molecule

        if isinstance(indices, str):
            if not "CC" in indices.upper():
                raise TequilaException("Pool of type {} not yet supported.\nCreate your own by passing the initialized indices".format(indices))

            generalized = True if "G" in indices.upper() else False
            paired = True if "P" in indices.upper() else False
            singles = True if "S" in indices.upper() else False
            doubles = True if "D" in indices.upper() else False

            indices = []
            if doubles: indices += self.make_indices_doubles(generalized=generalized, paired=paired)
            if singles: indices += self.make_indices_singles(generalized=generalized)

        indices = [tuple(k) for k in indices]
        super().__init__(generators=indices)
Exemple #21
0
    def with_gates(self, gates):
        """
        with gate, but on multiple gates.

        Parameters
        ----------
        gates:
            list of QGates

        Returns
        -------
        Moment:
            a new Moment, with the desired gates insert into self.

        """
        gl = list(gates)
        first_overlap = []
        for g in gl:
            for q in g.qubits:
                if q not in first_overlap:
                    first_overlap.append(q)
                else:
                    raise TequilaException('cannot have a moment with multiple operations acting on the same qubit!')

        new = self.with_gate(gl[0])
        for g in gl[1:]:
            new = new.with_gate(g)
        new.sort_gates()
        return new
Exemple #22
0
    def check_device(self, device):
        """
        Verify if a device is valid.
        Parameters
        ----------
        device:
            a cirq.Device or the name of a known cirq device.
        Returns
        -------
        None

        """
        if device is None:
            return
        if isinstance(device, cirq.Device):
            return
        else:
            assert isinstance(device, str)
            if device.lower() in [
                    'foxtail', 'sycamore', 'sycamore23', 'bristlecone'
            ]:
                pass
            else:
                raise TequilaException(
                    'requested device {} could not be found!'.format(device))
Exemple #23
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
Exemple #24
0
def get_generator(gate) -> paulis.QubitHamiltonian:
    """
    get the generator of a gaussian gate as a Qubit hamiltonian. Relies on the name of the gate.
    Parameters
    ----------
    gate: QGateImpl:
        QGateImpl object or inheritor thereof, with name corresponding to its generator in some fashion.

    Returns
    -------
    QubitHamiltonian:
        the generator of the gate acting, on the gate's target.

    """

    if gate.name.lower() == 'rx':
        gen = paulis.X(gate.target[0])
    elif gate.name.lower() == 'ry':
        gen = paulis.Y(gate.target[0])
    elif gate.name.lower() == 'rz':
        gen = paulis.Z(gate.target[0])
    elif gate.name.lower() == 'phase':
        gen = paulis.Qm(gate.target[0])
    else:
        print(gate.name.lower())
        raise TequilaException(
            'cant get the generator of a non Gaussian gate, you fool!')
    return gen
Exemple #25
0
 def __call__(self, wfn):
     if hasattr(wfn, "apply_qubitoperator"):
         return wfn.apply_qubitoperator(self)
     else:
         raise TequilaException(
             "Not sure what to do here with {} and {} ...".format(
                 self, wfn))
Exemple #26
0
    def __init__(self, abstract_circuit: QCircuit, variables, qubit_map=None, noise=None, device=None, *args, **kwargs):
        """

        Parameters
        ----------
        abstract_circuit: QCircuit:
            Tequila unitary to compile to cirq
        variables: dict:
            values of all variables in the circuit, to compile with.
        qubit_map: dictionary:
            a qubit map which maps the abstract qubits in the abstract_circuit to the qubits on the backend
            there is no need to initialize the corresponding backend types
            the dictionary should simply be {int:int} (preferred) or {int:name}
            if None the default will map to qubits 0 ... n_qubits -1 in the backend
        noise:
            Noise to apply to the circuit.
        device:
            device on which to emulatedly execute all sampling.
        args
        kwargs
        """

        self.op_lookup = {
            'I': (cirq.ops.IdentityGate, None),
            'X': (cirq.ops.common_gates.XPowGate, map_1),
            'Y': (cirq.ops.common_gates.YPowGate, map_1),
            'Z': (cirq.ops.common_gates.ZPowGate, map_1),
            'H': (cirq.ops.common_gates.HPowGate, map_1),
            'Rx': (cirq.ops.common_gates.XPowGate, map_2),
            'Ry': (cirq.ops.common_gates.YPowGate, map_2),
            'Rz': (cirq.ops.common_gates.ZPowGate, map_2),
            'SWAP': (cirq.ops.SwapPowGate, None),
        }

        self.tq_to_sympy = {}
        self.counter = 0
        if device is not None:
            self.compiler_arguments['cc_max'] = True
        super().__init__(abstract_circuit=abstract_circuit, variables=variables,
                         noise=noise, qubit_map=qubit_map, device=device, *args, **kwargs)
        if len(self.tq_to_sympy.keys()) is None:
            self.sympy_to_tq = None
            self.resolver = None
        else:
            self.sympy_to_tq = {v: k for k, v in self.tq_to_sympy.items()}
            self.resolver = cirq.ParamResolver({k: v(variables) for k, v in self.sympy_to_tq.items()})
        if self.device is not None:
            self.circuit = self.build_device_circuit()
        if self.noise is not None:
            if self.noise == 'device':
                raise TequilaException('cannot get device noise for cirq yet, sorry!')
            self.noise_lookup = {
                'bit flip': [lambda x: cirq.bit_flip(x)],
                'phase flip': [lambda x: cirq.phase_flip(x)],
                'phase damp': [cirq.phase_damp],
                'amplitude damp': [cirq.amplitude_damp],
                'phase-amplitude damp': [cirq.amplitude_damp, cirq.phase_damp],
                'depolarizing': [lambda x: cirq.depolarize(p=(3 / 4) * x)]
            }
            self.circuit = self.build_noisy_circuit(self.noise)
Exemple #27
0
def parse_custom_gate(
        gate_custom: str, custom_gates_map: Dict[str,
                                                 QCircuit]) -> (str, QCircuit):
    """
    Parse custom gates code

    Args:
        gate_custom: code with custom gates
    """
    gate_custom = gate_custom[5:]
    spec, body = gate_custom.split("{", 1)

    if "(" in spec:
        i = spec.find("(")
        j = spec.find(")")
        if spec[i + 1:j].strip():
            raise TequilaException(
                "Parameters for custom gates not supported: {}".format(spec))
        spec = spec[:i] + spec[j + 1:]

    spec = spec.strip()

    if " " in spec:
        name, qargs = spec.split(" ", 1)
        name = name.strip()
        qargs = qargs.strip()
    else:
        raise TequilaException(
            "Custom gate specification doesn't have any arguments: {}".format(
                spec))

    custom_qregisters: Dict[str, int] = {}
    for qarg in qargs.split(','):
        custom_qregisters[qarg] = len(custom_qregisters)

    body = body[:-1].strip()
    commands = [s.strip() for s in body.split(";") if s.strip()]

    custom_circuit = QCircuit()
    for c in commands:
        partial_circuit = parse_command(command=c,
                                        custom_gates_map=custom_gates_map,
                                        qregisters=custom_qregisters)
        if partial_circuit is not None:
            custom_circuit += partial_circuit

    return name, custom_circuit
Exemple #28
0
    def binary_operator(cls, left, right, op):
        """
        Core arithmetical method for creating differentiable callables of two Tequila Objectives and or Variables.

        this function, usually called by the convenience magic-methods of Observable objects, constructs a new Objective
        whose Transformation  is the JoinedTransformation of the lower arguments and transformations
        of the left and right objects, alongside op (if they are or can be rendered as objectives).
        In case one of left or right is a number, calls unary_operator instead.

        Parameters
        ----------
        left:
            the left hand argument to op
        right:
            the right hand argument to op.
        op: callable:
            an operation; a function object.

        Returns
        -------
        Objective:
            an objective whose Transformation is op acting on left and right.
        """

        if isinstance(right, numbers.Number):
            if isinstance(left, Objective):
                return cls.unary_operator(left=left, op=lambda E: op(E, right))
            else:
                raise TequilaException(
                    'BinaryOperator method called on types ' +
                    str(type(left)) + ',' + str(type(right)))
        elif isinstance(left, numbers.Number):
            if isinstance(right, Objective):
                return cls.unary_operator(left=right, op=lambda E: op(left, E))
            else:
                raise TequilaException(
                    'BinaryOperator method called on types ' +
                    str(type(left)) + ',' + str(type(right)))
        else:
            split_at = len(left.args)
            return Objective(args=left.args + right.args,
                             transformation=JoinedTransformation(
                                 left=left.transformation,
                                 right=right.transformation,
                                 split=split_at,
                                 op=op))
Exemple #29
0
 def from_moments(moments: typing.List):
     """
     Raises
     ------
     TequilaException
     """
     raise TequilaException(
         'this method should never be called from Moment. Call from the QCircuit class itself instead.')
Exemple #30
0
def compile_power_base(gate):
    """
    Base case of compile_power_gate: convert a 1-qubit parametrized power gate into rotation gates.
    Parameters
    ----------
    gate:
        the gate.

    Returns
    -------
        A QCircuit; the result of compilation.
    """
    if not isinstance(gate, PowerGateImpl):
        return QCircuit.wrap_gate(gate)

    if gate.is_controlled():
        return QCircuit.wrap_gate(gate)

    power = gate.power
    if gate.name.lower() in ['h', 'hadamard']:
        ### off by global phase of Exp[ pi power /2]
        theta = power * numpy.pi

        result = QCircuit()
        result += Ry(angle=-numpy.pi / 4, target=gate.target)
        result += Rz(angle=theta, target=gate.target)
        result += Ry(angle=numpy.pi / 4, target=gate.target)
    elif gate.name == 'X':
        ### off by global phase of Exp[ pi power /2]
        '''
        if we wanted to do it formally we would use the following
        a=-numpy.pi/2
        b=numpy.pi/2
        theta = power*numpy.pi

        result = QCircuit()
        result+= Rz(angle=b,target=gate.target)
        result+= Ry(angle=theta,target=gate.target)
        result+= Rz(angle=a,target=gate.target)
        '''
        result = Rx(angle=power * numpy.pi, target=gate.target)
    elif gate.name == 'Y':
        ### off by global phase of Exp[ pi power /2]
        theta = power * numpy.pi

        result = QCircuit()
        result += Ry(angle=theta, target=gate.target)
    elif gate.name == 'Z':
        ### off by global phase of Exp[ pi power /2]
        a = 0
        b = power * numpy.pi
        theta = 0
        result = QCircuit()
        result += Rz(angle=b, target=gate.target)
    else:
        raise TequilaException('passed a gate with name ' + gate.name +
                               ', which cannot be handled!')
    return result