예제 #1
0
def unroll_circuit_op_greedy_frontier(
    circuit: CIRCUIT_TYPE, *,
    tags_to_check=(MAPPED_CIRCUIT_OP_TAG, )) -> CIRCUIT_TYPE:
    """Unrolls (tagged) `cirq.CircuitOperation`s by inserting operations inline at qubit frontier.

    Each matching `cirq.CircuitOperation` is replaced by inserting underlying operations using the
    `circuit.insert_at_frontier` method. The greedy approach attempts to reuse any available space
    in existing moments on the right of circuit_op before inserting new moments.

    Args:
        circuit: Input circuit to apply the transformations on. The input circuit is not mutated.
        tags_to_check: If specified, only circuit operations tagged with one of the `tags_to_check`
            are unrolled.

    Returns:
        Copy of input circuit with (Tagged) CircuitOperation's expanded inline at qubit frontier.
    """
    unrolled_circuit = circuit.unfreeze(copy=True)
    frontier: Dict['cirq.Qid', int] = defaultdict(lambda: 0)
    for idx, op in circuit.findall_operations(
            lambda op: _check_circuit_op(op, tags_to_check)):
        idx = max(idx, max(frontier[q] for q in op.qubits))
        unrolled_circuit.clear_operations_touching(op.qubits, [idx])
        frontier = unrolled_circuit.insert_at_frontier(
            protocols.decompose_once(op), idx, frontier)
    return _to_target_circuit_type(unrolled_circuit, circuit)
예제 #2
0
def unroll_circuit_op_greedy_earliest(
    circuit: CIRCUIT_TYPE, *,
    tags_to_check=(MAPPED_CIRCUIT_OP_TAG, )) -> CIRCUIT_TYPE:
    """Unrolls (tagged) `cirq.CircuitOperation`s by inserting operations using EARLIEST strategy.

    Each matching `cirq.CircuitOperation` is replaced by inserting underlying operations using the
    `cirq.InsertStrategy.EARLIEST` strategy. The greedy approach attempts to minimize circuit depth
    of the resulting circuit.

    Args:
        circuit: Input circuit to apply the transformations on. The input circuit is not mutated.
        tags_to_check: If specified, only circuit operations tagged with one of the `tags_to_check`
            are unrolled.

    Returns:
        Copy of input circuit with (Tagged) CircuitOperation's expanded using EARLIEST strategy.
    """
    batch_removals = [
        *circuit.findall_operations(
            lambda op: _check_circuit_op(op, tags_to_check))
    ]
    batch_inserts = [(i, protocols.decompose_once(op))
                     for i, op in batch_removals]
    unrolled_circuit = circuit.unfreeze(copy=True)
    unrolled_circuit.batch_remove(batch_removals)
    unrolled_circuit.batch_insert(batch_inserts)
    return _to_target_circuit_type(unrolled_circuit, circuit)
예제 #3
0
def assert_decompose_is_consistent_with_unitary(
        val: Any, ignoring_global_phase: bool = False):
    """Uses `val._unitary_` to check `val._phase_by_`'s behavior."""

    expected = protocols.unitary(val, None)
    if expected is None:
        # If there's no unitary, it's vacuously consistent.
        return
    qubit_count = len(expected).bit_length() - 1
    if isinstance(val, ops.Operation):
        qubits = val.qubits
        dec = protocols.decompose_once(val, default=None)
    else:
        qubits = tuple(line.LineQubit.range(qubit_count))
        dec = protocols.decompose_once_with_qubits(val, qubits, default=None)
    if dec is None:
        # If there's no decomposition, it's vacuously consistent.
        return

    actual = circuits.Circuit.from_ops(dec).unitary(qubit_order=qubits)

    if ignoring_global_phase:
        lin_alg_utils.assert_allclose_up_to_global_phase(actual,
                                                         expected,
                                                         atol=1e-8)
    else:
        # coverage: ignore
        np.testing.assert_allclose(actual, expected, atol=1e-8)
예제 #4
0
 def defer(op: 'cirq.Operation', _) -> 'cirq.OP_TREE':
     if op in terminal_measurements:
         return op
     gate = op.gate
     if isinstance(gate, ops.MeasurementGate):
         key = value.MeasurementKey.parse_serialized(gate.key)
         targets = [_MeasurementQid(key, q) for q in op.qubits]
         measurement_qubits[key] = targets
         cxs = [ops.CX(q, target) for q, target in zip(op.qubits, targets)]
         xs = [ops.X(targets[i]) for i, b in enumerate(gate.full_invert_mask()) if b]
         return cxs + xs
     elif protocols.is_measurement(op):
         return [defer(op, None) for op in protocols.decompose_once(op)]
     elif op.classical_controls:
         controls = []
         for c in op.classical_controls:
             if isinstance(c, value.KeyCondition):
                 if c.key not in measurement_qubits:
                     raise ValueError(f'Deferred measurement for key={c.key} not found.')
                 qubits = measurement_qubits[c.key]
                 if len(qubits) != 1:
                     # TODO: Multi-qubit conditions require
                     # https://github.com/quantumlib/Cirq/issues/4512
                     # Remember to update docstring above once this works.
                     raise ValueError('Only single qubit conditions are allowed.')
                 controls.extend(qubits)
             else:
                 raise ValueError('Only KeyConditions are allowed.')
         return op.without_classical_controls().controlled_by(
             *controls, control_values=[tuple(range(1, q.dimension)) for q in controls]
         )
     return op
