def run(self, dag):
        """Run the ConsolidateBlocks pass on `dag`.

        Iterate over each block and replace it with an equivalent Unitary
        on the same wires.
        """

        if self.decomposer is None:
            return dag

        new_dag = dag._copy_circuit_metadata()

        # compute ordered indices for the global circuit wires
        global_index_map = {wire: idx for idx, wire in enumerate(dag.qubits)}

        blocks = self.property_set['block_list']
        # just to make checking if a node is in any block easier
        all_block_nodes = {nd for bl in blocks for nd in bl}

        for node in dag.topological_op_nodes():
            if node not in all_block_nodes:
                # need to add this node to find out where in the list it goes
                preds = [nd for nd in dag.predecessors(node) if nd.type == 'op']

                block_count = 0
                while preds:
                    if block_count < len(blocks):
                        block = blocks[block_count]

                        # if any of the predecessors are in the block, remove them
                        preds = [p for p in preds if p not in block]
                    else:
                        # should never occur as this would mean not all
                        # nodes before this one topologically had been added
                        # so not all predecessors were removed
                        raise TranspilerError("Not all predecessors removed due to error"
                                              " in topological order")

                    block_count += 1

                # we have now seen all predecessors
                # so update the blocks list to include this block
                blocks = blocks[:block_count] + [[node]] + blocks[block_count:]

        # create the dag from the updated list of blocks
        basis_gate_name = self.decomposer.gate.name
        for block in blocks:
            if len(block) == 1 and block[0].name != basis_gate_name:
                # pylint: disable=too-many-boolean-expressions
                if block[0].type == 'op' \
                        and self.basis_gates \
                        and block[0].name not in self.basis_gates \
                        and len(block[0].cargs) == 0 and block[0].condition is None \
                        and isinstance(block[0].op, Gate) \
                        and hasattr(block[0].op, '__array__') \
                        and not block[0].op.is_parameterized():
                    new_dag.apply_operation_back(UnitaryGate(block[0].op.to_matrix()),
                                                 block[0].qargs, block[0].cargs)
                else:
                    # an intermediate node that was added into the overall list
                    new_dag.apply_operation_back(block[0].op, block[0].qargs,
                                                 block[0].cargs)
            else:
                # find the qubits involved in this block
                block_qargs = set()
                block_cargs = set()
                for nd in block:
                    block_qargs |= set(nd.qargs)
                    if nd.condition:
                        block_cargs |= set(nd.condition[0])
                # convert block to a sub-circuit, then simulate unitary and add
                q = QuantumRegister(len(block_qargs))
                # if condition in node, add clbits to circuit
                if len(block_cargs) > 0:
                    c = ClassicalRegister(len(block_cargs))
                    subcirc = QuantumCircuit(q, c)
                else:
                    subcirc = QuantumCircuit(q)
                block_index_map = self._block_qargs_to_indices(block_qargs,
                                                               global_index_map)
                basis_count = 0
                for nd in block:
                    if nd.op.name == basis_gate_name:
                        basis_count += 1
                    subcirc.append(nd.op, [q[block_index_map[i]] for i in nd.qargs])
                unitary = UnitaryGate(Operator(subcirc))  # simulates the circuit

                max_2q_depth = 20  # If depth > 20, there will be 1q gates to consolidate.
                if (  # pylint: disable=too-many-boolean-expressions
                        self.force_consolidate
                        or unitary.num_qubits > 2
                        or self.decomposer.num_basis_gates(unitary) < basis_count
                        or len(subcirc) > max_2q_depth
                        or (self.basis_gates is not None
                            and not set(subcirc.count_ops()).issubset(self.basis_gates))
                ):
                    new_dag.apply_operation_back(
                        unitary,
                        sorted(block_qargs, key=lambda x: block_index_map[x]))
                else:
                    for nd in block:
                        new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)

        return new_dag
 def test_euler_angles_1q_hard_thetas(self):
     """Verify euler_angles_1q for close-to-degenerate theta"""
     for gate in HARD_THETA_ONEQS:
         self.check_one_qubit_euler_angles(Operator(gate))
 def test_exact_two_qubit_cnot_decompose_paulis(self):
     """Verify exact CNOT decomposition for Paulis
     """
     pauli_xz = Pauli(label='XZ')
     unitary = Operator(pauli_xz)
     self.check_exact_decomposition(unitary.data, two_qubit_cnot_decompose)
示例#4
0
def Two_Controlled_Unitary(U):
    Q = np.kron( np.diag([0,0,0,1]),U)
    Q += np.diag([1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0])
    return Operator(Q)
示例#5
0
 def test_unitary_decomposition(self):
     """Test decomposition for unitary gates over 2 qubits."""
     qc = QuantumCircuit(3)
     qc.unitary(random_unitary(8, seed=42), [0, 1, 2])
     self.assertTrue(Operator(qc).equiv(Operator(qc.decompose())))
 def actual_fidelity(self, **kwargs) -> float:
     """Calculates the actual fidelity of the decomposed circuit to the input unitary"""
     circ = self.circuit(**kwargs)
     trace = np.trace(Operator(circ).data.T.conj() @ self.unitary_matrix)
     return trace_to_fid(trace)
