def test_map_operations_respects_tags_to_ignore(): q = cirq.LineQubit.range(2) c = cirq.Circuit(cirq.CNOT(*q), cirq.CNOT(*q).with_tags("ignore"), cirq.CNOT(*q)) cirq.testing.assert_same_circuits( cirq.Circuit(cirq.Z.on_each(*q), cirq.CNOT(*q).with_tags("ignore"), cirq.Z.on_each(*q)), cirq.map_operations(c, lambda op, i: cirq.Z.on_each(*op.qubits), tags_to_ignore=["ignore"]), )
def test_map_operations_can_drop_operations(): q = cirq.LineQubit.range(2) c = cirq.Circuit(cirq.X(q[0]), cirq.Y(q[1]), cirq.X(q[1]), cirq.Y(q[0])) c_mapped = cirq.map_operations( c, lambda op, _: op if op.gate == cirq.X else []) c_expected = cirq.Circuit(cirq.Moment(cirq.X(q[0])), cirq.Moment(cirq.X(q[1]))) cirq.testing.assert_same_circuits(c_mapped, c_expected)
def test_map_operations_can_add_qubits_if_flag_false(): q = cirq.LineQubit.range(2) c = cirq.Circuit(cirq.H(q[0])) c_mapped = cirq.map_operations(c, lambda *_: cirq.CNOT(q[0], q[1]), raise_if_add_qubits=False) cirq.testing.assert_same_circuits(c_mapped, cirq.Circuit(cirq.CNOT(q[0], q[1])))
def test_map_operations_does_not_insert_too_many_moments(): q = cirq.LineQubit.range(5) c_orig = cirq.Circuit( cirq.CX(q[0], q[1]), cirq.CX(q[3], q[2]), cirq.CX(q[3], q[4]), ) def map_func(op: cirq.Operation, _: int) -> cirq.OP_TREE: if op.gate == cirq.CX: yield cirq.Z.on_each(*op.qubits) yield cirq.CX(*op.qubits) yield cirq.Z.on_each(*op.qubits) return op cirq.testing.assert_has_diagram( c_orig, ''' 0: ───@─────── │ 1: ───X─────── 2: ───X─────── │ 3: ───@───@─── │ 4: ───────X─── ''', ) c_mapped = cirq.map_operations(c_orig, map_func) circuit_op = cirq.CircuitOperation( cirq.FrozenCircuit(cirq.Z.on_each(q[0], q[1]), cirq.CNOT(q[0], q[1]), cirq.Z.on_each(q[0], q[1]))) c_expected = cirq.Circuit( circuit_op.with_qubits( q[0], q[1]).mapped_op().with_tags('<mapped_circuit_op>'), circuit_op.with_qubits( q[3], q[2]).mapped_op().with_tags('<mapped_circuit_op>'), circuit_op.with_qubits( q[3], q[4]).mapped_op().with_tags('<mapped_circuit_op>'), ) cirq.testing.assert_same_circuits(c_mapped, c_expected) cirq.testing.assert_has_diagram( cirq.map_operations_and_unroll(c_orig, map_func), ''' 0: ───Z───@───Z─────────────── │ 1: ───Z───X───Z─────────────── 2: ───Z───X───Z─────────────── │ 3: ───Z───@───Z───Z───@───Z─── │ 4: ───────────────Z───X───Z─── ''', )
def test_unroll_circuit_op_and_variants(): q = cirq.LineQubit.range(2) c = cirq.Circuit(cirq.X(q[0]), cirq.CNOT(q[0], q[1]), cirq.X(q[0])) cirq.testing.assert_has_diagram( c, ''' 0: ───X───@───X─── │ 1: ───────X─────── ''', ) mapped_circuit = cirq.map_operations( c, lambda op, i: [cirq.Z(q[1])] * 2 if op.gate == cirq.CNOT else op) mapped_circuit_deep = cirq.Circuit( [ cirq.Moment(cirq.CircuitOperation(cirq.FrozenCircuit(m))) for m in mapped_circuit[:-1] ], mapped_circuit[-1], ) for unroller in [ cirq.unroll_circuit_op_greedy_earliest, cirq.unroll_circuit_op_greedy_frontier, cirq.unroll_circuit_op, ]: cirq.testing.assert_same_circuits( unroller(mapped_circuit), unroller(mapped_circuit_deep, tags_to_check=None, deep=True)) cirq.testing.assert_has_diagram( cirq.unroll_circuit_op(mapped_circuit), ''' 0: ───X───────────X─── 1: ───────Z───Z─────── ''', ) cirq.testing.assert_has_diagram( cirq.unroll_circuit_op_greedy_earliest(mapped_circuit), ''' 0: ───X───────X─── 1: ───Z───Z─────── ''', ) cirq.testing.assert_has_diagram( cirq.unroll_circuit_op_greedy_frontier(mapped_circuit), ''' 0: ───X───────X─── 1: ───────Z───Z─── ''', )
def test_map_operations_deep_respects_tags_to_ignore(): q = cirq.LineQubit.range(2) c_nested = cirq.FrozenCircuit(cirq.CX(*q), cirq.CX(*q).with_tags("ignore"), cirq.CX(*q)) c_nested_mapped = cirq.FrozenCircuit(cirq.CZ(*q), cirq.CX(*q).with_tags("ignore"), cirq.CZ(*q)) c_orig = cirq.Circuit( c_nested, cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"), c_nested, cirq.CircuitOperation( cirq.FrozenCircuit( cirq.CircuitOperation(c_nested).repeat(5).with_tags( "preserve_tag"), cirq.CircuitOperation(c_nested).repeat(6).with_tags("ignore"), cirq.CircuitOperation(c_nested).repeat(7), )), c_nested, ) c_expected = cirq.Circuit( c_nested_mapped, cirq.CircuitOperation(c_nested).repeat(4).with_tags("ignore"), c_nested_mapped, cirq.CircuitOperation( cirq.FrozenCircuit( cirq.CircuitOperation(c_nested_mapped).repeat(5).with_tags( "preserve_tag"), cirq.CircuitOperation(c_nested).repeat(6).with_tags("ignore"), cirq.CircuitOperation(c_nested_mapped).repeat(7), )), c_nested_mapped, ) cirq.testing.assert_same_circuits( cirq.map_operations( c_orig, lambda op, _: cirq.CZ(*op.qubits) if op.gate == cirq.CX else op, tags_to_ignore=["ignore"], deep=True, ), c_expected, )
def test_map_operations_can_write_new_gates_inline(): x = cirq.NamedQubit('x') y = cirq.NamedQubit('y') z = cirq.NamedQubit('z') c = cirq.Circuit( cirq.CZ(x, y), cirq.Y(x), cirq.Z(x), cirq.X(y), cirq.CNOT(y, z), cirq.Z(y), cirq.Z(x), cirq.CNOT(y, z), cirq.CNOT(z, y), ) cirq.testing.assert_has_diagram( c, ''' x: ───@───Y───Z───Z─────────── │ y: ───@───X───@───Z───@───X─── │ │ │ z: ───────────X───────X───@─── ''', ) expected_diagram = ''' x: ───X───X───X───X─────────── y: ───X───X───X───X───X───X─── z: ───────────X───────X───X─── ''' cirq.testing.assert_has_diagram( cirq.map_operations(c, lambda op, _: cirq.X.on_each(*op.qubits)), expected_diagram) cirq.testing.assert_has_diagram( cirq.map_operations_and_unroll( c, lambda op, _: cirq.X.on_each(*op.qubits)), expected_diagram, )
def _convert_to_circuit_with_drift( simulator: PhasedFSimEngineSimulator, circuit: cirq.AbstractCircuit) -> cirq.Circuit: def map_func(op: cirq.Operation, _) -> cirq.Operation: if op.gate is None: raise IncompatibleMomentError(f'Operation {op} has a missing gate') if (isinstance(op.gate, (cirq.MeasurementGate, cirq.WaitGate)) or cirq.num_qubits(op.gate) == 1): return op translated = simulator.gates_translator(op.gate) if translated is None: raise IncompatibleMomentError( f'Moment contains non-single qubit operation ' f'{op} with unsupported gate') a, b = op.qubits return simulator.create_gate_with_drift(a, b, translated).on(a, b) return cirq.map_operations(circuit, map_func).unfreeze(copy=False)
def test_map_operations_deep_subcircuits(): q = cirq.LineQubit.range(5) c_orig = cirq.Circuit( cirq.CX(q[0], q[1]), cirq.CX(q[3], q[2]), cirq.CX(q[3], q[4]), ) c_orig_with_circuit_ops = cirq.Circuit( cirq.CircuitOperation( cirq.FrozenCircuit([ cirq.CircuitOperation(cirq.FrozenCircuit(op)) for op in c_orig.all_operations() ]))) def map_func(op: cirq.Operation, _: int) -> cirq.OP_TREE: yield [ cirq.Z.on_each(*op.qubits), cirq.CX(*op.qubits), cirq.Z.on_each(*op.qubits), ] if op.gate == cirq.CX else op c_mapped = cirq.map_operations(c_orig_with_circuit_ops, map_func, deep=True) c_mapped = cirq.unroll_circuit_op(c_mapped, deep=True, tags_to_check=None) cirq.testing.assert_has_diagram( c_mapped, ''' 0: ───Z───@───Z─────────────── │ 1: ───Z───X───Z─────────────── 2: ───Z───X───Z─────────────── │ 3: ───Z───@───Z───Z───@───Z─── │ 4: ───────────────Z───X───Z─── ''', )
def test_unroll_circuit_op_and_variants(): q = cirq.LineQubit.range(2) c = cirq.Circuit(cirq.X(q[0]), cirq.CNOT(q[0], q[1]), cirq.X(q[0])) cirq.testing.assert_has_diagram( c, ''' 0: ───X───@───X─── │ 1: ───────X─────── ''', ) mapped_circuit = cirq.map_operations( c, lambda op, i: [cirq.Z(q[1])] * 2 if op.gate == cirq.CNOT else op) cirq.testing.assert_has_diagram( cirq.unroll_circuit_op(mapped_circuit), ''' 0: ───X───────────X─── 1: ───────Z───Z─────── ''', ) cirq.testing.assert_has_diagram( cirq.unroll_circuit_op_greedy_earliest(mapped_circuit), ''' 0: ───X───────X─── 1: ───Z───Z─────── ''', ) cirq.testing.assert_has_diagram( cirq.unroll_circuit_op_greedy_frontier(mapped_circuit), ''' 0: ───X───────X─── 1: ───────Z───Z─── ''', )
def merge_swap_rzz_and_2q_unitaries( circuit: 'cirq.AbstractCircuit', *, context: Optional['cirq.TransformerContext'] = None, merged_swap_rzz_tag: str = "_merged_swap_rzz", merged_2q_component_tag: str = "_merged_2q_unitaries", intermediate_result_tag: Optional[str] = None, ) -> 'cirq.Circuit': """Merges 2-qubit connected components and adjacent `cirq.SWAP` and `cirq.ZZPowGate` gates. Does the following two transformations, in that order: 1. Merges adjacent occurrences of `cirq.SWAP` and `cirq.ZZPowGate` into a `cirq.CircuitOperation` tagged with `merged_swap_rzz_tag`. 2. Merges connected components of 1 and 2 qubit unitaries in the circuit into a `cirq.CircuitOperation` tagged with `merged_2q_component_tag`, ignoring the newly introduced tagged circuit operations added in Step-1. Args: circuit: Input circuit to transform. It will not be modified. context: `cirq.TransformerContext` storing common configurable options for transformers. merged_swap_rzz_tag: Tag to apply on newly introduced circuit operations wrapping adjacent `cirq.SWAP` and `cirq.ZZPowGate`s. merged_2q_component_tag: Tag to apply on newly introduced circuit operations wrapping connected components of 1 and 2 qubit unitaries. intermediate_result_tag: If specified, the tag is added to newly introduced both the newly introduced circuit operations encapsulating swap_rzz or 2q connected component. Returns: Copy of the transformed input circuit. Raises: ValueError: If merged_2q_component_tag and merged_swap_rzz_tag are equal. """ if merged_2q_component_tag == merged_swap_rzz_tag: raise ValueError( "merged_swap_rzz_tag and merged_2q_component_tag should be different." ) def merge_func_swap_rzz(ops1: Sequence['cirq.Operation'], ops2: Sequence['cirq.Operation']) -> bool: if not (len(ops1) == 1 and len(ops2) == 1): return False for op1, op2 in itertools.permutations([ops1[0], ops2[0]]): if op1.gate == cirq.SWAP and isinstance(op2.gate, cirq.ZZPowGate): return True return False tags_to_ignore = context.tags_to_ignore if context else () deep = context.deep if context else False circuit = cirq.merge_operations_to_circuit_op( circuit, merge_func_swap_rzz, tags_to_ignore=tags_to_ignore, merged_circuit_op_tag=merged_swap_rzz_tag, deep=deep, ) circuit = cirq.merge_k_qubit_unitaries_to_circuit_op( circuit, k=2, tags_to_ignore=tuple(tags_to_ignore) + (merged_swap_rzz_tag, ), merged_circuit_op_tag=merged_2q_component_tag, deep=deep, ) if intermediate_result_tag is not None: merged_cop_tags = {merged_swap_rzz_tag, merged_2q_component_tag} circuit = cirq.map_operations( circuit, map_func=lambda op, _: op if merged_cop_tags.isdisjoint(op.tags) else op.with_tags( cast(str, intermediate_result_tag)), tags_to_ignore=tags_to_ignore, deep=True, ) return circuit.unfreeze(copy=False)
def test_map_operations_raises_qubits_not_subset(): q = cirq.LineQubit.range(3) with pytest.raises(ValueError, match='should act on a subset'): _ = cirq.map_operations( cirq.Circuit(cirq.CNOT(q[0], q[1])), lambda op, i: cirq.CNOT(q[1], q[2]) )
def test_map_operations_deep_subcircuits(): q = cirq.LineQubit.range(5) c_orig = cirq.Circuit(cirq.CX(q[0], q[1]), cirq.CX(q[3], q[2]), cirq.CX(q[3], q[4])) c_orig_with_circuit_ops = cirq.Circuit( cirq.CircuitOperation( cirq.FrozenCircuit( [ cirq.CircuitOperation(cirq.FrozenCircuit(op)).repeat(2).with_tags("internal") for op in c_orig.all_operations() ] ) ) .repeat(6) .with_tags("external") ) def map_func(op: cirq.Operation, _: int) -> cirq.OP_TREE: yield [ cirq.Z.on_each(*op.qubits), cirq.CX(*op.qubits), cirq.Z.on_each(*op.qubits), ] if op.gate == cirq.CX else op cirq.testing.assert_has_diagram( c_orig_with_circuit_ops, ''' [ [ 0: ───@─── ] ] [ 0: ───[ │ ]────────────────────────────────────────────────────────────── ] [ [ 1: ───X─── ](loops=2)['internal'] ] [ │ ] [ 1: ───#2────────────────────────────────────────────────────────────────────────── ] [ ] [ [ 2: ───X─── ] ] 0: ───[ 2: ───[ │ ]────────────────────────────────────────────────────────────── ]──────────────────────── [ [ 3: ───@─── ](loops=2)['internal'] ] [ │ ] [ │ [ 3: ───@─── ] ] [ 3: ───#2────────────────────────────────────[ │ ]──────────────────────── ] [ [ 4: ───X─── ](loops=2)['internal'] ] [ │ ] [ 4: ─────────────────────────────────────────#2──────────────────────────────────── ](loops=6)['external'] │ 1: ───#2──────────────────────────────────────────────────────────────────────────────────────────────────────────── │ 2: ───#3──────────────────────────────────────────────────────────────────────────────────────────────────────────── │ 3: ───#4──────────────────────────────────────────────────────────────────────────────────────────────────────────── │ 4: ───#5──────────────────────────────────────────────────────────────────────────────────────────────────────────── ''', ) c_mapped = cirq.map_operations(c_orig_with_circuit_ops, map_func, deep=True) for unroller in [ cirq.unroll_circuit_op, cirq.unroll_circuit_op_greedy_earliest, cirq.unroll_circuit_op_greedy_frontier, ]: cirq.testing.assert_has_diagram( unroller(c_mapped, deep=True), ''' [ [ 0: ───Z───@───Z─── ] ] [ 0: ───[ │ ]────────────────────────────────────────────────────────────────────── ] [ [ 1: ───Z───X───Z─── ](loops=2)['internal'] ] [ │ ] [ 1: ───#2────────────────────────────────────────────────────────────────────────────────────────── ] [ ] [ [ 2: ───Z───X───Z─── ] ] 0: ───[ 2: ───[ │ ]────────────────────────────────────────────────────────────────────── ]──────────────────────── [ [ 3: ───Z───@───Z─── ](loops=2)['internal'] ] [ │ ] [ │ [ 3: ───Z───@───Z─── ] ] [ 3: ───#2────────────────────────────────────────────[ │ ]──────────────────────── ] [ [ 4: ───Z───X───Z─── ](loops=2)['internal'] ] [ │ ] [ 4: ─────────────────────────────────────────────────#2──────────────────────────────────────────── ](loops=6)['external'] │ 1: ───#2──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │ 2: ───#3──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │ 3: ───#4──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── │ 4: ───#5──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ''', )