mabuchilab / QNET

Computer algebra package for quantum mechanics and photonic quantum networks
https://qnet.readthedocs.io/
MIT License
71 stars 23 forks source link

Infinite loop when applying feedback to certain SeriesProduct instances #54

Open danielwe opened 7 years ago

danielwe commented 7 years ago

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: image 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).