In [31]:
from qnet.circuit_components.beamsplitter_cc import Beamsplitter
from qnet.circuit_components.two_port_kerr_cavity_cc import TwoPortKerrCavity
from qnet.circuit_components.displace_cc import Displace
import qnet.algebra.circuit_algebra as ca
In [32]:
import cirq; reload(cirq); cirq.init_js()
In [33]:
# domain
fm = cirq.Domain(name="fieldmode", causal=True, one2one=True)
In [34]:
# ports
Inputs = [cirq.Port(name="In{}".format(k+1), domain=fm, direction="in") for k in range(2)]
Outputs = [cirq.Port(name="Out{}".format(k+1), domain=fm, direction="out") for k in range(2)]
Inputs, Outputs
Out[34]:
In [35]:
# components
CBeamsplitter = cirq.ComponentType(name="Beamsplitter", ports=cirq.clone_ports(Inputs+Outputs), params=["theta"])
CKerr = cirq.ComponentType(name="KerrCavity", ports=cirq.clone_ports(Inputs+Outputs), params=["kappa_1", "kappa_2", "Delta", "chi"])
CDisplace = cirq.ComponentType(name="Displace", ports=cirq.clone_ports([Inputs[0], Outputs[0]]), params=["alpha"])
In [36]:
# Circuit editor
kerr_amp = cirq.CircuitBuilder([fm], [CBeamsplitter, CKerr, CDisplace], cirq.Circuit(name="KerrAmplifier"))
kerr_amp
In [43]:
methods = {
CBeamsplitter: Beamsplitter,
CKerr: TwoPortKerrCavity,
CDisplace: Displace,
}
def translate_to_QNET(circuit):
"""
Convert cirq-type Circuit model into QNET circuit algebra expression.
Works for all registered component models in the dict `methods`
Creates the circuit expression by concatenating all individual models together
and then applying successive feedbacks for each internal connection.
Finally computes input and output channel permutation objects to achieve
that the desired external inputs and outputs are the first inputs and output
ports of the circuit algebra object (in correct order).
"""
all_inputs = []
all_outputs = []
components = []
Q = ca.cid(0)
# build concatenated lists of component inputs and outputs
# as well as the combined concatenated circuit objects
for c in circuit.component_instances:
all_inputs += filter(lambda p: p.direction == "in", c.ports)
all_outputs += filter(lambda p: p.direction == "out", c.ports)
Q = Q + methods[c.ctype](c.name, **c.param_assignments)
# find external and internal connections
ext_connections = filter(lambda c: c.source.is_ext or c.target.is_ext, circuit.connections)
int_connections = filter(lambda c: not (c.source.is_ext or c.target.is_ext), circuit.connections)
for cc in int_connections:
# find correct indices for feedback from output kk_s to input kk_t of Q
kk_s = all_outputs.index(cc.source)
kk_t = all_inputs.index(cc.target)
Q = Q.feedback(kk_s, kk_t)
# update inputs and outputs
all_outputs.pop(kk_s)
all_inputs.pop(kk_t)
# remaining number of channels
n = Q.cdim
# external inputs and outputs in original order
ext_inputs = filter(lambda p: p.direction == "in", circuit.ports)
ext_outputs = filter(lambda p: p.direction == "out", circuit.ports)
# create input mapping and inverse output mapping
imap = {}
omap = {}
for kk, p in enumerate(ext_inputs):
imap[kk] = all_inputs.index(p.connections_out[0].target)
for kk, p in enumerate(ext_outputs):
omap[kk] = all_outputs.index(p.connections_in[0].source)
# wrap circuit in channel permuting objects.
Q = ca.map_signals_circuit(omap, n).series_inverse() << Q << ca.map_signals_circuit(imap, n)
return Q
In [45]:
Qres = translate_to_QNET(kerr_amp.circuit)
In [57]:
Qres
Out[57]:
In [48]:
Qres.show()
In [49]:
Qres.creduce().show()
In [53]:
QSLH = Qres.toSLH()
QSLH
Out[53]:
In [52]:
aK1, aK2 = map(ca.Destroy, sorted(Qres.space.local_factors()))
aK1, aK2
Out[52]:
In [58]:
noises = [ca.OperatorSymbol("eta_{}".format(kk), "noise_{{{}}}".format(kk)) for kk in range(1, Qres.cdim + 1)]
In [59]:
QSLH.symbolic_heisenberg_eom(aK1, noises=noises).expand().simplify_scalar()
Out[59]:
In [60]:
QSLH.symbolic_heisenberg_eom(aK2, noises=noises).expand().simplify_scalar()
Out[60]:
In [ ]: