示例#1
0
def _does_commute(node1, node2):
    """Function to verify commutation relation between two nodes in the DAG.

    Args:
        node1 (DAGnode): first node operation
        node2 (DAGnode): second node operation

    Return:
        bool: True if the nodes commute and false if it is not the case.
    """

    # Create set of qubits on which the operation acts
    qarg1 = [node1.qargs[i] for i in range(0, len(node1.qargs))]
    qarg2 = [node2.qargs[i] for i in range(0, len(node2.qargs))]

    # Create set of cbits on which the operation acts
    carg1 = [node1.cargs[i] for i in range(0, len(node1.cargs))]
    carg2 = [node2.cargs[i] for i in range(0, len(node2.cargs))]

    # Commutation for classical conditional gates
    # if and only if the qubits are different.
    # TODO: qubits can be the same if conditions are identical and
    # the non-conditional gates commute.
    if node1.condition or node2.condition:
        intersection = set(qarg1).intersection(set(qarg2))
        return not intersection

    # Commutation for non-unitary or parameterized or opaque ops
    # (e.g. measure, reset, directives or pulse gates)
    # if and only if the qubits and clbits are different.
    non_unitaries = ['measure', 'reset', 'initialize', 'delay']

    def _unknown_commutator(n):
        return (n.op._directive or
                n.name in non_unitaries or
                n.op.is_parameterized())

    if _unknown_commutator(node1) or _unknown_commutator(node2):
        intersection_q = set(qarg1).intersection(set(qarg2))
        intersection_c = set(carg1).intersection(set(carg2))
        return not (intersection_q or intersection_c)

    # Known non-commuting gates (TODO: add more).
    non_commute_gates = [{'x', 'y'}, {'x', 'z'}]
    if qarg1 == qarg2 and ({node1.name, node2.name} in non_commute_gates):
        return False

    # Create matrices to check commutation relation if no other criteria are matched
    qarg = list(set(node1.qargs + node2.qargs))
    qbit_num = len(qarg)

    qarg1 = [qarg.index(q) for q in node1.qargs]
    qarg2 = [qarg.index(q) for q in node2.qargs]

    id_op = Operator(np.eye(2 ** qbit_num))

    op12 = id_op.compose(node1.op, qargs=qarg1).compose(node2.op, qargs=qarg2)
    op21 = id_op.compose(node2.op, qargs=qarg2).compose(node1.op, qargs=qarg1)

    return op12 == op21
示例#2
0
 def check_two_qubit_weyl_decomposition(self,
                                        target_unitary,
                                        tolerance=1.e-7):
     """Check TwoQubitWeylDecomposition() works for a given operator"""
     with self.subTest(unitary=target_unitary):
         decomp = TwoQubitWeylDecomposition(target_unitary)
         op = Operator(np.eye(4))
         for u, qs in (
             (decomp.K2r, [0]),
             (decomp.K2l, [1]),
             (Ud(decomp.a, decomp.b, decomp.c), [0, 1]),
             (decomp.K1r, [0]),
             (decomp.K1l, [1]),
         ):
             op = op.compose(u, qs)
         decomp_unitary = op.data
         target_unitary *= la.det(target_unitary)**(-0.25)
         decomp_unitary *= la.det(decomp_unitary)**(-0.25)
         maxdists = [
             np.max(np.abs(target_unitary + phase * decomp_unitary))
             for phase in [1, 1j, -1, -1j]
         ]
         maxdist = np.min(maxdists)
         self.assertTrue(
             np.abs(maxdist) < tolerance,
             "Worst distance {}".format(maxdist))
示例#3
0
def _commute(node1, node2):

    if node1.type != "op" or node2.type != "op":
        return False

    if any([
            nd.name in {"barrier", "snapshot", "measure", "reset", "copy"}
            for nd in [node1, node2]
    ]):
        return False

    if node1.condition or node2.condition:
        return False

    if node1.op.is_parameterized() or node2.op.is_parameterized():
        return False

    qarg = list(set(node1.qargs + node2.qargs))
    qbit_num = len(qarg)

    qarg1 = [qarg.index(q) for q in node1.qargs]
    qarg2 = [qarg.index(q) for q in node2.qargs]

    id_op = Operator(np.eye(2**qbit_num))

    op12 = id_op.compose(node1.op, qargs=qarg1).compose(node2.op, qargs=qarg2)
    op21 = id_op.compose(node2.op, qargs=qarg2).compose(node1.op, qargs=qarg1)

    if_commute = (op12 == op21)

    return if_commute
