QNET
QNET copied to clipboard
Infinite loop when applying feedback to certain SeriesProduct instances
For certain circuits that are instances of the SeriesProduct
class, applying feedback spawns an infinite loop. Here's sort of a minimum non-working example:
import sympy
from qnet.algebra.operator_algebra import Destroy
from qnet.circuit_components.phase_cc import Phase
from qnet.circuit_components.beamsplitter_cc import Beamsplitter
from qnet.algebra.circuit_algebra import SLH, cid, map_signals_circuit
a = Destroy(hs=1)
phi, theta = sympy.symbols('phi theta', real=True)
ph = Phase('phi', phi=phi)
bs = Beamsplitter('theta', theta=theta)
cavity = SLH([[1]], [a], 0)
circuit = (
(ph + (bs << cid(1) + cavity).toSLH()) <<
map_signals_circuit({2: 0}, 3)
)
circuit.feedback()
Before feedback, the circuit looks like this:
Applying feedback should thus be as simple as moving the phase shift over to the right. Instead, the program loops indefinitely.
The offending cycle looks something like this: SeriesProduct.feedback
calls SeriesProduct._feedback
calls Feedback.create
calls super().create
(which dispatches via Operation
to Expression.create
, but with cls
bound to Feedback
) calls match_replace
(as one of the simplifications
on line 214 in abstract_algebra.py
) calls a lambda
defined in Feedback._rules
calls SeriesProduct.feedback
, and the game is on...
The bug only appears in somewhat special circumstances. In the example above, the call to toSLH
is essential; if left out, such that the beamsplitter and cavity remain separate components in the definition of the circuit, no infinite loop appears. It also seems like internal degrees of freedom are required; for example, if the single-port cavity is replaced by a phase shift, everything works as expected. Finally, if toSLH
is called on the full circuit before applying feedback, i.e., circuit.toSLH().feedback()
, the infinite loop is avoided (not too surprising, since now feedback
is called on an SLH
object instead of a SeriesProduct
object).