Models and Model Factories

Models and Model Factories#

Default SAX Models

import sax

Simple Models#

sax.models.straight()
{('in0', 'out0'): Array(0.82076344+0.57126822j, dtype=complex128),
 ('out0', 'in0'): Array(0.82076344+0.57126822j, dtype=complex128)}
sax.models.coupler()
{('in0', 'out0'): 0.7071067811865476,
 ('in0', 'out1'): 0.7071067811865476j,
 ('in1', 'out0'): 0.7071067811865476j,
 ('in1', 'out1'): 0.7071067811865476,
 ('out0', 'in0'): 0.7071067811865476,
 ('out1', 'in0'): 0.7071067811865476j,
 ('out0', 'in1'): 0.7071067811865476j,
 ('out1', 'in1'): 0.7071067811865476}

Model Factories#

A unitary model returns an SCoo by default:

unitary_model = sax.models.unitary(2, 2)
unitary_model()  # a unitary model returns an SCoo by default
(Array([0, 0, 1, 1, 2, 2, 3, 3], dtype=int64),
 Array([2, 3, 2, 3, 0, 1, 0, 1], dtype=int64),
 Array([0.70710678, 0.70710678, 0.70710678, 0.70710678, 0.70710678,
        0.70710678, 0.70710678, 0.70710678], dtype=float64),
 {'in0': Array(0, dtype=int64, weak_type=True),
  'in1': Array(1, dtype=int64, weak_type=True),
  'out0': Array(2, dtype=int64, weak_type=True),
  'out1': Array(3, dtype=int64, weak_type=True)})

As you probably already know, it’s very easy to convert a model returning any Stype into a model returning an SDict as follows:

unitary_sdict_model = sax.sdict(unitary_model)
unitary_sdict_model()
{('in0', 'out0'): Array(0.70710678, dtype=float64),
 ('in0', 'out1'): Array(0.70710678, dtype=float64),
 ('in1', 'out0'): Array(0.70710678, dtype=float64),
 ('in1', 'out1'): Array(0.70710678, dtype=float64),
 ('out0', 'in0'): Array(0.70710678, dtype=float64),
 ('out0', 'in1'): Array(0.70710678, dtype=float64),
 ('out1', 'in0'): Array(0.70710678, dtype=float64),
 ('out1', 'in1'): Array(0.70710678, dtype=float64)}

If we need custom port names, we can also just specify them explicitly:

unitary_model = sax.models.unitary(ports=("in0", "in1", "out0", "out1"))
unitary_model()
(Array([0, 0, 1, 1, 2, 2, 3, 3], dtype=int64),
 Array([2, 3, 2, 3, 0, 1, 0, 1], dtype=int64),
 Array([0.70710678, 0.70710678, 0.70710678, 0.70710678, 0.70710678,
        0.70710678, 0.70710678, 0.70710678], dtype=float64),
 {'in0': Array(0, dtype=int64, weak_type=True),
  'in1': Array(1, dtype=int64, weak_type=True),
  'out0': Array(2, dtype=int64, weak_type=True),
  'out1': Array(3, dtype=int64, weak_type=True)})

A unitary model will by default split a signal at an input port equally over all output ports. However, if there are an equal number of input ports as output ports we can in stead create a passthru by setting the diagonal flag to True:

passthru_model = sax.models.unitary(2, 2, diagonal=True)
sax.sdict(passthru_model())
{('in0', 'out0'): Array(1., dtype=float64),
 ('in1', 'out1'): Array(1., dtype=float64),
 ('out0', 'in0'): Array(1., dtype=float64),
 ('out1', 'in1'): Array(1., dtype=float64)}
ports_in = ["in0"]
ports_out = ["out0", "out1", "out2", "out3", "out4"]
model = sax.models.unitary(ports=tuple(ports_in + ports_out), jit=True, reciprocal=True)
model = sax.sdict(model)
model()
{('in0', 'out0'): Array(0.4472136, dtype=float64),
 ('in0', 'out1'): Array(0.4472136, dtype=float64),
 ('in0', 'out2'): Array(0.4472136, dtype=float64),
 ('in0', 'out3'): Array(0.4472136, dtype=float64),
 ('in0', 'out4'): Array(0.4472136, dtype=float64),
 ('out0', 'in0'): Array(0.4472136, dtype=float64),
 ('out1', 'in0'): Array(0.4472136, dtype=float64),
 ('out2', 'in0'): Array(0.4472136, dtype=float64),
 ('out3', 'in0'): Array(0.4472136, dtype=float64),
 ('out4', 'in0'): Array(0.4472136, dtype=float64)}

Because this is a pretty common usecase we have a dedicated model factory for this as well. This passthru component just takes the number of links ('in{i}' -> 'out{i]') as input. Alternatively, as before, one can also specify the port names directly but one needs to ensure that len(ports) == 2*num_links.

passthru_model = sax.passthru(3)
passthru_sdict_model = sax.sdict(passthru_model)
passthru_sdict_model()
{('in0', 'out0'): Array(1., dtype=float64),
 ('in1', 'out1'): Array(1., dtype=float64),
 ('in2', 'out2'): Array(1., dtype=float64),
 ('out0', 'in0'): Array(1., dtype=float64),
 ('out1', 'in1'): Array(1., dtype=float64),
 ('out2', 'in2'): Array(1., dtype=float64)}
