Ejemplo n.º 1
0
def _potential_cross_whole_w(
        op: ops.Operation, atol: float,
        held_w_phases: Dict[ops.Qid, value.TParamVal]) -> 'cirq.OP_TREE':
    """Grabs or cancels a held W gate against an existing W gate.

    [Where W(a) is shorthand for PhasedX(phase_exponent=a).]

    Uses the following identity:
        ───W(a)───W(b)───
        ≡ ───Z^-a───X───Z^a───Z^-b───X───Z^b───
        ≡ ───Z^-a───Z^-a───Z^b───X───X───Z^b───
        ≡ ───Z^-a───Z^-a───Z^b───Z^b───
        ≡ ───Z^2(b-a)───
    """
    _, phase_exponent = cast(Tuple[value.TParamVal, value.TParamVal],
                             _try_get_known_phased_pauli(op))
    q = op.qubits[0]
    a = held_w_phases.get(q, None)
    b = phase_exponent

    if a is None:
        # Collect the gate.
        held_w_phases[q] = b
    else:
        # Cancel the gate.
        del held_w_phases[q]
        t = 2 * (b - a)
        if not single_qubit_decompositions.is_negligible_turn(t / 2, atol):
            return ops.Z(q)**t
    return []
Ejemplo n.º 2
0
def _potential_cross_whole_w(moment_index: int, op: ops.Operation,
                             tolerance: float, state: _OptimizerState) -> None:
    """Grabs or cancels a held W gate against an existing W gate.

    [Where W(a) is shorthand for PhasedX(phase_exponent=a).]

    Uses the following identity:
        ───W(a)───W(b)───
        ≡ ───Z^-a───X───Z^a───Z^-b───X───Z^b───
        ≡ ───Z^-a───Z^-a───Z^b───X───X───Z^b───
        ≡ ───Z^-a───Z^-a───Z^b───Z^b───
        ≡ ───Z^2(b-a)───
    """
    state.deletions.append((moment_index, op))

    _, phase_exponent = cast(Tuple[value.TParamVal, value.TParamVal],
                             _try_get_known_phased_pauli(op))
    q = op.qubits[0]
    a = state.held_w_phases.get(q, None)
    b = phase_exponent

    if a is None:
        # Collect the gate.
        state.held_w_phases[q] = b
    else:
        # Cancel the gate.
        del state.held_w_phases[q]
        t = 2 * (b - a)
        if not single_qubit_decompositions.is_negligible_turn(
                t / 2, tolerance):
            leftover_phase = ops.Z(q)**t
            state.inline_intos.append((moment_index, leftover_phase))
Ejemplo n.º 3
0
 def dump_tracked_phase(qubits: Iterable[ops.Qid]) -> 'cirq.OP_TREE':
     """Zeroes qubit_phase entries by emitting Z gates."""
     for q in qubits:
         p, key = qubit_phase[q], last_phased_xz_op[q]
         qubit_phase[q] = 0
         if not (key or single_qubit_decompositions.is_negligible_turn(
                 p, atol)):
             yield ops.Z(q)**(p * 2)
         elif key:
             phased_xz_replacements[key] = phased_xz_replacements[
                 key].with_z_exponent(p * 2)
Ejemplo n.º 4
0
    def optimize_circuit(self, circuit: circuits.Circuit):
        state = _OptimizerState()

        for moment_index, moment in enumerate(circuit):
            for op in moment.operations:
                affected = [q for q in op.qubits if q in state.held_w_phases]

                # Collect, phase, and merge Ws.
                w = _try_get_known_phased_pauli(
                    op, no_symbolic=not self.eject_parameterized)
                if w is not None:
                    if single_qubit_decompositions.is_negligible_turn(
                        (w[0] - 1) / 2, self.tolerance):
                        _potential_cross_whole_w(moment_index, op,
                                                 self.tolerance, state)
                    else:
                        _potential_cross_partial_w(moment_index, op, state)
                    continue

                if not affected:
                    continue

                # Absorb Z rotations.
                t = _try_get_known_z_half_turns(
                    op, no_symbolic=not self.eject_parameterized)
                if t is not None:
                    _absorb_z_into_w(moment_index, op, state)
                    continue

                # Dump coherent flips into measurement bit flips.
                if isinstance(op.gate, ops.MeasurementGate):
                    _dump_into_measurement(moment_index, op, state)

                # Cross CZs using kickback.
                if (_try_get_known_cz_half_turns(
                        op, no_symbolic=not self.eject_parameterized)
                        is not None):
                    if len(affected) == 1:
                        _single_cross_over_cz(moment_index, op, affected[0],
                                              state)
                    else:
                        _double_cross_over_cz(op, state)
                    continue

                # Don't know how to handle this situation. Dump the gates.
                _dump_held(op.qubits, moment_index, state)

        # Put anything that's still held at the end of the circuit.
        _dump_held(state.held_w_phases.keys(), len(circuit), state)

        circuit.batch_remove(state.deletions)
        circuit.batch_insert_into(state.inline_intos)
        circuit.batch_insert(state.insertions)