示例#7
0
def sqrtw():
    """Returns the single qubit gate Sqrt(W)"""
    return Operator([[(1. + 1.j) / 2, -1.j / np.sqrt(2)],
                     [1. / np.sqrt(2), (1. + 1.j) / 2]])
示例#8
0
def MPS_to_circuit(A, phi_initial, phi_final):
    """
    INPUT:
    A (list): list of N MPS tensors of size (d,chi,chi)
    phi_initial (vector): right boundary condition of size (chi,1)
    phi_final (vector): left boundary condition of size (chi,1)
    keep_ancilla (bool, optional):  the MPS is generated via an ancilla
                                    that disentangles at the end of the
                                    procedure. By default, the ancilla is
                                    measured out and thrown away at the end.
                                    Set to True to keep the ancilla
                                    (in the first ceil(log(chi)) qubits)

    OUTPUT:
    qc(QuantumCircuit): the circuit that produces the MPS
    
         q0...qn   phi_final - A_{N-1} - A_{N-2} - ... - A_0 - phi_initial
                                 |          |             |
                               q_{n+N}  q_{n+N-1}       q_{n+1}
                               
                   where n = ceil(log2(chi)) is the number of ancilla qubits
    
    reg(QuantumRegister):   the register in which the MPS wavefunction sits
                            (to distinguish from the ancilla)
                            
    N.B. By construction, after applying qc the system will be in a product
         state between the first ceil(log2(chi)) qubits and the rest.
         Those first qubits form the ancilla register and the remaining qubits
         are in the QuantumRegister 'reg'.
         The ancilla is guaranteed to be in phi_final.
    """
    N = len(A)
    chi = A[0].shape[1]
    d = A[0].shape[0]

    #Normalize boundary conditions
    phi_final = phi_final / np.linalg.norm(phi_final)
    phi_initial = phi_initial / np.linalg.norm(phi_initial)

    #Convert MPS to isometric form
    Vs, phi_initial_U, phi_final_U, Ms = convert_to_isometry(
        A, phi_initial, phi_final)

    #Construct circuit
    n_ancilla_qubits = int(np.log2(chi))

    ancilla = QuantumRegister(n_ancilla_qubits)
    reg = QuantumRegister(N)

    qc = QuantumCircuit(ancilla, reg)

    phi_initial_U = phi_initial_U / np.linalg.norm(phi_initial_U)
    qc.initialize(phi_initial_U, range(n_ancilla_qubits))

    for i in range(N):
        qubits = list(range(n_ancilla_qubits))
        qubits.append(i + n_ancilla_qubits)
        qc.unitary(Operator(isometry_to_unitary(Vs[i].reshape(d, chi, chi))),
                   qubits)

    return qc, reg
示例#9
0
def process_fidelity(channel1, channel2, require_cptp=True):
    """Return the process fidelity between two quantum channels.

    This is given by

        F_p(E1, E2) = Tr[S2^dagger.S1])/dim^2

    where S1 and S2 are the SuperOp matrices for channels E1 and E2,
    and dim is the dimension of the input output statespace.

    Args:
        channel1 (QuantumChannel or matrix): a quantum channel or unitary matrix.
        channel2 (QuantumChannel or matrix): a quantum channel or unitary matrix.
        require_cptp (bool): require input channels to be CPTP [Default: True].

    Returns:
        array_like: The state fidelity F(state1, state2).

    Raises:
        QiskitError: if inputs channels do not have the same dimensions,
        have different input and output dimensions, or are not CPTP with
        `require_cptp=True`.
    """
    # First we must determine if input is to be interpreted as a unitary matrix
    # or as a channel.
    # If input is a raw numpy array we will interpret it as a unitary matrix.
    is_cptp1 = None
    is_cptp2 = None
    if isinstance(channel1, (list, np.ndarray)):
        channel1 = Operator(channel1)
        if require_cptp:
            is_cptp1 = channel1.is_unitary()
    if isinstance(channel2, (list, np.ndarray)):
        channel2 = Operator(channel2)
        if require_cptp:
            is_cptp2 = channel2.is_unitary()

    # Next we convert inputs SuperOp objects
    # This works for objects that also have a `to_operator` or `to_channel` method
    s1 = SuperOp(channel1)
    s2 = SuperOp(channel2)

    # Check inputs are CPTP
    if require_cptp:
        # Only check SuperOp if we didn't already check unitary inputs
        if is_cptp1 is None:
            is_cptp1 = s1.is_cptp()
        if not is_cptp1:
            raise QiskitError('channel1 is not CPTP')
        if is_cptp2 is None:
            is_cptp2 = s2.is_cptp()
        if not is_cptp2:
            raise QiskitError('channel2 is not CPTP')

    # Check dimensions match
    input_dim1, output_dim1 = s1.dim
    input_dim2, output_dim2 = s2.dim
    if input_dim1 != output_dim1 or input_dim2 != output_dim2:
        raise QiskitError('Input channels must have same size input and output dimensions.')
    if input_dim1 != input_dim2:
        raise QiskitError('Input channels have different dimensions.')

    # Compute process fidelity
    fidelity = np.trace(s1.compose(s2.adjoint()).data) / (input_dim1 ** 2)
    return fidelity
