Channels can only be applied at the end of a circuit
Closed this issue · 3 comments
Before posting a bug report
- I have searched exisisting GitHub issues to make sure the issue does not already exist.
Expected behavior
An Attenuator
or Amplifier
gate is applied on a single mode of the circuit.
Actual behavior
An Attenuator
or Amplifier
gates can only be applied at the end of the circuit, otherwise it raises the reported error.
Reproduces how often
Every time an Attenuator
or Amplifier
is applied somewhere other than the end of the circuit.
System information
Python version: 3.8.5
Platform info: Linux-5.4.72-microsoft-standard-WSL2-x86_64-with-glibc2.29
Installation path: /home/sduquemesa/xanadu/MrMustard/mrmustard
Mr Mustard version: 0.2.0-dev
Numpy version: 1.19.5
Numba version: 0.53.1
Scipy version: 1.7.3
The Walrus version: 0.17.0
TensorFlow version: 2.6.2
Torch version: None
Source code
import mrmustard.lab as mml
circ = mml.Circuit()
circ = circ >> mml.Attenuator(0.932)[1] >> mml.BSgate(theta=0.627, phi=1.15)[0, 1]
S = circ.XYd[0]
Tracebacks
Input In [4], in <module>
1 circ = mml.Circuit()
2 circ = circ >> mml.Attenuator(0.932)[1] >> mml.BSgate(theta=0.627, phi=1.15)[0, 1]
----> 4 circ.XYd[0]
File ~/xanadu/MrMustard/mrmustard/lab/circuit.py:83, in Circuit.XYd(self)
81 opd = opd.clone(len(op.modes), modes=op.modes)
82 X = opX @ X
---> 83 Y = opX @ Y @ opX.T + opY
84 d = opX @ d + opd
85 return X.to_xxpp(), Y.to_xxpp(), d.to_xxpp()
File ~/xanadu/MrMustard/mrmustard/utils/xptensor.py:253, in XPTensor.__matmul__(self, other)
251 if self.isMatrix and other.isMatrix:
252 tensor, modes = self._mode_aware_matmul(other)
--> 253 return XPMatrix(tensor, like_1=self.like_1 and other.like_1, modes=modes)
254 elif self.isMatrix and other.isVector:
255 tensor, modes = self._mode_aware_matmul(other)
File ~/xanadu/MrMustard/mrmustard/utils/xptensor.py:541, in XPMatrix.__init__(self, tensor, like_0, like_1, modes)
537 modes = tuple(
538 list(range(s)) for s in tensor.shape[:2]
539 ) # NOTE assuming that it isn't a coherence block
540 like_0 = like_0 if like_0 is not None else not like_1
--> 541 super().__init__(tensor, like_0, isVector=False, modes=modes)
File ~/xanadu/MrMustard/mrmustard/utils/xptensor.py:85, in XPTensor.__init__(self, tensor, like_0, isVector, modes)
83 self.tensor = tensor
84 if not (set(modes[0]) == set(modes[1]) or set(modes[0]).isdisjoint(modes[1])):
---> 85 raise ValueError(
86 "The inmodes and outmodes should either contain the same modes or be disjoint"
87 )
88 self.modes = modes
ValueError: The inmodes and outmodes should either contain the same modes or be disjoint
Additional information
Notice that the following code where the `Attenuator` is located at the end of the circuit will run successfully.
import mrmustard.lab as mml
# %%
circ = mml.Circuit()
circ = circ >> mml.BSgate(theta=0.627, phi=1.15)[0, 1] >> mml.Attenuator(0.932)[1]
S = circ.XYd[0]
The issue is resolved if the Attenuator
is applied explicitly to all the circuit modes, where only the mode of interest have a parameter value different than one (a trivial attenuator).
import mrmustard.lab as mml
circ = mml.Circuit()
circ = circ >> mml.Attenuator([0.932,1], modes = [0,1]) >> mml.BSgate(theta=0.627, phi=1.15)[0, 1]
S = circ.XYd[0]
In this case there is an overhead of adding trivial operations to modes, which in turn can impact calculation and circuit optimization performance.
All in all this seems to be an issue coming from the way in which modes are handled by the inner workings of Mr Mustard. In specific, when doing mode-aware math multiplication.
what is happening is that an empty circuit has no modes, so if you concatenate other operations without specifying the modes (using __getitem__
) then XPTensor doesn't know where to apply them (hence the error message).
in principle one shouldn't create a Circuit
on its own, but rather a Circuit
is returned by concatenating operations:
circ = mml.Attenuator(0.932)[1] >> mml.BSgate(theta=0.627, phi=1.15)[0, 1]
Alternatively, specify all the modes and things should work just fine
circ = mml.Circuit()
circ = circ[0,1] >> mml.Attenuator(0.932)[1] >> mml.BSgate(theta=0.627, phi=1.15)[0, 1]
Thanks @filippo for your suggestion, however it does not seem to solve the issue. I've tried with this simple circuit:
circ = mml.Sgate(r) >> mml.Attenuator(eta)[1] >> mml.BSgate(theta=alpha[t], phi=np.pi / 2)[1, 0]
np.array(circ.XYd[0])
and also directly specifying the modes as you suggested
circ = mml.Circuit()
circ = mml.Sgate(r) >> mml.Attenuator(eta)[1] >> mml.BSgate(theta=alpha[t], phi=np.pi / 2)[1, 0]
np.array(circ.XYd[0])
In both cases the execution fails with the same error.
The issue seem to come from the way in which _mode_aware_matmul
checks for valid modes (for example here and here). Notice this check is applied in several different places in Mr Mustard.
For the sake of illustration, consider the following check
if not (set(modes[0]) == set(modes[1]) or set(modes[0]).isdisjoint(modes[1])):
raise ValueError(
"The inmodes and outmodes should either contain the same modes or be disjoint"
)
and the following circuit
circ = Sgate(0.4)[0,1] >> Attenuator(t, nbar)[0] >> BSgate(theta, 0)[0,1]
When executing circ.XYd
the calculation, the updates related to the BSgate
to the value Y
(on circuit.py:L83) will have
inmodes = [0]
this is the mode in which the last op acted on, meaning theAttenuator
outmodes = [0,1]
this are the output modes of theBSgate
op;
hence the _mode_aware_matmul
operation Y = X_bs[0,1] @ Y_attenuator[0] @ X_bs.T[0,1]
fails (values between brackets are annotation of the modes on which the ops acts on, this is not runnable code) due to the check mentioned above.