示例#4
0
def _commute(node1, node2, cache):

    if node1.type != "op" or node2.type != "op":
        return False

    if any([
            nd.name
            in {"barrier", "snapshot", "measure", "reset", "copy", "delay"}
            for nd in [node1, node2]
    ]):
        return False

    if node1.condition or node2.condition:
        return False

    if node1.op.is_parameterized() or node2.op.is_parameterized():
        return False

    qarg = list(set(node1.qargs + node2.qargs))
    qbit_num = len(qarg)

    qarg1 = [qarg.index(q) for q in node1.qargs]
    qarg2 = [qarg.index(q) for q in node2.qargs]

    id_op = Operator(np.eye(2**qbit_num))

    node1_key = (node1.op.name, str(node1.op.params), str(qarg1))
    node2_key = (node2.op.name, str(node2.op.params), str(qarg2))
    if (node1_key, node2_key) in cache:
        op12 = cache[(node1_key, node2_key)]
    else:
        op12 = id_op.compose(node1.op, qargs=qarg1).compose(node2.op,
                                                            qargs=qarg2)
        cache[(node1_key, node2_key)] = op12
    if (node2_key, node1_key) in cache:
        op21 = cache[(node2_key, node1_key)]
    else:
        op21 = id_op.compose(node2.op, qargs=qarg2).compose(node1.op,
                                                            qargs=qarg1)
        cache[(node2_key, node1_key)] = op21

    if_commute = (op12 == op21)

    return if_commute
def _commute(node1, node2, cache):

    if not isinstance(node1, DAGOpNode) or not isinstance(node2, DAGOpNode):
        return False

    for nd in [node1, node2]:
        if nd.op._directive or nd.name in {"measure", "reset", "delay"}:
            return False

    if node1.op.condition or node2.op.condition:
        return False

    if node1.op.is_parameterized() or node2.op.is_parameterized():
        return False

    qarg = list(set(node1.qargs + node2.qargs))
    qbit_num = len(qarg)

    qarg1 = [qarg.index(q) for q in node1.qargs]
    qarg2 = [qarg.index(q) for q in node2.qargs]

    id_op = Operator(np.eye(2**qbit_num))

    node1_key = (node1.op.name, str(node1.op.params), str(qarg1))
    node2_key = (node2.op.name, str(node2.op.params), str(qarg2))
    if (node1_key, node2_key) in cache:
        op12 = cache[(node1_key, node2_key)]
    else:
        op12 = id_op.compose(node1.op, qargs=qarg1).compose(node2.op,
                                                            qargs=qarg2)
        cache[(node1_key, node2_key)] = op12
    if (node2_key, node1_key) in cache:
        op21 = cache[(node2_key, node1_key)]
    else:
        op21 = id_op.compose(node2.op, qargs=qarg2).compose(node1.op,
                                                            qargs=qarg1)
        cache[(node2_key, node1_key)] = op21

    if_commute = op12 == op21

    return if_commute