示例#10
0
def iswap():
    return Operator([[1., 0., 0., 0.], [0., 0., -1.j, 0.], [0., -1.j, 0., 0.],
                     [0., 0., 0., 1.]])
示例#11
0
def sqrtiswap():
    return Operator([[1., 0., 0., 0.],
                     [0., 1. / np.sqrt(2), -1.j / np.sqrt(2), 0.],
                     [0., -1.j / np.sqrt(2), 1. / np.sqrt(2), 0.],
                     [0., 0., 0., 1.]])
    def run(self, dag):
        """iterate over each block and replace it with an equivalent Unitary
        on the same wires.
        """
        new_dag = DAGCircuit()
        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        # compute ordered indices for the global circuit wires
        global_index_map = {}
        for wire in dag.wires:
            if not isinstance(wire, Qubit):
                continue
            global_qregs = list(dag.qregs.values())
            global_index_map[wire] = global_qregs.index(
                wire.register) + wire.index

        blocks = self.property_set['block_list']
        nodes_seen = set()

        for node in dag.topological_op_nodes():
            # skip already-visited nodes or input/output nodes
            if node in nodes_seen or node.type == 'in' or node.type == 'out':
                continue
            # check if the node belongs to the next block
            if blocks and node in blocks[0]:
                block = blocks[0]
                # find the qubits involved in this block
                block_qargs = set()
                for nd in block:
                    block_qargs |= set(nd.qargs)
                # convert block to a sub-circuit, then simulate unitary and add
                block_width = len(block_qargs)
                q = QuantumRegister(block_width)
                subcirc = QuantumCircuit(q)
                block_index_map = self._block_qargs_to_indices(
                    block_qargs, global_index_map)
                for nd in block:
                    nodes_seen.add(nd)
                    subcirc.append(nd.op,
                                   [q[block_index_map[i]] for i in nd.qargs])
                unitary = UnitaryGate(
                    Operator(subcirc))  # simulates the circuit
                new_dag.apply_operation_back(
                    unitary,
                    sorted(block_qargs, key=lambda x: block_index_map[x]))
                del blocks[0]
            else:
                # the node could belong to some future block, but in that case
                # we simply skip it. It is guaranteed that we will revisit that
                # future block, via its other nodes
                for block in blocks[1:]:
                    if node in block:
                        break
                # freestanding nodes can just be added
                else:
                    nodes_seen.add(node)
                    new_dag.apply_operation_back(node.op, node.qargs,
                                                 node.cargs)

        return new_dag
示例#13
0
 def test_unitary_decomposition_via_definition_2q(self):
     """Test decomposition for 2Q unitary via definition."""
     mat = numpy.array([[0, 0, 1, 0], [0, 0, 0, -1], [1, 0, 0, 0],
                        [0, -1, 0, 0]])
     self.assertTrue(
         numpy.allclose(Operator(UnitaryGate(mat).definition).data, mat))
示例#14
0
    def test_qv_natural(self):
        """check that quantum volume circuit compiles for natural direction"""
        qv64 = QuantumVolume(5, seed=15)

        def construct_passmanager(basis_gates, coupling_map, synthesis_fidelity, pulse_optimize):
            def _repeat_condition(property_set):
                return not property_set["depth_fixed_point"]

            seed = 2
            _map = [SabreLayout(coupling_map, max_iterations=2, seed=seed)]
            _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]
            _unroll3q = Unroll3qOrMore()
            _swap_check = CheckMap(coupling_map)
            _swap = [
                BarrierBeforeFinalMeasurements(),
                SabreSwap(coupling_map, heuristic="lookahead", seed=seed),
            ]
            _check_depth = [Depth(), FixedPoint("depth")]
            _optimize = [
                Collect2qBlocks(),
                ConsolidateBlocks(basis_gates=basis_gates),
                UnitarySynthesis(
                    basis_gates,
                    synthesis_fidelity,
                    coupling_map,
                    pulse_optimize=pulse_optimize,
                    natural_direction=True,
                ),
                Optimize1qGates(basis_gates),
            ]

            pm = PassManager()
            pm.append(_map)  # map to hardware by inserting swaps
            pm.append(_embed)
            pm.append(_unroll3q)
            pm.append(_swap_check)
            pm.append(_swap)
            pm.append(
                _check_depth + _optimize, do_while=_repeat_condition
            )  # translate to & optimize over hardware native gates
            return pm

        coupling_map = CouplingMap([[0, 1], [1, 2], [3, 2], [3, 4], [5, 4]])
        basis_gates = ["rz", "sx", "cx"]

        pm1 = construct_passmanager(
            basis_gates=basis_gates,
            coupling_map=coupling_map,
            synthesis_fidelity=0.99,
            pulse_optimize=True,
        )
        pm2 = construct_passmanager(
            basis_gates=basis_gates,
            coupling_map=coupling_map,
            synthesis_fidelity=0.99,
            pulse_optimize=False,
        )

        qv64_1 = pm1.run(qv64.decompose())
        qv64_2 = pm2.run(qv64.decompose())
        edges = [list(edge) for edge in coupling_map.get_edges()]
        self.assertTrue(
            all(
                # pylint: disable=no-member
                [qv64_1.qubits.index(qubit) for qubit in qlist] in edges
                # pylint: disable=no-member
                for _, qlist, _ in qv64_1.get_instructions("cx")
            )
        )
        self.assertEqual(Operator(qv64_1), Operator(qv64_2))
    def _get_sx_vz_2cx_efficient_euler(self, decomposition, target_decomposed):
        """
        Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT gates assuming
        two CNOT gates are needed.

        This first decomposes each unitary from the KAK decomposition into ZXZ on the source
        qubit of the CNOTs and XZX on the targets in order to commute operators to beginning and
        end of decomposition. The beginning and ending single qubit gates are then
        collapsed and re-decomposed with the single qubit decomposer. This last step could be avoided
        if performance is a concern.
        """
        best_nbasis = 2  # by assumption
        num_1q_uni = len(decomposition)
        # list of euler angle decompositions on qubits 0 and 1
        euler_q0 = np.empty((num_1q_uni // 2, 3), dtype=float)
        euler_q1 = np.empty((num_1q_uni // 2, 3), dtype=float)
        global_phase = 0.0

        # decompose source unitaries to zxz
        zxz_decomposer = OneQubitEulerDecomposer("ZXZ")
        for iqubit, decomp in enumerate(decomposition[0::2]):
            euler_angles = zxz_decomposer.angles_and_phase(decomp)
            euler_q0[iqubit, [1, 2, 0]] = euler_angles[:3]
            global_phase += euler_angles[3]
        # decompose target unitaries to xzx
        xzx_decomposer = OneQubitEulerDecomposer("XZX")
        for iqubit, decomp in enumerate(decomposition[1::2]):
            euler_angles = xzx_decomposer.angles_and_phase(decomp)
            euler_q1[iqubit, [1, 2, 0]] = euler_angles[:3]
            global_phase += euler_angles[3]
        qc = QuantumCircuit(2)
        qc.global_phase = target_decomposed.global_phase
        qc.global_phase -= best_nbasis * self.basis.global_phase
        qc.global_phase += global_phase

        # TODO: make this more effecient to avoid double decomposition
        # prepare beginning 0th qubit local unitary
        circ = QuantumCircuit(1)
        circ.rz(euler_q0[0][0], 0)
        circ.rx(euler_q0[0][1], 0)
        circ.rz(euler_q0[0][2] + euler_q0[1][0] + math.pi / 2, 0)
        # re-decompose to basis of 1q decomposer
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [0], inplace=True)

        # prepare beginning 1st qubit local unitary
        circ = QuantumCircuit(1)
        circ.rx(euler_q1[0][0], 0)
        circ.rz(euler_q1[0][1], 0)
        circ.rx(euler_q1[0][2] + euler_q1[1][0], 0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [1], inplace=True)

        qc.cx(0, 1)
        # the central decompositions are dependent on the specific form of the
        # unitaries coming out of the two qubit decomposer which have some flexibility
        # of choice.
        qc.sx(0)
        qc.rz(euler_q0[1][1] - math.pi, 0)
        qc.sx(0)
        qc.rz(euler_q1[1][1], 1)
        qc.global_phase += math.pi / 2

        qc.cx(0, 1)

        circ = QuantumCircuit(1)
        circ.rz(euler_q0[1][2] + euler_q0[2][0] + math.pi / 2, 0)
        circ.rx(euler_q0[2][1], 0)
        circ.rz(euler_q0[2][2], 0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [0], inplace=True)
        circ = QuantumCircuit(1)
        circ.rx(euler_q1[1][2] + euler_q1[2][0], 0)
        circ.rz(euler_q1[2][1], 0)
        circ.rx(euler_q1[2][2], 0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [1], inplace=True)

        return qc
示例#16
0
 def test_transpose(self, label):
     """Test transpose method."""
     value = Pauli(label).transpose()
     target = operator_from_label(label).transpose()
     self.assertEqual(Operator(value), target)
    def _get_sx_vz_3cx_efficient_euler(self, decomposition, target_decomposed):
        """
        Decomposition of SU(4) gate for device with SX, virtual RZ, and CNOT gates assuming
        three CNOT gates are needed.

        This first decomposes each unitary from the KAK decomposition into ZXZ on the source
        qubit of the CNOTs and XZX on the targets in order commute operators to beginning and
        end of decomposition. Inserting Hadamards reverses the direction of the CNOTs and transforms
        a variable Rx -> variable virtual Rz. The beginning and ending single qubit gates are then
        collapsed and re-decomposed with the single qubit decomposer. This last step could be avoided
        if performance is a concern.
        """
        best_nbasis = 3  # by assumption
        num_1q_uni = len(decomposition)
        # create structure to hold euler angles: 1st index represents unitary "group" wrt cx
        # 2nd index represents index of euler triple.
        euler_q0 = np.empty((num_1q_uni // 2, 3), dtype=float)
        euler_q1 = np.empty((num_1q_uni // 2, 3), dtype=float)
        global_phase = 0.0
        atol = 1e-10  # absolute tolerance for floats

        # decompose source unitaries to zxz
        zxz_decomposer = OneQubitEulerDecomposer("ZXZ")
        for iqubit, decomp in enumerate(decomposition[0::2]):
            euler_angles = zxz_decomposer.angles_and_phase(decomp)
            euler_q0[iqubit, [1, 2, 0]] = euler_angles[:3]
            global_phase += euler_angles[3]
        # decompose target unitaries to xzx
        xzx_decomposer = OneQubitEulerDecomposer("XZX")
        for iqubit, decomp in enumerate(decomposition[1::2]):
            euler_angles = xzx_decomposer.angles_and_phase(decomp)
            euler_q1[iqubit, [1, 2, 0]] = euler_angles[:3]
            global_phase += euler_angles[3]

        qc = QuantumCircuit(2)
        qc.global_phase = target_decomposed.global_phase
        qc.global_phase -= best_nbasis * self.basis.global_phase
        qc.global_phase += global_phase

        x12 = euler_q0[1][2] + euler_q0[2][0]
        x12_isNonZero = not math.isclose(x12, 0, abs_tol=atol)
        x12_isOddMult = None
        x12_isPiMult = math.isclose(math.sin(x12), 0, abs_tol=atol)
        if x12_isPiMult:
            x12_isOddMult = math.isclose(math.cos(x12), -1, abs_tol=atol)
            x12_phase = math.pi * math.cos(x12)
        x02_add = x12 - euler_q0[1][0]
        x12_isHalfPi = math.isclose(x12, math.pi / 2, abs_tol=atol)

        # TODO: make this more effecient to avoid double decomposition
        circ = QuantumCircuit(1)
        circ.rz(euler_q0[0][0], 0)
        circ.rx(euler_q0[0][1], 0)
        if x12_isNonZero and x12_isPiMult:
            circ.rz(euler_q0[0][2] - x02_add, 0)
        else:
            circ.rz(euler_q0[0][2] + euler_q0[1][0], 0)
        circ.h(0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [0], inplace=True)

        circ = QuantumCircuit(1)
        circ.rx(euler_q1[0][0], 0)
        circ.rz(euler_q1[0][1], 0)
        circ.rx(euler_q1[0][2] + euler_q1[1][0], 0)
        circ.h(0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [1], inplace=True)

        qc.cx(1, 0)

        if x12_isPiMult:
            # even or odd multiple
            if x12_isNonZero:
                qc.global_phase += x12_phase
            if x12_isNonZero and x12_isOddMult:
                qc.rz(-euler_q0[1][1], 0)
            else:
                qc.rz(euler_q0[1][1], 0)
                qc.global_phase += math.pi
        if x12_isHalfPi:
            qc.sx(0)
            qc.global_phase -= math.pi / 4
        elif x12_isNonZero and not x12_isPiMult:
            # this is non-optimal but doesn't seem to occur currently
            if self.pulse_optimize is None:
                qc.compose(self._decomposer1q(Operator(RXGate(x12)).data), [0],
                           inplace=True)
            else:
                raise QiskitError(
                    "possible non-pulse-optimal decomposition encountered")
        if math.isclose(euler_q1[1][1], math.pi / 2, abs_tol=atol):
            qc.sx(1)
            qc.global_phase -= math.pi / 4
        else:
            # this is non-optimal but doesn't seem to occur currently
            if self.pulse_optimize is None:
                qc.compose(self._decomposer1q(
                    Operator(RXGate(euler_q1[1][1])).data), [1],
                           inplace=True)
            else:
                raise QiskitError(
                    "possible non-pulse-optimal decomposition encountered")
        qc.rz(euler_q1[1][2] + euler_q1[2][0], 1)

        qc.cx(1, 0)

        qc.rz(euler_q0[2][1], 0)
        if math.isclose(euler_q1[2][1], math.pi / 2, abs_tol=atol):
            qc.sx(1)
            qc.global_phase -= math.pi / 4
        else:
            # this is non-optimal but doesn't seem to occur currently
            if self.pulse_optimize is None:
                qc.compose(self._decomposer1q(
                    Operator(RXGate(euler_q1[2][1])).data), [1],
                           inplace=True)
            else:
                raise QiskitError(
                    "possible non-pulse-optimal decomposition encountered")

        qc.cx(1, 0)

        circ = QuantumCircuit(1)
        circ.h(0)
        circ.rz(euler_q0[2][2] + euler_q0[3][0], 0)
        circ.rx(euler_q0[3][1], 0)
        circ.rz(euler_q0[3][2], 0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [0], inplace=True)

        circ = QuantumCircuit(1)
        circ.h(0)
        circ.rx(euler_q1[2][2] + euler_q1[3][0], 0)
        circ.rz(euler_q1[3][1], 0)
        circ.rx(euler_q1[3][2], 0)
        qceuler = self._decomposer1q(Operator(circ).data)
        qc.compose(qceuler, [1], inplace=True)

        # TODO: fix the sign problem to avoid correction here
        if cmath.isclose(target_decomposed.unitary_matrix[0, 0],
                         -(Operator(qc).data[0, 0]),
                         abs_tol=atol):
            qc.global_phase += math.pi
        return qc
示例#18
0
 def test_adjoint(self, label):
     """Test adjoint method."""
     value = Pauli(label).adjoint()
     target = operator_from_label(label).adjoint()
     self.assertEqual(Operator(value), target)
    def __init__(self,
                 gate,
                 basis_fidelity=1.0,
                 euler_basis=None,
                 pulse_optimize=None):
        self.gate = gate
        self.basis_fidelity = basis_fidelity
        self.pulse_optimize = pulse_optimize

        basis = self.basis = TwoQubitWeylDecomposition(Operator(gate).data)
        if euler_basis is not None:
            self._decomposer1q = OneQubitEulerDecomposer(euler_basis)
        else:
            self._decomposer1q = OneQubitEulerDecomposer("U3")

        # FIXME: find good tolerances
        self.is_supercontrolled = math.isclose(
            basis.a, np.pi / 4) and math.isclose(basis.c, 0.0)

        # Create some useful matrices U1, U2, U3 are equivalent to the basis,
        # expand as Ui = Ki1.Ubasis.Ki2
        b = basis.b
        K11l = (1 / (1 + 1j) * np.array(
            [
                [-1j * cmath.exp(-1j * b),
                 cmath.exp(-1j * b)],
                [-1j * cmath.exp(1j * b), -cmath.exp(1j * b)],
            ],
            dtype=complex,
        ))
        K11r = (1 / math.sqrt(2) * np.array(
            [
                [1j * cmath.exp(-1j * b), -cmath.exp(-1j * b)],
                [cmath.exp(1j * b), -1j * cmath.exp(1j * b)],
            ],
            dtype=complex,
        ))
        K12l = 1 / (1 + 1j) * np.array([[1j, 1j], [-1, 1]], dtype=complex)
        K12r = 1 / math.sqrt(2) * np.array([[1j, 1], [-1, -1j]], dtype=complex)
        K32lK21l = (1 / math.sqrt(2) * np.array(
            [
                [1 + 1j * np.cos(2 * b), 1j * np.sin(2 * b)],
                [1j * np.sin(2 * b), 1 - 1j * np.cos(2 * b)],
            ],
            dtype=complex,
        ))
        K21r = (1 / (1 - 1j) * np.array(
            [
                [-1j * cmath.exp(-2j * b),
                 cmath.exp(-2j * b)],
                [1j * cmath.exp(2j * b),
                 cmath.exp(2j * b)],
            ],
            dtype=complex,
        ))
        K22l = 1 / math.sqrt(2) * np.array([[1, -1], [1, 1]], dtype=complex)
        K22r = np.array([[0, 1], [-1, 0]], dtype=complex)
        K31l = (1 / math.sqrt(2) * np.array(
            [[cmath.exp(-1j * b), cmath.exp(-1j * b)],
             [-cmath.exp(1j * b), cmath.exp(1j * b)]],
            dtype=complex,
        ))
        K31r = 1j * np.array(
            [[cmath.exp(1j * b), 0], [0, -cmath.exp(-1j * b)]], dtype=complex)
        K32r = (1 / (1 - 1j) * np.array(
            [
                [cmath.exp(1j * b), -cmath.exp(-1j * b)],
                [-1j * cmath.exp(1j * b), -1j * cmath.exp(-1j * b)],
            ],
            dtype=complex,
        ))
        k1ld = basis.K1l.T.conj()
        k1rd = basis.K1r.T.conj()
        k2ld = basis.K2l.T.conj()
        k2rd = basis.K2r.T.conj()

        # Pre-build the fixed parts of the matrices used in 3-part decomposition
        self.u0l = K31l.dot(k1ld)
        self.u0r = K31r.dot(k1rd)
        self.u1l = k2ld.dot(K32lK21l).dot(k1ld)
        self.u1ra = k2rd.dot(K32r)
        self.u1rb = K21r.dot(k1rd)
        self.u2la = k2ld.dot(K22l)
        self.u2lb = K11l.dot(k1ld)
        self.u2ra = k2rd.dot(K22r)
        self.u2rb = K11r.dot(k1rd)
        self.u3l = k2ld.dot(K12l)
        self.u3r = k2rd.dot(K12r)

        # Pre-build the fixed parts of the matrices used in the 2-part decomposition
        self.q0l = K12l.T.conj().dot(k1ld)
        self.q0r = K12r.T.conj().dot(_ipz).dot(k1rd)
        self.q1la = k2ld.dot(K11l.T.conj())
        self.q1lb = K11l.dot(k1ld)
        self.q1ra = k2rd.dot(_ipz).dot(K11r.T.conj())
        self.q1rb = K11r.dot(k1rd)
        self.q2l = k2ld.dot(K12l)
        self.q2r = k2rd.dot(K12r)

        # Decomposition into different number of gates
        # In the future could use different decomposition functions for different basis classes, etc
        if not self.is_supercontrolled:
            warnings.warn(
                "Only know how to decompose properly for supercontrolled basis gate. "
                "This gate is ~Ud({}, {}, {})".format(basis.a, basis.b,
                                                      basis.c),
                stacklevel=2,
            )
        self.decomposition_fns = [
            self.decomp0,
            self.decomp1,
            self.decomp2_supercontrolled,
            self.decomp3_supercontrolled,
        ]
        self._rqc = None
示例#20
0
 def test_to_operator(self, label):
     """Test Pauli operator conversion"""
     value = Operator(Pauli(label))
     target = operator_from_label(label)
     self.assertEqual(value, target)
示例#21
0
def sqrtx():
    """Returns the single qubit gate Sqrt(X)"""
    return Operator([[(1. + 1.j) / 2, (1. - 1.j) / 2],
                     [(1. - 1.j) / 2, (1. + 1.j) / 2]])
示例#22
0
 def test_to_matrix_sparse(self, label):
     """Test Pauli operator conversion"""
     spmat = Pauli(label).to_matrix(sparse=True)
     value = Operator(spmat.todense())
     target = operator_from_label(label)
     self.assertEqual(value, target)
示例#23
0
            [U3Gate(np.pi/2 + smallest * factor**i, phi, lam) for i in range(steps)] +
            [U3Gate(np.pi/2 - smallest * factor**i, phi, lam) for i in range(steps)] +
            [U3Gate(np.pi + smallest * factor**i, phi, lam) for i in range(steps)] +
            [U3Gate(np.pi - smallest * factor**i, phi, lam) for i in range(steps)] +
            [U3Gate(3*np.pi/2 + smallest * factor**i, phi, lam) for i in range(steps)] +
            [U3Gate(3*np.pi/2 - smallest * factor**i, phi, lam) for i in range(steps)])


HARD_THETA_ONEQS = make_hard_thetas_oneq()


# It's too slow to use all 24**4 Clifford combos. If we can make it faster, use a larger set
K1K2S = [(ONEQ_CLIFFORDS[3], ONEQ_CLIFFORDS[5], ONEQ_CLIFFORDS[2], ONEQ_CLIFFORDS[21]),
         (ONEQ_CLIFFORDS[5], ONEQ_CLIFFORDS[6], ONEQ_CLIFFORDS[9], ONEQ_CLIFFORDS[7]),
         (ONEQ_CLIFFORDS[2], ONEQ_CLIFFORDS[1], ONEQ_CLIFFORDS[0], ONEQ_CLIFFORDS[4]),
         [Operator(U3Gate(x, y, z)) for x, y, z in
          [(0.2, 0.3, 0.1), (0.7, 0.15, 0.22), (0.001, 0.97, 2.2), (3.14, 2.1, 0.9)]]]


class TestEulerAngles1Q(QiskitTestCase):
    """Test euler_angles_1q()"""

    def check_one_qubit_euler_angles(self, operator, tolerance=1e-14):
        """Check euler_angles_1q works for the given unitary
        """
        with self.subTest(operator=operator):
            target_unitary = operator.data
            angles = euler_angles_1q(target_unitary)
            decomp_unitary = U3Gate(*angles).to_matrix()
            target_unitary *= la.det(target_unitary)**(-0.5)
            decomp_unitary *= la.det(decomp_unitary)**(-0.5)
示例#24
0
 def test_to_instruction(self, label):
     """Test Pauli to instruction"""
     pauli = Pauli(label)
     value = Operator(pauli.to_instruction())
     target = Operator(pauli)
     self.assertEqual(value, target)
             U3Gate(3 * np.pi / 2 - smallest * factor**i, phi, lam)
             for i in range(steps)
         ])


HARD_THETA_ONEQS = make_hard_thetas_oneq()

# It's too slow to use all 24**4 Clifford combos. If we can make it faster, use a larger set
K1K2S = [(ONEQ_CLIFFORDS[3], ONEQ_CLIFFORDS[5], ONEQ_CLIFFORDS[2],
          ONEQ_CLIFFORDS[21]),
         (ONEQ_CLIFFORDS[5], ONEQ_CLIFFORDS[6], ONEQ_CLIFFORDS[9],
          ONEQ_CLIFFORDS[7]),
         (ONEQ_CLIFFORDS[2], ONEQ_CLIFFORDS[1], ONEQ_CLIFFORDS[0],
          ONEQ_CLIFFORDS[4]),
         [
             Operator(U3Gate(x, y, z))
             for x, y, z in [(0.2, 0.3, 0.1), (0.7, 0.15,
                                               0.22), (0.001, 0.97,
                                                       2.2), (3.14, 2.1, 0.9)]
         ]]


class TestEulerAngles1Q(QiskitTestCase):
    """Test euler_angles_1q()"""
    def check_one_qubit_euler_angles(self, operator, tolerance=1e-14):
        """Check euler_angles_1q works for the given unitary"""
        with self.subTest(operator=operator):
            target_unitary = operator.data
            angles = euler_angles_1q(target_unitary)
            decomp_unitary = U3Gate(*angles).to_matrix()
            target_unitary *= la.det(target_unitary)**(-0.5)
示例#26
0
 def test_unitary_decomposition_via_definition(self):
     """Test decomposition for 1Q unitary via definition."""
     mat = numpy.array([[0, 1], [1, 0]])
     numpy.allclose(Operator(UnitaryGate(mat).definition).data, mat)
 def test_one_qubit_hard_thetas_xyx_basis(self):
     """Verify for rx, ry, rx basis and close-to-degenerate theta."""
     for gate in HARD_THETA_ONEQS:
         self.check_one_qubit_euler_angles(Operator(gate), 'XYX')
示例#28
0
    def run(self, dag):
        """Run the ConsolidateBlocks pass on `dag`.

        Iterate over each block and replace it with an equivalent Unitary
        on the same wires.
        """
        new_dag = DAGCircuit()
        for qreg in dag.qregs.values():
            new_dag.add_qreg(qreg)
        for creg in dag.cregs.values():
            new_dag.add_creg(creg)

        # compute ordered indices for the global circuit wires
        global_index_map = {wire: idx for idx, wire in enumerate(dag.qubits())}

        blocks = self.property_set['block_list']
        # just to make checking if a node is in any block easier
        all_block_nodes = {nd for bl in blocks for nd in bl}

        for node in dag.topological_op_nodes():
            if node not in all_block_nodes:
                # need to add this node to find out where in the list it goes
                preds = [
                    nd for nd in dag.predecessors(node) if nd.type == 'op'
                ]

                block_count = 0
                while preds:
                    if block_count < len(blocks):
                        block = blocks[block_count]

                        # if any of the predecessors are in the block, remove them
                        preds = [p for p in preds if p not in block]
                    else:
                        # should never occur as this would mean not all
                        # nodes before this one topologically had been added
                        # so not all predecessors were removed
                        raise TranspilerError(
                            "Not all predecessors removed due to error"
                            " in topological order")

                    block_count += 1

                # we have now seen all predecessors
                # so update the blocks list to include this block
                blocks = blocks[:block_count] + [[node]] + blocks[block_count:]

        # create the dag from the updated list of blocks
        basis_gate_name = self.decomposer.gate.name
        for block in blocks:

            if len(block) == 1 and block[0].name != 'cx':
                # an intermediate node that was added into the overall list
                new_dag.apply_operation_back(block[0].op, block[0].qargs,
                                             block[0].cargs,
                                             block[0].condition)
            else:
                # find the qubits involved in this block
                block_qargs = set()
                for nd in block:
                    block_qargs |= set(nd.qargs)
                # convert block to a sub-circuit, then simulate unitary and add
                block_width = len(block_qargs)
                q = QuantumRegister(block_width)
                subcirc = QuantumCircuit(q)
                block_index_map = self._block_qargs_to_indices(
                    block_qargs, global_index_map)
                basis_count = 0
                for nd in block:
                    if nd.op.name == basis_gate_name:
                        basis_count += 1
                    subcirc.append(nd.op,
                                   [q[block_index_map[i]] for i in nd.qargs])
                unitary = UnitaryGate(
                    Operator(subcirc))  # simulates the circuit
                if self.force_consolidate or unitary.num_qubits > 2 or \
                        self.decomposer.num_basis_gates(unitary) != basis_count:

                    new_dag.apply_operation_back(
                        unitary,
                        sorted(block_qargs, key=lambda x: block_index_map[x]))
                else:
                    for nd in block:
                        new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs,
                                                     nd.condition)

        return new_dag
示例#29
0
 def test_conjugate(self, label):
     """Test conjugate method."""
     value = Pauli(label).conjugate()
     target = operator_from_label(label).conjugate()
     self.assertEqual(Operator(value), target)
示例#30
0
 def test_div(self, num_qubits, value):
     """Test / method for {num_qubits} qubits and value {value}."""
     spp_op = random_unitary(2**num_qubits, seed=self.RNG)
     target = Operator(spp_op) / value
     value = (spp_op / value).to_operator()
     self.assertEqual(value, target)