Exemplo n.º 1
0
    def __call__(self, target, basis_fidelity=None):
        """Decompose a two-qubit unitary over fixed basis + SU(2) using the best approximation given
        that each basis application has a finite fidelity.
        """
        basis_fidelity = basis_fidelity or self.basis_fidelity
        if hasattr(target, 'to_operator'):
            # If input is a BaseOperator subclass this attempts to convert
            # the object to an Operator so that we can extract the underlying
            # numpy matrix from `Operator.data`.
            target = target.to_operator().data
        if hasattr(target, 'to_matrix'):
            # If input is Gate subclass or some other class object that has
            # a to_matrix method this will call that method.
            target = target.to_matrix()
        # Convert to numpy array incase not already an array
        target = np.asarray(target, dtype=complex)
        # Check input is a 2-qubit unitary
        if target.shape != (4, 4):
            raise QiskitError(
                "TwoQubitBasisDecomposer: expected 4x4 matrix for target")
        if not is_unitary_matrix(target):
            raise QiskitError(
                "TwoQubitBasisDecomposer: target matrix is not unitary.")

        target_decomposed = TwoQubitWeylDecomposition(target)
        traces = self.traces(target_decomposed)
        expected_fidelities = [
            trace_to_fid(traces[i]) * basis_fidelity**i for i in range(4)
        ]

        best_nbasis = np.argmax(expected_fidelities)
        decomposition = self.decomposition_fns[best_nbasis](target_decomposed)
        decomposition_angles = [_DECOMPOSER1Q.angles(x) for x in decomposition]

        q = QuantumRegister(2)
        return_circuit = QuantumCircuit(q)
        for i in range(best_nbasis):
            return_circuit.append(U3Gate(*decomposition_angles[2 * i]), [q[0]])
            return_circuit.append(U3Gate(*decomposition_angles[2 * i + 1]),
                                  [q[1]])
            return_circuit.append(self.gate, [q[0], q[1]])
        return_circuit.append(U3Gate(*decomposition_angles[2 * best_nbasis]),
                              [q[0]])
        return_circuit.append(
            U3Gate(*decomposition_angles[2 * best_nbasis + 1]), [q[1]])

        return return_circuit
Exemplo n.º 2
0
 def _define(self):
     """
     gate ms2(theta, phi) a,b
     {
     rz(phi) a;
     rz(phi+pi/2) b;
     CX b,a;
     rz(-pi/2) a;
     ry(theta+pi/2) b;
     CX a,b;
     ry(-pi/2) b;
     CX b,a;
     rz(-phi-pi/2) a;
     rz(-phi) b;
     }
     """
     definition = []
     q = QuantumRegister(2, "q")
     theta, phi = tuple(self.params)
     #         rule = [
     #             (U3Gate(0, 0, phi), [q[0]], []),
     #             (U3Gate(0, 0, phi+pi/2), [q[1]], []),
     #             (CnotGate(), [q[1], q[0]], []),
     #             (U3Gate(0, 0, -pi/2), [q[0]], []),
     #             (U3Gate(theta+pi/2,0,0), [q[1]], []),
     #             (CnotGate(), [q[0], q[1]], []),
     #             (U3Gate(-pi/2,0,0), [q[1]], []),
     #             (CnotGate(), [q[1], q[0]], []),
     #             (U3Gate(0, 0, -phi-pi/2), [q[0]], []),
     #             (U3Gate(0, 0, -phi), [q[1]], []),
     #         ]
     rule = [
         (U3Gate(0, 0, phi), [q[0]], []),
         (U3Gate(0, 0, phi), [q[1]], []),
         (RXXGate(theta), [q[0], q[1]], []),
         (U3Gate(0, 0, -phi), [q[0]], []),
         (U3Gate(0, 0, -phi), [q[1]], []),
     ]
     for inst in rule:
         definition.append(inst)
     self.definition = definition
