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]:
([In1, In2], [Out1, Out2])

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]:
${\mathbf{P}_\sigma \begin{pmatrix} 0 & 1 & 2 & 3 \\ 0 & 3 & 1 & 2 \end{pmatrix}} \lhd {\left\lfloor\left(\mathbf{1}_{3} \boxplus {\rm K2}\right) \lhd \left((\left({\rm B2} \boxplus \mathbf{1}_{2}\right) \lhd \left(\mathbf{1}_{1} \boxplus (\left({\rm K1} \boxplus \mathbf{1}_{1}\right) \lhd {\mathbf{P}_\sigma \begin{pmatrix} 0 & 1 & 2 \\ 0 & 2 & 1 \end{pmatrix}} \lhd \left({\rm B1} \boxplus \mathbf{1}_{1}\right))\right)) \boxplus \mathbf{1}_{1}\right)\right\rfloor_{3\to0}} \lhd \left({W(\alpha)} \boxplus \mathbf{1}_{3}\right) \lhd {\mathbf{P}_\sigma \begin{pmatrix} 0 & 1 & 2 & 3 \\ 1 & 2 & 3 & 0 \end{pmatrix}}$

In [48]:
Qres.show()



In [49]:
Qres.creduce().show()


Ready to do symbolic/analytic work with this model


In [53]:
QSLH = Qres.toSLH()
QSLH


Out[53]:
$\left( \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1\end{pmatrix}, \begin{pmatrix} - \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {a_{{{\rm K1}}}} + \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {a_{{{\rm K2}}}} \\ \sqrt{\kappa_{2}} {a_{{{\rm K1}}}} \\ \sqrt{\kappa_{2}} {a_{{{\rm K2}}}} \\ \alpha + \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {a_{{{\rm K1}}}} + \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {a_{{{\rm K2}}}}\end{pmatrix}, \frac{i}{2} \left( - \alpha \left( \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {a_{{{\rm K1}}}^\dagger} + \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {a_{{{\rm K2}}}^\dagger}\right) + \overline{\alpha} \left( \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {a_{{{\rm K1}}}} + \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {a_{{{\rm K2}}}}\right)\right) + \chi {a_{{{\rm K1}}}^\dagger} {a_{{{\rm K1}}}^\dagger} {a_{{{\rm K1}}}} {a_{{{\rm K1}}}} + \Delta {a_{{{\rm K1}}}^\dagger} {a_{{{\rm K1}}}} + \chi {a_{{{\rm K2}}}^\dagger} {a_{{{\rm K2}}}^\dagger} {a_{{{\rm K2}}}} {a_{{{\rm K2}}}} + \Delta {a_{{{\rm K2}}}^\dagger} {a_{{{\rm K2}}}} \right)$

In [52]:
aK1, aK2 = map(ca.Destroy, sorted(Qres.space.local_factors()))
aK1, aK2


Out[52]:
(Destroy(LocalSpace(u'K1', '')), Destroy(LocalSpace(u'K2', '')))

Heisenberg picture QSDEs for cavity mode operators


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]:
$ - \frac{\sqrt{2} \alpha}{2} \sqrt{\kappa_{1}} - \left(i \Delta + \frac{\kappa_{1}}{2} + \frac{\kappa_{2}}{2}\right) {a_{{{\rm K1}}}} + \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {\eta}_1 - \sqrt{\kappa_{2}} {\eta}_2 - \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {\eta}_4 - 2 i \chi {a_{{{\rm K1}}}^\dagger} {a_{{{\rm K1}}}} {a_{{{\rm K1}}}}$

In [60]:
QSLH.symbolic_heisenberg_eom(aK2, noises=noises).expand().simplify_scalar()


Out[60]:
$ - \frac{\sqrt{2} \alpha}{2} \sqrt{\kappa_{1}} - \left(i \Delta + \frac{\kappa_{1}}{2} + \frac{\kappa_{2}}{2}\right) {a_{{{\rm K2}}}} - \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {\eta}_1 - \sqrt{\kappa_{2}} {\eta}_3 - \frac{\sqrt{2} \sqrt{\kappa_{1}}}{2} {\eta}_4 - 2 i \chi {a_{{{\rm K2}}}^\dagger} {a_{{{\rm K2}}}} {a_{{{\rm K2}}}}$

In [ ]: