quantumlib / Cirq

A Python framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
Apache License 2.0
4.28k stars 1.02k forks source link

Can't simulate a repeated `CircuitOperation` that contains a `repeat_until` `CircuitOperation` #6446

Open vtomole opened 9 months ago

vtomole commented 9 months ago

Description of the issue Repetitively checking the syndromes of a prepared state before using it to measure stabilizers is an important primitive for fault tolerance. True fault tolerance requires that this procedure happen multiple times.

For a minimum reproducible example, measuring a qubit until it's |0> and then applying an X gate to it multiple times will throw a raise ValueError('Infinite loop: condition is not modified in subcircuit.')

How to reproduce the issue

import cirq
import sympy

sim = cirq.Simulator()
q = cirq.LineQubit(0)
inner_loop = cirq.CircuitOperation(
    cirq.FrozenCircuit(cirq.H(q), cirq.measure(q, key="inner_loop")),
    use_repetition_ids=False,
    repeat_until=cirq.SympyCondition(sympy.Eq(sympy.Symbol("inner_loop"), 0)),
)
outer_loop = cirq.Circuit(inner_loop, cirq.X(q), cirq.measure(q, key="outer_loop"))

circuit = cirq.Circuit(cirq.CircuitOperation(cirq.FrozenCircuit(outer_loop), repetitions=2))
print(circuit)
result = sim.run(circuit, repetitions=1)
print(result)

Will print

ValueError: Infinite loop: condition is not modified in subcircuit.

The alternative is to run the CircuitOperation twice, but this breaks the printing of the result but this will throw ValueError: Cannot extract 2D measurements for repeated keys

import cirq
import sympy

sim = cirq.Simulator()
q = cirq.LineQubit(0)
inner_loop = cirq.CircuitOperation(
    cirq.FrozenCircuit(cirq.H(q), cirq.measure(q, key="inner_loop0")),
    use_repetition_ids=False,
    repeat_until=cirq.SympyCondition(sympy.Eq(sympy.Symbol("inner_loop0"), 0)),
)
inner_loop1 = cirq.CircuitOperation(
    cirq.FrozenCircuit(cirq.H(q), cirq.measure(q, key="inner_loop1")),
    use_repetition_ids=False,
    repeat_until=cirq.SympyCondition(sympy.Eq(sympy.Symbol("inner_loop1"), 0)),
)

outer_loop = cirq.Circuit(inner_loop, cirq.X(q), cirq.measure(q, key="outer_loop"))
outer_loop1 = cirq.Circuit(inner_loop1, cirq.X(q), cirq.measure(q, key="outer_loop1"))

circuit = cirq.Circuit(outer_loop, outer_loop1)
print(circuit)
result = sim.run(circuit, repetitions=1)
print(result)

Cirq version 1.4.0.dev20240126200039

daxfohl commented 9 months ago

I can't for the life of me remember how to set up the dev environment, but looking through the code, it looks like you might be able to work around the first issue by setting use_repetition_ids=False in the outer loop as well.

The bug appears to be in the CircuitOperation._with_rescoped_keys_ implementation, as it rescopes all the measurement keys in the subcircuit but doesn't rescope the repeat_until key, so they no longer align. repeat_until is of type Condition, which has a _with_rescoped_keys_ implementation, so it should just be a matter of forwarding on the call.

It looks like a number of other protocols in CircuitOperation should account for the keys in repeat_until condition too: _with_measurement_key_mapping_, _with_key_path_, _with_key_path_prefix_.

As for the error printing the result, IIRC that was by design, but it wasn't my domain so I can't say for sure. Definitely seems like something to revisit though if it blocks this use case. Some discussion at https://github.com/quantumlib/Cirq/pull/4555

vtomole commented 9 months ago

it looks like you might be able to work around the first issue by setting use_repetition_ids=Falsein the outer loop as well.

I've confirmed that this workaround will do the trick for now.

As for the error printing the result, IIRC that was by design, but it wasn't my domain so I can't say for sure.

I've created a separate issue to ask about this.

telescopic commented 7 months ago

@tanujkhattar / @verult I'd like to take this up if possible

vtomole commented 7 months ago

Thanks for the offer @telescopic . I've assigned this to you.