Exemplo n.º 3
0
    def _process_node(self, node):
        """Carry out the action associated with a node."""
        if node.type == "program":
            self._process_children(node)

        elif node.type == "qreg":
            qreg = QuantumRegister(node.index, node.name)
            self.dag.add_qreg(qreg)

        elif node.type == "creg":
            creg = ClassicalRegister(node.index, node.name)
            self.dag.add_creg(creg)

        elif node.type == "id":
            raise QiskitError("internal error: _process_node on id")

        elif node.type == "int":
            raise QiskitError("internal error: _process_node on int")

        elif node.type == "real":
            raise QiskitError("internal error: _process_node on real")

        elif node.type == "indexed_id":
            raise QiskitError("internal error: _process_node on indexed_id")

        elif node.type == "id_list":
            # We process id_list nodes when they are leaves of barriers.
            return [self._process_bit_id(node_children)
                    for node_children in node.children]

        elif node.type == "primary_list":
            # We should only be called for a barrier.
            return [self._process_bit_id(m) for m in node.children]

        elif node.type == "gate":
            self._process_gate(node)

        elif node.type == "custom_unitary":
            self._process_custom_unitary(node)

        elif node.type == "universal_unitary":
            args = self._process_node(node.children[0])
            qid = self._process_bit_id(node.children[1])
            for element in qid:
                u3_gate = U3Gate(*args, element)
                u3_gate.condition = self.condition
                self.dag.apply_operation_back(u3_gate)

        elif node.type == "cnot":
            self._process_cnot(node)

        elif node.type == "expression_list":
            return node.children

        elif node.type == "binop":
            raise QiskitError("internal error: _process_node on binop")

        elif node.type == "prefix":
            raise QiskitError("internal error: _process_node on prefix")

        elif node.type == "measure":
            self._process_measure(node)

        elif node.type == "format":
            self.version = node.version()

        elif node.type == "barrier":
            ids = self._process_node(node.children[0])
            qubits = []
            for qubit in ids:
                for j, _ in enumerate(qubit):
                    qubits.append(qubit[j])
            self.dag.apply_operation_back(Barrier(len(qubits)), qubits, [])

        elif node.type == "reset":
            id0 = self._process_bit_id(node.children[0])
            for i, _ in enumerate(id0):
                reset = Reset()
                reset.condition = self.condition
                self.dag.apply_operation_back(reset, [id0[i]], [])

        elif node.type == "if":
            self._process_if(node)

        elif node.type == "opaque":
            self._process_gate(node, opaque=True)

        elif node.type == "external":
            raise QiskitError("internal error: _process_node on external")

        else:
            raise QiskitError("internal error: undefined node type",
                              node.type, "line=%s" % node.line,
                              "file=%s" % node.file)
        return None
Exemplo n.º 4
0
    def run(self, dag):
        """Run the Optimize1qGates pass on `dag`.

        Args:
            dag (DAGCircuit): the DAG to be optimized.

        Returns:
            DAGCircuit: the optimized DAG.

        Raises:
            TranspilerError: if YZY and ZYZ angles do not give same rotation matrix.
        """
        use_u = 'u' in self.basis
        use_p = 'p' in self.basis
        runs = dag.collect_runs(["u1", "u2", "u3", "u", 'p'])
        runs = _split_runs_on_parameters(runs)
        for run in runs:
            if use_p:
                right_name = "p"
            else:
                right_name = "u1"
            right_parameters = (0, 0, 0)  # (theta, phi, lambda)
            right_global_phase = 0
            for current_node in run:
                left_name = current_node.name
                if (current_node.condition is not None
                        or len(current_node.qargs) != 1
                        or left_name not in ["p", "u1", "u2", "u3", 'u', "id"]):
                    raise TranspilerError("internal error")
                if left_name in ("u1", "p"):
                    left_parameters = (0, 0, current_node.op.params[0])
                elif left_name == "u2":
                    left_parameters = (np.pi / 2, current_node.op.params[0],
                                       current_node.op.params[1])
                elif left_name in ("u3", 'u'):
                    left_parameters = tuple(current_node.op.params)
                else:
                    if use_p:
                        left_name = "p"
                    else:
                        left_name = "u1"  # replace id with u1
                    left_parameters = (0, 0, 0)
                if (current_node.op.definition is not None and
                        current_node.op.definition.global_phase):
                    right_global_phase += current_node.op.definition.global_phase
                # If there are any sympy objects coming from the gate convert
                # to numpy.
                left_parameters = tuple([float(x) for x in left_parameters])
                # Compose gates
                name_tuple = (left_name, right_name)
                if name_tuple in (("u1", "u1"), ("p", "p")):
                    # u1(lambda1) * u1(lambda2) = u1(lambda1 + lambda2)
                    right_parameters = (0, 0, right_parameters[2] +
                                        left_parameters[2])
                elif name_tuple in (("u1", "u2"), ("p", "u2")):
                    # u1(lambda1) * u2(phi2, lambda2) = u2(phi2 + lambda1, lambda2)
                    right_parameters = (np.pi / 2, right_parameters[1] +
                                        left_parameters[2], right_parameters[2])
                elif name_tuple in (("u2", "u1"), ("u2", "p")):
                    # u2(phi1, lambda1) * u1(lambda2) = u2(phi1, lambda1 + lambda2)
                    right_name = "u2"
                    right_parameters = (np.pi / 2, left_parameters[1],
                                        right_parameters[2] + left_parameters[2])
                elif name_tuple in (("u1", "u3"), ("u1", "u"), ("p", "u3"), ("p", "u")):
                    # u1(lambda1) * u3(theta2, phi2, lambda2) =
                    #     u3(theta2, phi2 + lambda1, lambda2)
                    right_parameters = (right_parameters[0], right_parameters[1] +
                                        left_parameters[2], right_parameters[2])
                elif name_tuple in (("u3", "u1"), ('u', 'u1'), ("u3", "p"), ("u", "p")):
                    # u3(theta1, phi1, lambda1) * u1(lambda2) =
                    #     u3(theta1, phi1, lambda1 + lambda2)
                    if use_u:
                        right_name = 'u'
                    else:
                        right_name = "u3"
                    right_parameters = (left_parameters[0], left_parameters[1],
                                        right_parameters[2] + left_parameters[2])
                elif name_tuple == ("u2", "u2"):
                    # Using Ry(pi/2).Rz(2*lambda).Ry(pi/2) =
                    #    Rz(pi/2).Ry(pi-2*lambda).Rz(pi/2),
                    # u2(phi1, lambda1) * u2(phi2, lambda2) =
                    #    u3(pi - lambda1 - phi2, phi1 + pi/2, lambda2 + pi/2)
                    if use_u:
                        right_name = 'u'
                    else:
                        right_name = "u3"
                    right_parameters = (np.pi - left_parameters[2] -
                                        right_parameters[1], left_parameters[1] +
                                        np.pi / 2, right_parameters[2] +
                                        np.pi / 2)
                elif name_tuple[1] == "nop":
                    right_name = left_name
                    right_parameters = left_parameters
                else:
                    # For composing u3's or u2's with u3's, use
                    # u2(phi, lambda) = u3(pi/2, phi, lambda)
                    # together with the qiskit.mapper.compose_u3 method.
                    if use_u:
                        right_name = 'u'
                    else:
                        right_name = "u3"
                    # Evaluate the symbolic expressions for efficiency
                    right_parameters = Optimize1qGates.compose_u3(left_parameters[0],
                                                                  left_parameters[1],
                                                                  left_parameters[2],
                                                                  right_parameters[0],
                                                                  right_parameters[1],
                                                                  right_parameters[2])
                    # Why evalf()? This program:
                    #   OPENQASM 2.0;
                    #   include "qelib1.inc";
                    #   qreg q[2];
                    #   creg c[2];
                    #   u3(0.518016983430947*pi,1.37051598592907*pi,1.36816383603222*pi) q[0];
                    #   u3(1.69867232277986*pi,0.371448347747471*pi,0.461117217930936*pi) q[0];
                    #   u3(0.294319836336836*pi,0.450325871124225*pi,1.46804720442555*pi) q[0];
                    #   measure q -> c;
                    # took >630 seconds (did not complete) to optimize without
                    # calling evalf() at all, 19 seconds to optimize calling
                    # evalf() AFTER compose_u3, and 1 second to optimize
                    # calling evalf() BEFORE compose_u3.
                # 1. Here down, when we simplify, we add f(theta) to lambda to
                # correct the global phase when f(theta) is 2*pi. This isn't
                # necessary but the other steps preserve the global phase, so
                # we continue in that manner.
                # 2. The final step will remove Z rotations by 2*pi.
                # 3. Note that is_zero is true only if the expression is exactly
                # zero. If the input expressions have already been evaluated
                # then these final simplifications will not occur.
                # TODO After we refactor, we should have separate passes for
                # exact and approximate rewriting.

                # Y rotation is 0 mod 2*pi, so the gate is a u1
                if abs(np.mod(right_parameters[0],
                              (2 * np.pi))) < self.eps and right_name != "u1" \
                        and right_name != "p":
                    if use_p:
                        right_name = "p"
                    else:
                        right_name = "u1"
                    right_parameters = (0, 0, right_parameters[1] +
                                        right_parameters[2] +
                                        right_parameters[0])
                # Y rotation is pi/2 or -pi/2 mod 2*pi, so the gate is a u2
                if right_name == "u3" or 'u':
                    # theta = pi/2 + 2*k*pi
                    right_angle = right_parameters[0] - np.pi / 2
                    if abs(right_angle) < self.eps:
                        right_angle = 0
                    if abs(np.mod((right_angle),
                                  2 * np.pi)) < self.eps:
                        right_name = "u2"
                        right_parameters = (np.pi / 2, right_parameters[1],
                                            right_parameters[2] +
                                            (right_parameters[0] - np.pi / 2))
                    # theta = -pi/2 + 2*k*pi
                    right_angle = right_parameters[0] + np.pi / 2
                    if abs(right_angle) < self.eps:
                        right_angle = 0
                    if abs(np.mod(right_angle,
                                  2 * np.pi)) < self.eps:
                        right_name = "u2"
                        right_parameters = (np.pi / 2, right_parameters[1] +
                                            np.pi, right_parameters[2] -
                                            np.pi + (right_parameters[0] +
                                                     np.pi / 2))
                # u1 and lambda is 0 mod 2*pi so gate is nop (up to a global phase)
                if right_name in ("u1", "p") and abs(
                        np.mod(right_parameters[2], 2 * np.pi)) < self.eps:
                    right_name = "nop"

            if right_name == "u2" and "u2" not in self.basis:
                if use_u:
                    right_name = 'u'
                else:
                    right_name = "u3"
            if right_name in ("u1", "p") and right_name not in self.basis:
                if use_u:
                    right_name = 'u'
                else:
                    right_name = "u3"

            new_op = Gate(name="", num_qubits=1, params=[])
            if right_name == "u1":
                new_op = U1Gate(right_parameters[2])
            if right_name == "p":
                new_op = PhaseGate(right_parameters[2])
            if right_name == "u2":
                new_op = U2Gate(right_parameters[1], right_parameters[2])
            if right_name == "u":
                if "u" in self.basis:
                    new_op = UGate(*right_parameters)
            if right_name == "u3":
                if "u3" in self.basis:
                    new_op = U3Gate(*right_parameters)
                else:
                    raise TranspilerError('It was not possible to use the basis %s' % self.basis)

            dag.global_phase += right_global_phase

            if right_name != 'nop':
                dag.substitute_node(run[0], new_op, inplace=True)

            # Delete the other nodes in the run
            for current_node in run[1:]:
                dag.remove_op_node(current_node)
            if right_name == "nop":
                dag.remove_op_node(run[0])

        return dag