예제 #5
0
def _decompose_into_cliffords(op: 'cirq.Operation') -> List['cirq.Operation']:
    # An operation that can be ignored?
    if isinstance(op, global_phase_op.GlobalPhaseOperation):
        return []

    # Already a known Clifford?
    if isinstance(op.gate, (clifford_gate.SingleQubitCliffordGate,
                            pauli_interaction_gate.PauliInteractionGate)):
        return [op]

    # Specifies a decomposition into Cliffords?
    v = getattr(op, '_decompose_into_clifford_', None)
    if v is not None:
        result = v()
        if result is not None and result is not NotImplemented:
            return list(op_tree.flatten_to_ops(result))

    # Specifies a decomposition that happens to contain only Cliffords?
    decomposed = protocols.decompose_once(op, None)
    if decomposed is not None:
        return [
            out for sub_op in decomposed
            for out in _decompose_into_cliffords(sub_op)
        ]

    raise TypeError(f'Operation is not a known Clifford and did not decompose '
                    f'into known Cliffords: {op!r}')
예제 #6
0
    def _convert_one(self, op: ops.Operation) -> ops.OP_TREE:
        # Check if this is a CZ
        # Only keep partial CZ gates if allow_partial_czs
        if (isinstance(op, ops.GateOperation)
                and isinstance(op.gate, ops.CZPowGate)
                and (self.allow_partial_czs or op.gate.exponent == 1)):
            return op

        # Measurement?
        if ops.MeasurementGate.is_measurement(op):
            return op

        # Known matrix?
        mat = protocols.unitary(op, None)
        if mat is not None and len(op.qubits) == 1:
            return op
        if mat is not None and len(op.qubits) == 2:
            return decompositions.two_qubit_matrix_to_operations(
                op.qubits[0], op.qubits[1], mat, allow_partial_czs=False)

        # Provides a decomposition?
        decomposed = protocols.decompose_once(op, None)
        if decomposed is not None:
            return decomposed

        # Just let it be?
        if self.ignore_failures:
            return op

        raise TypeError("Don't know how to work with {!r}. "
                        "It isn't composite or an operation with a "
                        "known unitary effect on 1 or 2 qubits.".format(op))
def assert_decompose_is_consistent_with_unitary(
        val: Any, ignoring_global_phase: bool = False):
    """Uses `val._unitary_` to check `val._phase_by_`'s behavior."""
    # pylint: disable=unused-variable
    __tracebackhide__ = True
    # pylint: enable=unused-variable

    expected = protocols.unitary(val, None)
    if expected is None:
        # If there's no unitary, it's vacuously consistent.
        return
    if isinstance(val, ops.Operation):
        qubits = val.qubits
        dec = protocols.decompose_once(val, default=None)
    else:
        qubits = tuple(devices.LineQid.for_gate(val))
        dec = protocols.decompose_once_with_qubits(val, qubits, default=None)
    if dec is None:
        # If there's no decomposition, it's vacuously consistent.
        return

    actual = circuits.Circuit(dec).unitary(qubit_order=qubits)

    if ignoring_global_phase:
        lin_alg_utils.assert_allclose_up_to_global_phase(actual,
                                                         expected,
                                                         atol=1e-8)
    else:
        # coverage: ignore
        np.testing.assert_allclose(actual, expected, atol=1e-8)
예제 #8
0
    def _convert_one(self, op: ops.Operation) -> ops.OP_TREE:
        # Don't change if it's already a SingleQubitCliffordGate
        if (isinstance(op, ops.GateOperation)
                and isinstance(op.gate, ops.SingleQubitCliffordGate)):
            return op

        # Single qubit gate with known matrix?
        if len(op.qubits) == 1:
            mat = protocols.unitary(op, None)
            if mat is not None:
                cliff_op = self._matrix_to_clifford_op(mat, op.qubits[0])
                if cliff_op is not None:
                    return cliff_op
                if self.ignore_failures:
                    return op
                raise ValueError('Single qubit operation is not in the '
                                 'Clifford group: {!r}'.format(op))

        # Provides a decomposition?
        decomposed = protocols.decompose_once(op, None)
        if decomposed is not None:
            return decomposed

        # Just let it be?
        if self.ignore_failures:
            return op

        raise TypeError("Don't know how to work with {!r}. "
                        "It isn't composite or a 1-qubit operation "
                        "with a known unitary effect.".format(op))
예제 #9
0
    def _decompose_(self):
        result = protocols.decompose_once(self.sub_operation, NotImplemented)
        if result is NotImplemented:
            return NotImplemented

        return [
            ControlledOperation(self.controls, op, self.control_values)
            for op in result
        ]
