def test_merge_operations_respects_tags_to_ignore(): q = cirq.LineQubit.range(2) c = cirq.Circuit( cirq.CZ(*q), cirq.Moment(cirq.X(q[0]), cirq.Y(q[1]).with_tags("ignore")), cirq.Moment(cirq.X(q[0]).with_tags("ignore"), cirq.Y(q[1])), cirq.CZ(*q), [cirq.CNOT(*q), cirq.CNOT(*q).with_tags("ignore"), cirq.CNOT(*q)], cirq.CZ(*q), ) c_merged = cirq.Circuit( cirq.Moment(cirq.CZ(*q)), cirq.Moment(cirq.Y(q[1]).with_tags("ignore")), cirq.Moment(cirq.X(q[0]).with_tags("ignore")), cirq.Moment(cirq.CZ(*q)), cirq.Moment(), cirq.Moment(cirq.CNOT(*q).with_tags("ignore")), cirq.Moment(cirq.CZ(*q)), cirq.Moment(), ) def merge_func(op1, op2): """Artificial example where a CZ will absorb any merge-able operation.""" return op1 if op1.gate == cirq.CZ else (op2 if op2.gate == cirq.CZ else None) cirq.testing.assert_same_circuits( cirq.merge_operations(c, merge_func, tags_to_ignore=["ignore"]), c_merged )
def test_merge_operations_deep(): q = cirq.LineQubit.range(2) h_cz_y = [cirq.H(q[0]), cirq.CZ(*q), cirq.Y(q[1])] m_cz_m = [cirq.Moment(), cirq.Moment(cirq.CZ(*q)), cirq.Moment()] c_orig = cirq.Circuit( h_cz_y, cirq.Moment(cirq.X(q[0]).with_tags("ignore"), cirq.Y(q[1])), cirq.CircuitOperation(cirq.FrozenCircuit(h_cz_y)).repeat(6).with_tags("ignore"), [cirq.CNOT(*q), cirq.CNOT(*q)], cirq.CircuitOperation(cirq.FrozenCircuit(h_cz_y)).repeat(4), [cirq.CNOT(*q), cirq.CZ(*q), cirq.CNOT(*q)], cirq.CircuitOperation(cirq.FrozenCircuit(h_cz_y)).repeat(5).with_tags("preserve_tag"), ) c_expected = cirq.Circuit( m_cz_m, cirq.Moment(cirq.X(q[0]).with_tags("ignore")), cirq.CircuitOperation(cirq.FrozenCircuit(h_cz_y)).repeat(6).with_tags("ignore"), [cirq.CNOT(*q), cirq.CNOT(*q)], cirq.CircuitOperation(cirq.FrozenCircuit(m_cz_m)).repeat(4), [cirq.CZ(*q), cirq.Moment(), cirq.Moment()], cirq.CircuitOperation(cirq.FrozenCircuit(m_cz_m)).repeat(5).with_tags("preserve_tag"), strategy=cirq.InsertStrategy.NEW, ) def merge_func(op1, op2): """Artificial example where a CZ will absorb any merge-able operation.""" for op in [op1, op2]: if op.gate == cirq.CZ: return op return None cirq.testing.assert_same_circuits( cirq.merge_operations(c_orig, merge_func, tags_to_ignore=["ignore"], deep=True), c_expected )
def test_merge_operations_merges_connected_component(): c_orig = _create_circuit_to_merge() 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───────────────────────── ''', ) def merge_func(op1, op2): """Artificial example where a CZ will absorb any merge-able operation.""" for op in [op1, op2]: if op.gate == cirq.CZ: return op return None c_new = cirq.merge_operations(c_orig, merge_func) cirq.testing.assert_has_diagram( c_new, ''' 0: ───H───@───────────@───────────────────────────@─── │ │ │ 1: ───────┼───────────@───────────────@───────Y───X─── │ │ 2: ───H───X───────────────────────────X───────────────''', )
def test_merge_operations_nothing_to_merge(): def fail_if_called_func(*_): assert False # Empty Circuit. c = cirq.Circuit() assert cirq.merge_operations(c, fail_if_called_func) == c # Single moment q = cirq.LineQubit.range(3) c += cirq.Moment(cirq.CZ(*q[:2])) assert cirq.merge_operations(c, fail_if_called_func) == c # Multi moment with disjoint operations + global phase operation. c += cirq.Moment(cirq.X(q[2]), cirq.global_phase_operation(1j)) assert cirq.merge_operations(c, fail_if_called_func) == c # Tagged operations to be ignored. c += cirq.Moment(cirq.CNOT(*q[:2]).with_tags("ignore")) assert cirq.merge_operations(c, fail_if_called_func, tags_to_ignore=["ignore"]) == c
def test_merge_operations_does_not_merge_measurements_behind_ccos(): q = cirq.LineQubit.range(2) measure_op = cirq.measure(q[0], key="a") cco_op = cirq.X(q[1]).with_classical_controls("a") def merge_func(op1, op2): return cirq.I( *op1.qubits) if op1 == measure_op and op2 == measure_op else None circuit = cirq.Circuit([cirq.H(q[0]), measure_op, cco_op] * 2) cirq.testing.assert_same_circuits( cirq.merge_operations(circuit, merge_func), circuit) circuit = cirq.Circuit( [cirq.H(q[0]), measure_op, cco_op, measure_op, measure_op] * 2) expected_circuit = cirq.Circuit( [cirq.H(q[0]), measure_op, cco_op, cirq.I(q[0])] * 2) cirq.testing.assert_same_circuits( cirq.align_left(cirq.merge_operations(circuit, merge_func)), expected_circuit)
def test_merge_operations_complexity(op_density): prng = cirq.value.parse_random_state(11011) circuit = cirq.testing.random_circuit(20, 500, op_density, random_state=prng) for merge_func in [ lambda _, __: None, lambda op1, _: op1, lambda _, op2: op2, lambda op1, op2: (op1, op2, None)[prng.choice(3)], ]: def wrapped_merge_func(op1, op2): wrapped_merge_func.num_function_calls += 1 return merge_func(op1, op2) wrapped_merge_func.num_function_calls = 0 _ = cirq.merge_operations(circuit, wrapped_merge_func) total_operations = len([*circuit.all_operations()]) assert wrapped_merge_func.num_function_calls <= 2 * total_operations
def test_merge_operations_deterministic_order(qubit_order): q = cirq.LineQubit.range(2) c_orig = cirq.Circuit(cirq.identity_each(*q), cirq.H.on_each(q[i] for i in qubit_order)) cirq.testing.assert_has_diagram( c_orig, ''' 0: ───I───H─── │ 1: ───I───H───''', ) c_new = cirq.merge_operations( c_orig, lambda op1, op2: op2 if isinstance(op1.gate, cirq.IdentityGate) else None ) cirq.testing.assert_has_diagram( c_new, ''' 0: ───H─────── 1: ───────H───''', )
def test_merge_operations_raises(): q = cirq.LineQubit.range(3) c = cirq.Circuit(cirq.CZ(*q[:2]), cirq.X(q[0])) with pytest.raises(ValueError, match='must act on a subset of qubits'): cirq.merge_operations(c, lambda *_: cirq.X(q[2]))