Beispiel #1
0
def simplify_circuit(
    circuit: cirq.Circuit,
    *,
    max_iterations: int = 20,
    drop_final_rz: bool = False,
) -> cirq.Circuit:
    """Simplifies and optimizes the given circuit.

    Currently it

    * merges any neighboring gates belonging to the same one-parameter family
    * merges all one-qubit rotations into phased X rotations followed by Z rotations
    * pushes the Z rotations towards the end of the circuit as far as possible
    * drops any empty Moments

    This sequence of optimization passes is repeated until the circuit hits a fixed point,
    or ``max_iterations`` is exceeded.

    Finally, it removes Z rotations that are immediately followed by a Z-basis measurement.

    Args:
        circuit: circuit to simplify
        max_iterations: maximum number of simplification rounds
        drop_final_rz: iff True, drop z rotations that have no successor operations

    Returns:
        simplified circuit
    """
    c = circuit.copy()

    # the optimizers cause the immediate decomposition of any gates they insert into the Circuit
    for _ in range(max_iterations):
        # It seems that Cirq optimizers have no way of communicating
        # if they actually made any changes to the Circuit.
        # See https://github.com/quantumlib/Cirq/issues/3761

        before = c.copy()

        # all mergeable 2-qubit gates are merged
        MergeOneParameterGroupGates().optimize_circuit(c)
        c = cirq.merge_single_qubit_gates_to_phased_x_and_z(c)
        # all z rotations are pushed past the first two-qubit gate following them
        c = cirq.eject_z(c, eject_parameterized=True)
        c = cirq.drop_empty_moments(c)

        if c == before:
            # the optimization hit a fixed point
            break

    DropRZBeforeMeasurement(drop_final=drop_final_rz).optimize_circuit(c)
    c = cirq.drop_empty_moments(c)

    return c
Beispiel #2
0
def test_merge_1q_unitaries():
    q, q2 = cirq.LineQubit.range(2)
    # 1. Combines trivial 1q sequence.
    c = cirq.Circuit(cirq.X(q)**0.5, cirq.Z(q)**0.5, cirq.X(q)**-0.5)
    c = cirq.merge_k_qubit_unitaries(c, k=1)
    op_list = [*c.all_operations()]
    assert len(op_list) == 1
    assert isinstance(op_list[0].gate, cirq.MatrixGate)
    cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary(c),
                                                    cirq.unitary(cirq.Y**0.5),
                                                    atol=1e-7)

    # 2. Gets blocked at a 2q operation.
    c = cirq.Circuit([
        cirq.Z(q),
        cirq.H(q),
        cirq.X(q),
        cirq.H(q),
        cirq.CZ(q, q2),
        cirq.H(q)
    ])
    c = cirq.drop_empty_moments(cirq.merge_k_qubit_unitaries(c, k=1))
    assert len(c) == 3
    cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary(c[0]),
                                                    np.eye(2),
                                                    atol=1e-7)
    assert isinstance(c[-1][q].gate, cirq.MatrixGate)
Beispiel #3
0
def test_rewrite():
    q0 = cirq.LineQubit(0)
    q1 = cirq.LineQubit(1)
    circuit = cirq.Circuit(
        cirq.X(q0),
        cirq.X(q1),
        cirq.Y(q0),
        cirq.CZ(q0, q1),
        cirq.Y(q1),
    )
    with cirq.testing.assert_deprecated("Use cirq.merge_k_qubit_unitaries",
                                        deadline='v1.0'):
        cirq.MergeSingleQubitGates(rewriter=lambda ops: cirq.H(ops[0].qubits[
            0])).optimize_circuit(circuit)
    circuit = cirq.drop_empty_moments(circuit)

    cirq.testing.assert_same_circuits(
        circuit,
        cirq.Circuit(
            cirq.H(q0),
            cirq.H(q1),
            cirq.CZ(q0, q1),
            cirq.H(q1),
        ),
    )
def test_merge_2q_unitaries_to_circuit_op():
    c_orig = _create_circuit_to_merge()
    c_orig[-1] = c_orig[-1].with_operations(cirq.measure(cirq.LineQubit(2)))
    cirq.testing.assert_has_diagram(
        c_orig,
        '''
0: ───H───@───@───H───@───X───────@───────X───X['ignore']───@───
          │   │       │           │                         │
1: ───H───┼───X───────@───────Y───X───@───────Y─────────────X───
          │                           │
2: ───H───X───────────────────────────X─────────────────────M───
''',
    )

    c_new = cirq.merge_k_qubit_unitaries_to_circuit_op(
        c_orig, k=2, merged_circuit_op_tag="merged", tags_to_ignore=["ignore"]
    )
    cirq.testing.assert_has_diagram(
        cirq.drop_empty_moments(c_new),
        '''
      [ 0: ───H───@─── ]             [ 0: ───────@───H───@───X───@───X─── ]
0: ───[           │    ]─────────────[           │       │       │        ]────────────────────────────────────────────X['ignore']───@───
      [ 2: ───H───X─── ]['merged']   [ 1: ───H───X───────@───Y───X─────── ]['merged']                                                │
      │                              │                                                                                               │
      │                              │                                                  [ 1: ───@───Y─── ]                           │
1: ───┼──────────────────────────────#2─────────────────────────────────────────────────[       │        ]───────────────────────────X───
      │                                                                                 [ 2: ───X─────── ]['merged']
      │                                                                                 │
2: ───#2────────────────────────────────────────────────────────────────────────────────#2───────────────────────────────────────────M───''',
    )
Beispiel #5
0
def test_post_clean_up():
    class Marker(cirq.testing.TwoQubitGate):
        pass

    a, b = cirq.LineQubit.range(2)
    c_orig = cirq.Circuit(
        cirq.CZ(a, b),
        cirq.CZ(a, b),
        cirq.CZ(a, b),
        cirq.CZ(a, b),
        cirq.CZ(a, b),
    )
    circuit = cirq.Circuit(c_orig)

    def clean_up(operations):
        yield Marker()(a, b)
        yield operations
        yield Marker()(a, b)

    with cirq.testing.assert_deprecated("Use cirq.optimize_for_target_gateset",
                                        deadline='v1.0',
                                        count=2):
        optimizer = cirq.MergeInteractions(allow_partial_czs=False,
                                           post_clean_up=clean_up)
    optimizer.optimize_circuit(circuit)
    circuit = cirq.drop_empty_moments(circuit)

    assert isinstance(circuit[0].operations[0].gate, Marker)
    assert isinstance(circuit[-1].operations[0].gate, Marker)

    u_before = c_orig.unitary()
    u_after = circuit[1:-1].unitary()
    cirq.testing.assert_allclose_up_to_global_phase(u_before,
                                                    u_after,
                                                    atol=1e-8)
def test_drop_empty_moments():
    q1, q2 = cirq.LineQubit.range(2)
    c_nested = cirq.FrozenCircuit(cirq.Moment(), cirq.Moment(),
                                  cirq.Moment([cirq.CNOT(q1, q2)]),
                                  cirq.Moment())
    c_nested_dropped = cirq.FrozenCircuit(cirq.CNOT(q1, q2))
    c_orig = cirq.Circuit(
        c_nested,
        cirq.CircuitOperation(c_nested).repeat(6).with_tags("nocompile"),
        c_nested,
        cirq.CircuitOperation(c_nested).repeat(5).with_tags("preserve_tag"),
        c_nested,
    )
    c_expected = cirq.Circuit(
        c_nested_dropped,
        cirq.CircuitOperation(c_nested).repeat(6).with_tags("nocompile"),
        c_nested_dropped,
        cirq.CircuitOperation(c_nested_dropped).repeat(5).with_tags(
            "preserve_tag"),
        c_nested_dropped,
    )
    context = cirq.TransformerContext(tags_to_ignore=("nocompile", ),
                                      deep=True)
    cirq.testing.assert_same_circuits(
        cirq.drop_empty_moments(c_orig, context=context), c_expected)
def test_drop():
    q1 = cirq.NamedQubit('q1')
    q2 = cirq.NamedQubit('q2')
    cirq.testing.assert_same_circuits(
        cirq.drop_empty_moments(
            cirq.Circuit(cirq.Moment(), cirq.Moment(),
                         cirq.Moment([cirq.CNOT(q1, q2)]), cirq.Moment())),
        cirq.Circuit(cirq.Moment([cirq.CNOT(q1, q2)])),
    )
def test_tensor_density_matrix_gridqubit():
    qubits = cirq.GridQubit.rect(2, 2)
    circuit = cirq.testing.random_circuit(qubits=qubits, n_moments=10, op_density=0.8)
    circuit = cirq.drop_empty_moments(circuit)
    noise_model = cirq.ConstantQubitNoiseModel(cirq.DepolarizingChannel(p=1e-3))
    circuit = cirq.Circuit(noise_model.noisy_moments(circuit.moments, qubits))
    rho1 = cirq.final_density_matrix(circuit, dtype=np.complex128)
    rho2 = ccq.tensor_density_matrix(circuit, qubits)
    np.testing.assert_allclose(rho1, rho2, atol=1e-8)
def test_respects_nocompile_tags():
    q = cirq.NamedQubit("q")
    c = cirq.Circuit(
        [cirq.Z(q), cirq.H(q), cirq.X(q), cirq.H(q), cirq.X(q).with_tags("nocompile"), cirq.H(q)]
    )
    context = cirq.TransformerContext(tags_to_ignore=("nocompile",))
    c = cirq.drop_empty_moments(cirq.merge_k_qubit_unitaries(c, k=1, context=context))
    assert len(c) == 3
    cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary(c[0]), np.eye(2), atol=1e-7)
    assert c[1][q] == cirq.X(q).with_tags("nocompile")
    assert isinstance(c[-1][q].gate, cirq.MatrixGate)
Beispiel #10
0
def assert_optimizes(
    before: cirq.Circuit,
    expected: cirq.Circuit,
    compare_unitaries: bool = True,
    eject_parameterized: bool = False,
):
    with cirq.testing.assert_deprecated("Use cirq.eject_phased_paulis",
                                        deadline='v1.0'):
        opt = cirq.EjectPhasedPaulis(eject_parameterized=eject_parameterized)

    circuit = before.copy()
    expected = cirq.drop_empty_moments(expected)
    opt.optimize_circuit(circuit)

    # They should have equivalent effects.
    if compare_unitaries:
        if cirq.is_parameterized(circuit):
            for a in (0, 0.1, 0.5, -1.0, np.pi, np.pi / 2):
                params: cirq.ParamDictType = {'x': a, 'y': a / 2, 'z': -2 * a}
                (cirq.testing.
                 assert_circuits_with_terminal_measurements_are_equivalent(
                     cirq.resolve_parameters(circuit, params),
                     cirq.resolve_parameters(expected, params),
                     1e-8,
                 ))
        else:
            (cirq.testing.
             assert_circuits_with_terminal_measurements_are_equivalent(
                 circuit, expected, 1e-8))

    # And match the expected circuit.
    assert circuit == expected, ("Circuit wasn't optimized as expected.\n"
                                 "INPUT:\n"
                                 "{}\n"
                                 "\n"
                                 "EXPECTED OUTPUT:\n"
                                 "{}\n"
                                 "\n"
                                 "ACTUAL OUTPUT:\n"
                                 "{}\n"
                                 "\n"
                                 "EXPECTED OUTPUT (detailed):\n"
                                 "{!r}\n"
                                 "\n"
                                 "ACTUAL OUTPUT (detailed):\n"
                                 "{!r}").format(before, expected, circuit,
                                                expected, circuit)

    # And it should be idempotent.
    opt.optimize_circuit(circuit)
    assert circuit == expected
Beispiel #11
0
def test_swap():
    a, b = cirq.LineQubit.range(2)
    original = cirq.Circuit([cirq.rz(0.123).on(a), cirq.SWAP(a, b)])
    optimized = original.copy()

    optimized = cirq.eject_z(optimized)
    optimized = cirq.drop_empty_moments(optimized)

    assert optimized[0].operations == (cirq.SWAP(a, b), )
    # Note: EjectZ drops `global_phase` from Rz turning it into a Z
    assert optimized[1].operations == (cirq.Z(b)**(0.123 / np.pi), )
    cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary(original),
                                                    cirq.unitary(optimized),
                                                    atol=1e-8)
Beispiel #12
0
def test_swap_iswap(exponent):
    a, b = cirq.LineQubit.range(2)
    original = cirq.Circuit([cirq.rz(0.123).on(a), cirq.ISWAP(a, b)**exponent])
    optimized = original.copy()
    with cirq.testing.assert_deprecated("Use cirq.eject_z", deadline='v1.0'):
        cirq.EjectZ().optimize_circuit(optimized)
    optimized = cirq.drop_empty_moments(optimized)

    assert optimized[0].operations == (cirq.ISWAP(a, b)**exponent, )
    # Note: EjectZ drops `global_phase` from Rz turning it into a Z
    assert optimized[1].operations == (cirq.Z(b)**(0.123 / np.pi), )
    cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary(original),
                                                    cirq.unitary(optimized),
                                                    atol=1e-8)
def test_drop_negligible():
    (q0, ) = _make_qubits(1)
    sym = sympy.Symbol('a')
    circuit = cirq.Circuit(
        cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**0.25,
        cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**1e-10,
        cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**sym,
    )
    expected = cirq.Circuit(
        cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**0.25,
        cirq.PauliStringPhasor(cirq.PauliString({q0: cirq.Z}))**sym,
    )
    circuit = cirq.drop_negligible_operations(circuit)
    circuit = cirq.drop_empty_moments(circuit)
    assert circuit == expected
Beispiel #14
0
def test_merge_swap_rzz_and_2q_unitaries_deep():
    q = cirq.LineQubit.range(3)
    swap_rzz = cirq.FrozenCircuit(cirq.SWAP(*q[:2]), cirq.ZZ(*q[:2]) ** 0.5)
    rzz_swap = cirq.FrozenCircuit(cirq.ZZ(*q[1:]) ** 0.25, cirq.SWAP(*q[1:]))
    x_cnot_x = cirq.FrozenCircuit(cirq.X(q[0]), cirq.CNOT(*q[:2]), cirq.X(q[0]))
    x_cz_x = cirq.FrozenCircuit(cirq.X(q[2]), cirq.CZ(*q[1:]), cirq.X(q[2]))
    c_orig = cirq.Circuit(
        cirq.CircuitOperation(swap_rzz).repeat(3).with_tags("ignore"),
        cirq.CircuitOperation(rzz_swap).repeat(5).with_tags("preserve_tag"),
        cirq.CircuitOperation(x_cnot_x).repeat(7).with_tags("ignore"),
        cirq.CircuitOperation(x_cz_x).repeat(9).with_tags("preserve_tag"),
        cirq.CircuitOperation(
            cirq.FrozenCircuit(
                [swap_rzz, rzz_swap, x_cnot_x, x_cz_x],
                cirq.Moment(cirq.Y(qq).with_tags("ignore") for qq in q),
            )
        ),
    )
    t_swap_rzz = "_merged_swap_rzz_tag"
    t_2q_cmp = "_merged_2q_unitaries_component"
    t_all = "_intermediate_result_tag_apply_to_all"

    def _wrap_cop(c: cirq.FrozenCircuit, *tags) -> cirq.FrozenCircuit:
        return cirq.FrozenCircuit(cirq.CircuitOperation(c).with_tags(*tags, t_all))

    c_expected = cirq.Circuit(
        cirq.CircuitOperation(swap_rzz).repeat(3).with_tags("ignore"),
        cirq.CircuitOperation(_wrap_cop(rzz_swap, t_swap_rzz)).repeat(5).with_tags("preserve_tag"),
        cirq.CircuitOperation(x_cnot_x).repeat(7).with_tags("ignore"),
        cirq.CircuitOperation(_wrap_cop(x_cz_x, t_2q_cmp)).repeat(9).with_tags("preserve_tag"),
        cirq.CircuitOperation(
            cirq.FrozenCircuit(
                [_wrap_cop(swap_rzz, t_swap_rzz), _wrap_cop(rzz_swap, t_swap_rzz)],
                [_wrap_cop(x_cnot_x, t_2q_cmp), _wrap_cop(x_cz_x, t_2q_cmp)],
                cirq.Moment(cirq.Y(qq).with_tags("ignore") for qq in q),
            )
        ),
    )
    context = cirq.TransformerContext(tags_to_ignore=["ignore"], deep=True)
    c_new = sycamore_gateset.merge_swap_rzz_and_2q_unitaries(
        c_orig,
        context=context,
        merged_swap_rzz_tag=t_swap_rzz,
        merged_2q_component_tag=t_2q_cmp,
        intermediate_result_tag=t_all,
    )
    cirq.testing.assert_same_circuits(cirq.drop_empty_moments(c_new, context=context), c_expected)
Beispiel #15
0
    def test_eject_z(self, family, ex, qubits):
        """Commuting z rotations towards the end of the circuit."""

        q0, q1 = qubits[:2]

        c = cirq.Circuit()
        c.append([
            cirq.ZPowGate(exponent=0.3)(q0),
            cirq.ZPowGate(exponent=0.8)(q1),
            family(exponent=ex)(q0, q1),
            cirq.MeasurementGate(1, key='q0')(q0),
            cirq.MeasurementGate(1, key='q1')(q1),
        ])

        c = cirq.eject_z(c)
        c = cirq.drop_empty_moments(c)

        # the ZPowGates have been commuted and canceled
        assert len(c) == 2
Beispiel #16
0
    def test_gate_merging(self, family, a, b, qubits):
        """Merging one-parameter group gates."""

        q0, q1 = qubits[:2]

        c = cirq.Circuit()
        c.append([
            family(exponent=a)(q0, q1),
            family(exponent=b)(q0, q1),
        ])

        MergeOneParameterGroupGates().optimize_circuit(c)
        c = cirq.drop_empty_moments(c)

        if abs((a + b) % MergeOneParameterGroupGates.PERIOD) < 1e-10:
            # the gates have canceled each other out
            assert len(c) == 0
        else:
            # the gates have been merged
            assert len(c) == 1
            expected = MergeOneParameterGroupGates._normalize_par(a + b)
            assert c[0].operations[0].gate.exponent == pytest.approx(expected, abs=TOLERANCE)
Beispiel #17
0
def simplify_expectation_value_circuit(circuit_sand: cirq.Circuit):
    """For low weight operators on low-degree circuits, we can simplify
    the circuit representation of an expectation value.

    In particular, this should be used on `circuit_for_expectation_value`
    circuits. It will merge single- and two-qubit gates from the "forwards"
    and "backwards" parts of the circuit outside of the operator's lightcone.

    This might be too slow in practice and you can just use quimb to simplify
    things for you.
    """
    n_op = sum(1 for _ in circuit_sand.all_operations())
    while True:
        MergeNQubitGates(n_qubits=1).optimize_circuit(circuit_sand)
        circuit_sand = cirq.drop_negligible_operations(circuit_sand, atol=1e-6)
        MergeNQubitGates(n_qubits=2).optimize_circuit(circuit_sand)
        circuit_sand = cirq.drop_empty_moments(circuit_sand)
        new_n_op = sum(1 for _ in circuit_sand.all_operations())

        if new_n_op < n_op:
            n_op = new_n_op
        else:
            return
Beispiel #18
0
def assert_equal_mod_empty(expected, actual):
    actual = cirq.drop_empty_moments(actual)
    cirq.testing.assert_same_circuits(actual, expected)
Beispiel #19
0
def test_merge_complex_circuit_preserving_moment_structure():
    q = cirq.LineQubit.range(3)
    c_orig = cirq.Circuit(
        cirq.Moment(cirq.H.on_each(*q)),
        cirq.CNOT(q[0], q[2]),
        cirq.CNOT(*q[0:2]),
        cirq.H(q[0]),
        cirq.CZ(*q[:2]),
        cirq.X(q[0]),
        cirq.Y(q[1]),
        cirq.CNOT(*q[0:2]),
        cirq.CNOT(*q[1:3]).with_tags("ignore"),
        cirq.X(q[0]),
        cirq.Moment(
            cirq.X(q[0]).with_tags("ignore"), cirq.Y(q[1]), cirq.Z(q[2])),
        cirq.Moment(cirq.CNOT(*q[:2]), cirq.measure(q[2], key="a")),
        cirq.X(q[0]).with_classical_controls("a"),
        strategy=cirq.InsertStrategy.NEW,
    )
    cirq.testing.assert_has_diagram(
        c_orig,
        '''
0: ───H───@───@───H───@───X───────@─────────────────X───X['ignore']───@───X───
          │   │       │           │                                   │   ║
1: ───H───┼───X───────@───────Y───X───@['ignore']───────Y─────────────X───╫───
          │                           │                                   ║
2: ───H───X───────────────────────────X─────────────────Z─────────────M───╫───
                                                                      ║   ║
a: ═══════════════════════════════════════════════════════════════════@═══^═══
''',
    )
    component_id = 0

    def rewriter_merge_to_circuit_op(
            op: 'cirq.CircuitOperation') -> 'cirq.OP_TREE':
        nonlocal component_id
        component_id = component_id + 1
        return op.with_tags(f'{component_id}')

    c_new = cirq.merge_k_qubit_unitaries(
        c_orig,
        k=2,
        context=cirq.TransformerContext(tags_to_ignore=("ignore", )),
        rewriter=rewriter_merge_to_circuit_op,
    )
    cirq.testing.assert_has_diagram(
        cirq.drop_empty_moments(c_new),
        '''
      [ 0: ───H───@─── ]        [ 0: ───────@───H───@───X───@───X─── ]                                            [ 0: ───────@─── ]
0: ───[           │    ]────────[           │       │       │        ]──────────────────────X['ignore']───────────[           │    ]────────X───
      [ 2: ───H───X─── ]['1']   [ 1: ───H───X───────@───Y───X─────── ]['2']                                       [ 1: ───Y───X─── ]['4']   ║
      │                         │                                                                                 │                         ║
1: ───┼─────────────────────────#2────────────────────────────────────────────@['ignore']─────────────────────────#2────────────────────────╫───
      │                                                                       │                                                             ║
2: ───#2──────────────────────────────────────────────────────────────────────X─────────────[ 2: ───Z─── ]['3']───M─────────────────────────╫───
                                                                                                                  ║                         ║
a: ═══════════════════════════════════════════════════════════════════════════════════════════════════════════════@═════════════════════════^═══''',
    )

    component_id = 0

    def rewriter_replace_with_decomp(
            op: 'cirq.CircuitOperation') -> 'cirq.OP_TREE':
        nonlocal component_id
        component_id = component_id + 1
        tag = f'{component_id}'
        if len(op.qubits) == 1:
            return [cirq.T(op.qubits[0]).with_tags(tag)]
        one_layer = [op.with_tags(tag) for op in cirq.T.on_each(*op.qubits)]
        two_layer = [cirq.SQRT_ISWAP(*op.qubits).with_tags(tag)]
        return [one_layer, two_layer, one_layer]

    c_new = cirq.merge_k_qubit_unitaries(
        c_orig,
        k=2,
        context=cirq.TransformerContext(tags_to_ignore=("ignore", )),
        rewriter=rewriter_replace_with_decomp,
    )
    cirq.testing.assert_has_diagram(
        cirq.drop_empty_moments(c_new),
        '''
0: ───T['1']───iSwap['1']───T['1']───T['2']───iSwap['2']───T['2']─────────────────X['ignore']───T['4']───iSwap['4']───T['4']───X───
               │                              │                                                          │                     ║
1: ────────────┼─────────────────────T['2']───iSwap^0.5────T['2']───@['ignore']─────────────────T['4']───iSwap^0.5────T['4']───╫───
               │                                                    │                                                          ║
2: ───T['1']───iSwap^0.5────T['1']──────────────────────────────────X─────────────T['3']────────M──────────────────────────────╫───
                                                                                                ║                              ║
a: ═════════════════════════════════════════════════════════════════════════════════════════════@══════════════════════════════^═══''',
    )
Beispiel #20
0
def assert_equal_mod_empty(expected, actual):
    actual = cirq.drop_empty_moments(actual)
    assert expected == actual, f'EXPECTED {expected} : ACTUAL {actual}'