Ejemplo n.º 5
0
    def map_func(op: 'cirq.Operation', moment_index: int) -> 'cirq.OP_TREE':
        last_phased_xz_op.update({q: None for q in op.qubits})

        if tags_to_ignore & set(op.tags):
            # Op marked with no-compile, dump phases and do not cross.
            return [dump_tracked_phase(op.qubits), op]

        gate = op.gate
        # Return if circuit operation.
        if gate is None:
            return [dump_tracked_phase(op.qubits), op]

        # Swap phases if `op` is a swap operation.
        if _is_swaplike(gate):
            a, b = op.qubits
            qubit_phase[a], qubit_phase[b] = qubit_phase[b], qubit_phase[a]
            return op

        # Z gate before measurement is a no-op. Drop tracked phase.
        if isinstance(gate, ops.MeasurementGate):
            for q in op.qubits:
                qubit_phase[q] = 0
            return op

        # Move Z gates into tracked qubit phases.
        if isinstance(gate, ops.ZPowGate) and (
                eject_parameterized or not protocols.is_parameterized(gate)):
            qubit_phase[op.qubits[0]] += gate.exponent / 2
            return []

        # Try to move the tracked phases over the operation via protocols.phase_by(op)
        phased_op = op
        for i, p in enumerate([qubit_phase[q] for q in op.qubits]):
            if not single_qubit_decompositions.is_negligible_turn(p, atol):
                phased_op = protocols.phase_by(phased_op, -p, i, default=None)
        if phased_op is None:
            return [dump_tracked_phase(op.qubits), op]

        gate = phased_op.gate
        if isinstance(gate, ops.PhasedXZGate) and (
                eject_parameterized
                or not protocols.is_parameterized(gate.z_exponent)):
            qubit = phased_op.qubits[0]
            qubit_phase[qubit] += gate.z_exponent / 2
            gate = gate.with_z_exponent(0)
            phased_op = gate.on(qubit)
            phased_xz_replacements[moment_index, phased_op] = gate
            last_phased_xz_op[qubit] = (moment_index, phased_op)
        return phased_op
Ejemplo n.º 6
0
 def dump_tracked_phase(qubits: Iterable[ops.Qid], index: int) -> None:
     """Zeroes qubit_phase entries by emitting Z gates."""
     for q in qubits:
         p = qubit_phase[q]
         qubit_phase[q] = 0
         if single_qubit_decompositions.is_negligible_turn(p, self.tolerance):
             continue
         dumped = False
         moment_index = circuit.prev_moment_operating_on([q], index)
         if moment_index is not None:
             op = circuit.moments[moment_index][q]
             if op and isinstance(op.gate, ops.PhasedXZGate):
                 # Attach z-rotation to replacing PhasedXZ gate.
                 idx = phased_xz_replacements[moment_index, q]
                 _, _, repl_op = replacements[idx]
                 gate = cast(ops.PhasedXZGate, repl_op.gate)
                 repl_op = gate.with_z_exponent(p * 2).on(q)
                 replacements[idx] = (moment_index, op, repl_op)
                 dumped = True
         if not dumped:
             # Add a new Z gate
             dump_op = ops.Z(q) ** (p * 2)
             insertions.append((index, dump_op))
Ejemplo n.º 7
0
    def map_func(op: 'cirq.Operation', _: int) -> 'cirq.OP_TREE':
        # Dump if `op` marked with a no compile tag.
        if set(op.tags) & tags_to_ignore:
            return [_dump_held(op.qubits, held_w_phases), op]

        # Collect, phase, and merge Ws.
        w = _try_get_known_phased_pauli(op, no_symbolic=not eject_parameterized)
        if w is not None:
            return (
                _potential_cross_whole_w(op, atol, held_w_phases)
                if single_qubit_decompositions.is_negligible_turn((w[0] - 1) / 2, atol)
                else _potential_cross_partial_w(op, held_w_phases)
            )

        affected = [q for q in op.qubits if q in held_w_phases]
        if not affected:
            return op

        # Absorb Z rotations.
        t = _try_get_known_z_half_turns(op, no_symbolic=not eject_parameterized)
        if t is not None:
            return _absorb_z_into_w(op, held_w_phases)

        # Dump coherent flips into measurement bit flips.
        if isinstance(op.gate, ops.MeasurementGate):
            return _dump_into_measurement(op, held_w_phases)

        # Cross CZs using kickback.
        if _try_get_known_cz_half_turns(op, no_symbolic=not eject_parameterized) is not None:
            return (
                _single_cross_over_cz(op, affected[0])
                if len(affected) == 1
                else _double_cross_over_cz(op, held_w_phases)
            )

        # Don't know how to handle this situation. Dump the gates.
        return [_dump_held(op.qubits, held_w_phases), op]
Ejemplo n.º 8
0
    def optimize_circuit(self, circuit: circuits.Circuit):
        # Tracks qubit phases (in half turns; multiply by pi to get radians).
        qubit_phase: Dict[ops.Qid, float] = defaultdict(lambda: 0)
        deletions: List[Tuple[int, ops.Operation]] = []
        replacements: List[Tuple[int, ops.Operation, ops.Operation]] = []
        insertions: List[Tuple[int, ops.Operation]] = []
        phased_xz_replacements: Dict[Tuple[int, ops.Qid], int] = {}

        def dump_tracked_phase(qubits: Iterable[ops.Qid], index: int) -> None:
            """Zeroes qubit_phase entries by emitting Z gates."""
            for q in qubits:
                p = qubit_phase[q]
                qubit_phase[q] = 0
                if single_qubit_decompositions.is_negligible_turn(p, self.tolerance):
                    continue
                dumped = False
                moment_index = circuit.prev_moment_operating_on([q], index)
                if moment_index is not None:
                    op = circuit.moments[moment_index][q]
                    if op and isinstance(op.gate, ops.PhasedXZGate):
                        # Attach z-rotation to replacing PhasedXZ gate.
                        idx = phased_xz_replacements[moment_index, q]
                        _, _, repl_op = replacements[idx]
                        gate = cast(ops.PhasedXZGate, repl_op.gate)
                        repl_op = gate.with_z_exponent(p * 2).on(q)
                        replacements[idx] = (moment_index, op, repl_op)
                        dumped = True
                if not dumped:
                    # Add a new Z gate
                    dump_op = ops.Z(q) ** (p * 2)
                    insertions.append((index, dump_op))

        for moment_index, moment in enumerate(circuit):
            for op in moment.operations:
                # Move Z gates into tracked qubit phases.
                h = _try_get_known_z_half_turns(op, self.eject_parameterized)
                if h is not None:
                    q = op.qubits[0]
                    qubit_phase[q] += h / 2
                    deletions.append((moment_index, op))
                    continue

                # Z gate before measurement is a no-op. Drop tracked phase.
                if isinstance(op.gate, ops.MeasurementGate):
                    for q in op.qubits:
                        qubit_phase[q] = 0

                # If there's no tracked phase, we can move on.
                phases = [qubit_phase[q] for q in op.qubits]
                if not isinstance(op.gate, ops.PhasedXZGate) and all(
                    single_qubit_decompositions.is_negligible_turn(p, self.tolerance)
                    for p in phases
                ):
                    continue

                if _is_swaplike(op):
                    a, b = op.qubits
                    qubit_phase[a], qubit_phase[b] = qubit_phase[b], qubit_phase[a]
                    continue

                # Try to move the tracked phasing over the operation.
                phased_op = op
                for i, p in enumerate(phases):
                    if not single_qubit_decompositions.is_negligible_turn(p, self.tolerance):
                        phased_op = protocols.phase_by(phased_op, -p, i, default=None)
                if phased_op is not None:
                    gate = phased_op.gate
                    if isinstance(gate, ops.PhasedXZGate) and (
                        self.eject_parameterized or not protocols.is_parameterized(gate.z_exponent)
                    ):
                        qubit = phased_op.qubits[0]
                        qubit_phase[qubit] += gate.z_exponent / 2
                        phased_op = gate.with_z_exponent(0).on(qubit)
                        repl_idx = len(replacements)
                        phased_xz_replacements[moment_index, qubit] = repl_idx
                    replacements.append((moment_index, op, phased_op))
                else:
                    dump_tracked_phase(op.qubits, moment_index)

        dump_tracked_phase(qubit_phase.keys(), len(circuit))
        circuit.batch_remove(deletions)
        circuit.batch_replace(replacements)
        circuit.batch_insert(insertions)