mzi, _ = sax.circuit(
    netlist={
        "instances": {
            "lft": "u12",
            "top": "u11",
            "rgt": "u12",
        },
        "connections": {
            "lft,out0": "rgt,out0",
            "lft,out1": "top,in0",
            "top,out0": "rgt,out1",
        },
        "ports": {
            "in0": "lft,in0",
            "out0": "rgt,in0",
        },
    },
    models={
        "u12": sax.models.unitary(1, 2),
        "u11": sax.models.unitary(1, 1),
    },
)
mzi()
{('in0', 'in0'): Array(0.+0.j, dtype=complex128),
 ('in0', 'out0'): Array(1.+0.j, dtype=complex128),
 ('out0', 'in0'): Array(1.+0.j, dtype=complex128),
 ('out0', 'out0'): Array(0.+0.j, dtype=complex128)}

A copier model is like a unitary model, but copies the input signal over all output signals. Hence, if the model has multiple output ports, this model can be considered to introduce gain. That said, it can sometimes be a useful component.

copier_model = sax.models.copier(2, 2)
copier_model()  # a copier model returns an SCoo by default
(Array([0, 0, 1, 1, 2, 2, 3, 3], dtype=int64),
 Array([2, 3, 2, 3, 0, 1, 0, 1], dtype=int64),
 Array([1., 1., 1., 1., 1., 1., 1., 1.], dtype=float64),
 {'in0': Array(0, dtype=int64, weak_type=True),
  'in1': Array(1, dtype=int64, weak_type=True),
  'out0': Array(2, dtype=int64, weak_type=True),
  'out1': Array(3, dtype=int64, weak_type=True)})

As you probably already know, it’s very easy to convert a model returning any Stype into a model returning an SDict as follows:

copier_sdict_model = sax.sdict(copier_model)
copier_sdict_model()
{('in0', 'out0'): Array(1., dtype=float64),
 ('in0', 'out1'): Array(1., dtype=float64),
 ('in1', 'out0'): Array(1., dtype=float64),
 ('in1', 'out1'): Array(1., dtype=float64),
 ('out0', 'in0'): Array(1., dtype=float64),
 ('out0', 'in1'): Array(1., dtype=float64),
 ('out1', 'in0'): Array(1., dtype=float64),
 ('out1', 'in1'): Array(1., dtype=float64)}

If we need custom port names, we can also just specify them explicitly:

copier_model = sax.models.copier(ports=("in0", "in1", "out0", "out1"))
copier_model()
(Array([0, 0, 1, 1, 2, 2, 3, 3], dtype=int64),
 Array([2, 3, 2, 3, 0, 1, 0, 1], dtype=int64),
 Array([1., 1., 1., 1., 1., 1., 1., 1.], dtype=float64),
 {'in0': Array(0, dtype=int64, weak_type=True),
  'in1': Array(1, dtype=int64, weak_type=True),
  'out0': Array(2, dtype=int64, weak_type=True),
  'out1': Array(3, dtype=int64, weak_type=True)})
ports_in = ["in0"]
ports_out = ["out0", "out1", "out2", "out3", "out4"]
model = sax.models.unitary(ports=tuple(ports_in + ports_out), jit=True, reciprocal=True)
model = sax.sdict(model)
model()
{('in0', 'out0'): Array(0.4472136, dtype=float64),
 ('in0', 'out1'): Array(0.4472136, dtype=float64),
 ('in0', 'out2'): Array(0.4472136, dtype=float64),
 ('in0', 'out3'): Array(0.4472136, dtype=float64),
 ('in0', 'out4'): Array(0.4472136, dtype=float64),
 ('out0', 'in0'): Array(0.4472136, dtype=float64),
 ('out1', 'in0'): Array(0.4472136, dtype=float64),
 ('out2', 'in0'): Array(0.4472136, dtype=float64),
 ('out3', 'in0'): Array(0.4472136, dtype=float64),
 ('out4', 'in0'): Array(0.4472136, dtype=float64)}

Because this is a pretty common usecase we have a dedicated model factory for this as well. This passthru component just takes the number of links ('in{i}' -> 'out{i]') as input. Alternatively, as before, one can also specify the port names directly but one needs to ensure that len(ports) == 2*num_links.

passthru_model = sax.models.passthru(3)
passthru_sdict_model = sax.sdict(passthru_model)
passthru_sdict_model()
{('in0', 'out0'): Array(1., dtype=float64),
 ('in1', 'out1'): Array(1., dtype=float64),
 ('in2', 'out2'): Array(1., dtype=float64),
 ('out0', 'in0'): Array(1., dtype=float64),
 ('out1', 'in1'): Array(1., dtype=float64),
 ('out2', 'in2'): Array(1., dtype=float64)}

All Models#

sax.models.models
{'copier': <functools._lru_cache_wrapper at 0x7f47abc505c0>,
 'coupler': <function sax.models.coupler(*, coupling: 'float' = 0.5) -> 'SDict'>,
 'passthru': <functools._lru_cache_wrapper at 0x7f47abc507d0>,
 'straight': <function sax.models.straight(*, wl: 'FloatArrayND | float' = 1.55, wl0: 'float' = 1.55, neff: 'float' = 2.34, ng: 'float' = 3.4, length: 'float' = 10.0, loss: 'float' = 0.0) -> 'SDict'>,
 'unitary': <functools._lru_cache_wrapper at 0x7f47abed3cc0>}