示例#6
0
def _commute(node1, node2):
    """Function to verify commutation relation between two nodes in the DAG

    Args:
        node1 (DAGnode): first node operation (attribute ['operation'] in the DAG)
        node2 (DAGnode): second node operation

    Return:
        bool: True if the gates commute and false if it is not the case.
    """

    # Create set of qubits on which the operation acts
    qarg1 = [node1.qargs[i].index for i in range(0, len(node1.qargs))]
    qarg2 = [node2.qargs[i].index for i in range(0, len(node2.qargs))]

    # Create set of cbits on which the operation acts
    carg1 = [node1.qargs[i].index for i in range(0, len(node1.cargs))]
    carg2 = [node2.qargs[i].index for i in range(0, len(node2.cargs))]

    # Commutation for classical conditional gates
    if node1.condition or node2.condition:
        intersection = set(qarg1).intersection(set(qarg2))
        if intersection or carg1 or carg2:
            commute_condition = False
        else:
            commute_condition = True
        return commute_condition

    # Commutation for measurement
    if node1.name == 'measure' or node2.name == 'measure':
        intersection_q = set(qarg1).intersection(set(qarg2))
        intersection_c = set(carg1).intersection(set(carg2))
        if intersection_q or intersection_c:
            commute_measurement = False
        else:
            commute_measurement = True
        return commute_measurement

    # Commutation for barrier-like directives
    directives = ['barrier', 'snapshot']
    if node1.name in directives or node2.name in directives:
        intersection = set(qarg1).intersection(set(qarg2))
        if intersection:
            commute_directive = False
        else:
            commute_directive = True
        return commute_directive

    # List of non commuting gates (TO DO: add more elements)
    non_commute_list = [set(['x', 'y']), set(['x', 'z'])]

    if qarg1 == qarg2 and (set([node1.name, node2.name]) in non_commute_list):
        return False

    # Create matrices to check commutation relation if no other criteria are matched
    qarg = list(set(node1.qargs + node2.qargs))
    qbit_num = len(qarg)

    qarg1 = [qarg.index(q) for q in node1.qargs]
    qarg2 = [qarg.index(q) for q in node2.qargs]

    id_op = Operator(np.eye(2**qbit_num))

    op12 = id_op.compose(node1.op, qargs=qarg1).compose(node2.op, qargs=qarg2)
    op21 = id_op.compose(node2.op, qargs=qarg2).compose(node1.op, qargs=qarg1)

    if_commute = (op12 == op21)

    return if_commute
def _commute(node1, node2, cache):
    if not isinstance(node1, DAGOpNode) or not isinstance(node2, DAGOpNode):
        return False
    for nd in [node1, node2]:
        if nd.op._directive or nd.name in {"measure", "reset", "delay"}:
            return False
    if node1.op.condition or node2.op.condition:
        return False
    if node1.op.is_parameterized() or node2.op.is_parameterized():
        return False

    # Assign indices to each of the qubits such that all `node1`'s qubits come first, followed by
    # any _additional_ qubits `node2` addresses.  This helps later when we need to compose one
    # operator with the other, since we can easily expand `node1` with a suitable identity.
    qarg = {q: i for i, q in enumerate(node1.qargs)}
    num_qubits = len(qarg)
    for q in node2.qargs:
        if q not in qarg:
            qarg[q] = num_qubits
            num_qubits += 1
    qarg1 = tuple(qarg[q] for q in node1.qargs)
    qarg2 = tuple(qarg[q] for q in node2.qargs)

    node1_key = (node1.op.name, _hashable_parameters(node1.op.params), qarg1)
    node2_key = (node2.op.name, _hashable_parameters(node2.op.params), qarg2)
    try:
        # We only need to try one orientation of the keys, since if we've seen the compound key
        # before, we've set it in both orientations.
        return cache[node1_key, node2_key]
    except KeyError:
        pass

    operator_1 = Operator(node1.op,
                          input_dims=(2, ) * len(qarg1),
                          output_dims=(2, ) * len(qarg1))
    operator_2 = Operator(node2.op,
                          input_dims=(2, ) * len(qarg2),
                          output_dims=(2, ) * len(qarg2))

    if qarg1 == qarg2:
        # Use full composition if possible to get the fastest matmul paths.
        op12 = operator_1.compose(operator_2)
        op21 = operator_2.compose(operator_1)
    else:
        # Expand operator_1 to be large enough to contain operator_2 as well; this relies on qargs1
        # being the lowest possible indices so the identity can be tensored before it.
        extra_qarg2 = num_qubits - len(qarg1)
        if extra_qarg2:
            try:
                id_op = _COMMUTE_ID_OP[extra_qarg2]
            except KeyError:
                id_op = _COMMUTE_ID_OP[extra_qarg2] = Operator(
                    np.eye(2**extra_qarg2),
                    input_dims=(2, ) * extra_qarg2,
                    output_dims=(2, ) * extra_qarg2,
                )
            operator_1 = id_op.tensor(operator_1)
        op12 = operator_1.compose(operator_2, qargs=qarg2, front=False)
        op21 = operator_1.compose(operator_2, qargs=qarg2, front=True)
    cache[node1_key, node2_key] = cache[node2_key,
                                        node1_key] = ret = op12 == op21
    return ret
    def commute(self, op1: Operation, qargs1: List, cargs1: List,
                op2: Operation, qargs2: List, cargs2: List):
        """
        Checks if two Operations commute.

        Args:
            op1: first operation.
            qargs1: first operation's qubits.
            cargs1: first operation's clbits.
            op2: second operation.
            qargs2: second operation's qubits.
            cargs2: second operation's clbits.

        Returns:
            bool: whether two operations commute.
        """
        # We don't support commutation of conditional gates for now due to bugs in
        # CommutativeCancellation.  See gh-8553.
        if getattr(op1, "condition") is not None or getattr(
                op2, "condition") is not None:
            return False

        # These lines are adapted from dag_dependency and say that two gates over
        # different quantum and classical bits necessarily commute. This is more
        # permissive that the check from commutation_analysis, as for example it
        # allows to commute X(1) and Measure(0, 0).
        # Presumably this check was not present in commutation_analysis as
        # it was only called on pairs of connected nodes from DagCircuit.
        intersection_q = set(qargs1).intersection(set(qargs2))
        intersection_c = set(cargs1).intersection(set(cargs2))
        if not (intersection_q or intersection_c):
            return True

        # These lines are adapted from commutation_analysis, which is more restrictive than the
        # check from dag_dependency when considering nodes with "_directive".  It would be nice to
        # think which optimizations from dag_dependency can indeed be used.
        for op in [op1, op2]:
            if (getattr(op, "_directive", False)
                    or op.name in {"measure", "reset", "delay"}
                    or op.is_parameterized()):
                return False

        # The main code is adapted from commutative analysis.
        # Assign indices to each of the qubits such that all `node1`'s qubits come first, followed by
        # any _additional_ qubits `node2` addresses.  This helps later when we need to compose one
        # operator with the other, since we can easily expand `node1` with a suitable identity.
        qarg = {q: i for i, q in enumerate(qargs1)}
        num_qubits = len(qarg)
        for q in qargs2:
            if q not in qarg:
                qarg[q] = num_qubits
                num_qubits += 1
        qarg1 = tuple(qarg[q] for q in qargs1)
        qarg2 = tuple(qarg[q] for q in qargs2)

        node1_key = (op1.name, self._hashable_parameters(op1.params), qarg1)
        node2_key = (op2.name, self._hashable_parameters(op2.params), qarg2)
        try:
            # We only need to try one orientation of the keys, since if we've seen the compound key
            # before, we've set it in both orientations.
            return self.cache[node1_key, node2_key]
        except KeyError:
            pass

        operator_1 = Operator(op1,
                              input_dims=(2, ) * len(qarg1),
                              output_dims=(2, ) * len(qarg1))
        operator_2 = Operator(op2,
                              input_dims=(2, ) * len(qarg2),
                              output_dims=(2, ) * len(qarg2))

        if qarg1 == qarg2:
            # Use full composition if possible to get the fastest matmul paths.
            op12 = operator_1.compose(operator_2)
            op21 = operator_2.compose(operator_1)
        else:
            # Expand operator_1 to be large enough to contain operator_2 as well; this relies on qargs1
            # being the lowest possible indices so the identity can be tensored before it.
            extra_qarg2 = num_qubits - len(qarg1)
            if extra_qarg2:
                id_op = _identity_op(2**extra_qarg2)
                operator_1 = id_op.tensor(operator_1)
            op12 = operator_1.compose(operator_2, qargs=qarg2, front=False)
            op21 = operator_1.compose(operator_2, qargs=qarg2, front=True)
        self.cache[node1_key,
                   node2_key] = self.cache[node2_key,
                                           node1_key] = ret = op12 == op21
        return ret