예제 #10
0
    def _decompose_(self):
        result = protocols.decompose_once(self._sub_operation, NotImplemented)
        if result is NotImplemented:
            return NotImplemented

        return [
            ClassicallyControlledOperation(op, self._conditions)
            for op in result
        ]
예제 #11
0
    def _decompose(self, op: ops.Operation) -> ops.OP_TREE:
        """Recursively decompose composite gates into an OP_TREE of gates."""
        skip = self.no_decomp(op)
        if skip and (skip is not NotImplemented):
            return op

        decomposed = protocols.decompose_once(op, None)
        if decomposed is None:
            return op
        return (self._decompose(op) for op in decomposed)
예제 #12
0
    def _write_operations(self,
                          op_tree: ops.OP_TREE,
                          output: Callable[[str], None],
                          output_line_gap: Callable[[int], None],
                          top=True) -> None:
        for op in ops.flatten_op_tree(op_tree):
            out = protocols.qasm(op, args=self.args, default=None)
            if out is not None:
                output(out)
                continue

            if isinstance(op, ops.GateOperation):
                comment = 'Gate: {!s}'.format(op.gate)
            else:
                comment = 'Operation: {!s}'.format(op)

            decomp = protocols.decompose_once(op, None)
            if decomp is not None:
                if top:
                    output_line_gap(1)
                    output('// {}\n'.format(comment))
                self._write_operations(decomp,
                                       output,
                                       output_line_gap,
                                       top=False)
                if top:
                    output_line_gap(1)
                continue

            mat = protocols.unitary(op, None) if len(op.qubits) <= 2 else None
            if mat is not None and len(op.qubits) == 1:
                u_op = QasmUGate.from_matrix(mat).on(*op.qubits)
                if top:
                    output_line_gap(1)
                    output('// {}\n'.format(comment))
                output(cast(str, protocols.qasm(u_op, args=self.args)))
                if top:
                    output_line_gap(1)
                continue

            if mat is not None and len(op.qubits) == 2:
                u_op = QasmTwoQubitGate.from_matrix(mat).on(*op.qubits)
                if top:
                    output_line_gap(1)
                    output('// {}\n'.format(comment))
                self._write_operations((u_op,),
                                       output,
                                       output_line_gap,
                                       top=False)
                if top:
                    output_line_gap(1)
                continue

            raise ValueError('Cannot output operation as QASM: {!r}'.format(op))
예제 #13
0
def assert_decompose_ends_at_default_gateset(val: Any):
    """Asserts that cirq.decompose(val) ends at default cirq gateset or a known gate."""
    if _known_gate_with_no_decomposition(val):
        return  # coverage: ignore
    args = () if isinstance(val, ops.Operation) else (tuple(
        devices.LineQid.for_gate(val)), )
    dec_once = protocols.decompose_once(val, [val(*args[0]) if args else val],
                                        *args)
    for op in [*ops.flatten_to_ops(protocols.decompose(d) for d in dec_once)]:
        assert _known_gate_with_no_decomposition(op.gate) or (
            op in protocols.decompose_protocol.DECOMPOSE_TARGET_GATESET
        ), f'{val} decomposed to {op}, which is not part of default cirq target gateset.'
예제 #14
0
def assert_decompose_is_consistent_with_unitary(val: Any):
    """Uses `val._unitary_` to check `val._phase_by_`'s behavior."""

    expected = protocols.unitary(val)
    qubit_count = len(expected).bit_length() - 1
    if isinstance(val, ops.Operation):
        qubits = val.qubits
        dec = protocols.decompose_once(val)
    else:
        qubits = tuple(line.LineQubit.range(qubit_count))
        dec = protocols.decompose_once_with_qubits(val, qubits)
    actual = circuits.Circuit.from_ops(dec).to_unitary_matrix(
        qubit_order=qubits)

    lin_alg_utils.assert_allclose_up_to_global_phase(actual,
                                                     expected,
                                                     atol=1e-8)
예제 #15
0
    def _decompose_(self):
        result = protocols.decompose_once_with_qubits(self.gate, self.qubits,
                                                      NotImplemented)
        if result is not NotImplemented:
            return result

        if isinstance(self.sub_operation.gate, matrix_gates.MatrixGate):
            # Default decompositions of 2/3 qubit `cirq.MatrixGate` ignores global phase, which is
            # local phase in the controlled variant and hence cannot be ignored.
            return NotImplemented

        result = protocols.decompose_once(self.sub_operation, NotImplemented)
        if result is NotImplemented:
            return NotImplemented

        return [
            op.controlled_by(*self.controls,
                             control_values=self.control_values)
            for op in result
        ]
예제 #16
0
 def _decompose_(self) -> 'cirq.OP_TREE':
     return protocols.decompose_once(self.sub_operation, default=None)
예제 #17
0
def is_an_indecomposable_operation(operation: cirq.GateOperation) -> bool:
    if decompose_once(operation, default=NotImplemented) is not NotImplemented:
        return False

    # TODO: add new decomposers
    return True