{ "cells": [ { "cell_type": "markdown", "id": "2c9c99fe", "metadata": { "papermill": { "duration": 0.034874, "end_time": "2024-06-17T18:16:22.605956", "exception": false, "start_time": "2024-06-17T18:16:22.571082", "status": "completed" }, "tags": [] }, "source": [ "# Circuit\n", "\n", "> SAX Circuits" ] }, { "cell_type": "code", "execution_count": null, "id": "666ac6a3-e9d7-424d-a85e-7c9b74e7ec58", "metadata": { "papermill": { "duration": 1.809369, "end_time": "2024-06-17T18:16:24.440303", "exception": true, "start_time": "2024-06-17T18:16:22.630934", "status": "failed" }, "tags": [] }, "outputs": [], "source": [ "from functools import partial\n", "\n", "import sax\n", "from sax.circuit import (\n", " _create_dag,\n", " _find_leaves,\n", " _find_root,\n", " _flat_circuit,\n", " _validate_models,\n", " draw_dag,\n", ")" ] }, { "cell_type": "markdown", "id": "45ad2306-1ead-4b8c-b91e-c02ce4569b3a", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "Let's start by creating a simple recursive netlist with gdsfactory.\n", "\n", ":::{note}\n", "We are using gdsfactory to create our netlist because it allows us to see the circuit we want to simulate and because we're striving to have a compatible netlist implementation in SAX.\n", "\n", "However... gdsfactory is not a dependency of SAX. You can also define your circuits by hand (see [SAX Quick Start](../examples/01_quick_start.ipynb) or you can use another tool to programmatically construct your netlists.\n", ":::" ] }, { "cell_type": "code", "execution_count": null, "id": "9fd69e3a-9854-4884-afb3-7ddcd4631c2e", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "import gdsfactory as gf\n", "from IPython.display import display\n", "from gdsfactory.components import mzi\n", "\n", "\n", "@gf.cell\n", "def twomzi():\n", " c = gf.Component()\n", "\n", " # instances\n", " mzi1 = mzi(delta_length=10)\n", " mzi2 = mzi(delta_length=20)\n", "\n", " # references\n", " mzi1_ = c.add_ref(mzi1, name=\"mzi1\")\n", " mzi2_ = c.add_ref(mzi2, name=\"mzi2\")\n", "\n", " # connections\n", " mzi2_.connect(\"o1\", mzi1_.ports[\"o2\"])\n", "\n", " # ports\n", " c.add_port(\"o1\", port=mzi1_.ports[\"o1\"])\n", " c.add_port(\"o2\", port=mzi2_.ports[\"o2\"])\n", " return c" ] }, { "cell_type": "code", "execution_count": null, "id": "b0cad9f5-81b3-448a-8860-ac409c2b1415", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "comp = twomzi()\n", "comp" ] }, { "cell_type": "code", "execution_count": null, "id": "65ef3413-4259-4d1f-ae65-891e968d2f57", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "recnet = sax.RecursiveNetlist.parse_obj(comp.get_netlist(recursive=True))\n", "mzi1_comp = recnet.root[\"twomzi\"].instances[\"mzi1\"].component\n", "flatnet = recnet.root[mzi1_comp]" ] }, { "cell_type": "markdown", "id": "2b2a38fb-c83e-4b71-b195-ecca485fb0a3", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "To be able to model this device we'll need some SAX dummy models:" ] }, { "cell_type": "code", "execution_count": null, "id": "2035cd9e-c65e-45b6-bd83-4ad19e4630fa", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "def bend_euler(\n", " angle=90.0,\n", " p=0.5,\n", " # cross_section=\"strip\",\n", " # direction=\"ccw\",\n", " # with_bbox=True,\n", " # with_arc_floorplan=True,\n", " # npoints=720,\n", "):\n", " return sax.reciprocal({(\"o1\", \"o2\"): 1.0})" ] }, { "cell_type": "code", "execution_count": null, "id": "192c4288-20d1-4aca-b3d0-277db6bb5118", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "def mmi1x2(\n", " width=0.5,\n", " width_taper=1.0,\n", " length_taper=10.0,\n", " length_mmi=5.5,\n", " width_mmi=2.5,\n", " gap_mmi=0.25,\n", " # cross_section= strip,\n", " # taper= {function= taper},\n", " # with_bbox= True,\n", "):\n", " return sax.reciprocal(\n", " {\n", " (\"o1\", \"o2\"): 0.45**0.5,\n", " (\"o1\", \"o3\"): 0.45**0.5,\n", " }\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "861a5b4a-d8d9-4ea0-a733-39e213a28ead", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "def mmi2x2(\n", " width=0.5,\n", " width_taper=1.0,\n", " length_taper=10.0,\n", " length_mmi=5.5,\n", " width_mmi=2.5,\n", " gap_mmi=0.25,\n", " # cross_section= strip,\n", " # taper= {function= taper},\n", " # with_bbox= True,\n", "):\n", " return sax.reciprocal(\n", " {\n", " (\"o1\", \"o3\"): 0.45**0.5,\n", " (\"o1\", \"o4\"): 1j * 0.45**0.5,\n", " (\"o2\", \"o3\"): 1j * 0.45**0.5,\n", " (\"o2\", \"o4\"): 0.45**0.5,\n", " }\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "84046c8e-45a7-4aa0-bbdf-c82cd2d0e24e", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "def straight(\n", " length=0.01,\n", " # npoints=2,\n", " # with_bbox=True,\n", " # cross_section=...\n", "):\n", " return sax.reciprocal({(\"o1\", \"o2\"): 1.0})" ] }, { "cell_type": "markdown", "id": "8efe90e4-dfe6-40bd-aa0a-26c74738d688", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "In SAX, we usually aggregate the available models in a models dictionary:" ] }, { "cell_type": "code", "execution_count": null, "id": "c1ac9132-0ee2-443b-98f4-f790234551b0", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "models = {\n", " \"straight\": straight,\n", " \"bend_euler\": bend_euler,\n", " \"mmi1x2\": mmi1x2,\n", "}" ] }, { "cell_type": "markdown", "id": "05c6a808-1fbf-446c-badd-03a36c76e438", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "We can also create some dummy multimode models:" ] }, { "cell_type": "code", "execution_count": null, "id": "b749320e-5a86-4c05-9736-89936b0d31b3", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "def bend_euler_mm(\n", " angle=90.0,\n", " p=0.5,\n", " # cross_section=\"strip\",\n", " # direction=\"ccw\",\n", " # with_bbox=True,\n", " # with_arc_floorplan=True,\n", " # npoints=720,\n", "):\n", " return sax.reciprocal(\n", " {\n", " (\"o1@TE\", \"o2@TE\"): 0.9**0.5,\n", " # ('o1@TE', 'o2@TM'): 0.01**0.5,\n", " # ('o1@TM', 'o2@TE'): 0.01**0.5,\n", " (\"o1@TM\", \"o2@TM\"): 0.8**0.5,\n", " }\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "958054fb-31f0-4d2c-9304-fdb72af8f333", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "def mmi1x2_mm(\n", " width=0.5,\n", " width_taper=1.0,\n", " length_taper=10.0,\n", " length_mmi=5.5,\n", " width_mmi=2.5,\n", " gap_mmi=0.25,\n", " # cross_section= strip,\n", " # taper= {function= taper},\n", " # with_bbox= True,\n", "):\n", " return sax.reciprocal(\n", " {\n", " (\"o1@TE\", \"o2@TE\"): 0.45**0.5,\n", " (\"o1@TE\", \"o3@TE\"): 0.45**0.5,\n", " (\"o1@TM\", \"o2@TM\"): 0.41**0.5,\n", " (\"o1@TM\", \"o3@TM\"): 0.41**0.5,\n", " (\"o1@TE\", \"o2@TM\"): 0.01**0.5,\n", " (\"o1@TM\", \"o2@TE\"): 0.01**0.5,\n", " (\"o1@TE\", \"o3@TM\"): 0.02**0.5,\n", " (\"o1@TM\", \"o3@TE\"): 0.02**0.5,\n", " }\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "34bad3c0-34a4-4b35-a942-7f75a204c1d0", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "def straight_mm(\n", " length=0.01,\n", " # npoints=2,\n", " # with_bbox=True,\n", " # cross_section=...\n", "):\n", " return sax.reciprocal(\n", " {\n", " (\"o1@TE\", \"o2@TE\"): 1.0,\n", " (\"o1@TM\", \"o2@TM\"): 1.0,\n", " }\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "b9476eb5-94e0-45c3-8e7e-a82fead8807b", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "models_mm = {\n", " \"straight\": straight_mm,\n", " \"bend_euler\": bend_euler_mm,\n", " \"mmi1x2\": mmi1x2_mm,\n", "}" ] }, { "cell_type": "markdown", "id": "3df7d9b6-acbb-4406-a3c8-8a9ad09db768", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "We can now represent our recursive netlist model as a Directed Acyclic Graph:" ] }, { "cell_type": "code", "execution_count": null, "id": "b6d6e06e-b501-4d3e-8eb6-0d7117abf8e8", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "dag = _create_dag(recnet, models)\n", "draw_dag(dag)" ] }, { "cell_type": "markdown", "id": "d081c64c-c42c-467e-885d-6e8db2cf64cc", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "Note that the DAG depends on the models we supply. We could for example stub one of the sub-netlists by a pre-defined model:" ] }, { "cell_type": "code", "execution_count": null, "id": "ebb11e68-ecc3-4d00-9870-b6682e06c39f", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "dag_ = _create_dag(recnet, {**models, \"mzi_delta_length10\": mmi2x2})\n", "draw_dag(dag_, with_labels=True)" ] }, { "cell_type": "markdown", "id": "b68c2f1a-f062-4f8a-89a5-8608524a5c06", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "This is useful if we for example pre-calculated a certain model." ] }, { "cell_type": "markdown", "id": "020381f6-872f-4aea-8822-ef1096553dc2", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "We can easily find the root of the DAG:" ] }, { "cell_type": "code", "execution_count": null, "id": "11728146-6b54-48f2-acac-c8a5fa4e3b0f", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "_find_root(dag)" ] }, { "cell_type": "markdown", "id": "4e8065d2-19c8-444a-b1c2-ea3c62f90e0a", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "Similarly we can find the leaves:" ] }, { "cell_type": "code", "execution_count": null, "id": "7fa9afff-26b5-44cb-90bf-a32ef5e95bf5", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "_find_leaves(dag)" ] }, { "cell_type": "markdown", "id": "59fb221d-951d-44df-a5d9-487a8b4dc4f8", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "To be able to simulate the circuit, we need to supply a model for each of the leaves in the dependency DAG. Let's write a validator that checks this" ] }, { "cell_type": "code", "execution_count": null, "id": "a97dbe93-a53f-4525-b721-2f329692cdc9", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "models = _validate_models(models, dag)" ] }, { "cell_type": "markdown", "id": "e9f30a86-aa00-4f51-8b93-3440edc0b46b", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "We can now dow a bottom-up simulation. Since at the bottom of the DAG, our circuit is always flat (i.e. not hierarchical) we can implement a minimal `_flat_circuit` definition, which only needs to work on a flat (non-hierarchical circuit):" ] }, { "cell_type": "code", "execution_count": null, "id": "1f415c32-70e4-4b40-a9e2-051fc187cbb9", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "flatnet = recnet.root[mzi1_comp]\n", "single_mzi = _flat_circuit(\n", " flatnet.instances,\n", " flatnet.connections,\n", " flatnet.ports,\n", " models,\n", " \"default\",\n", ")\n", "single_mzi()" ] }, { "cell_type": "markdown", "id": "c4f3375c-baa3-433f-924a-eff50557bf9f", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "The resulting circuit is just another SAX model (i.e. a python function) returing an SType:" ] }, { "cell_type": "code", "execution_count": null, "id": "058740c4-439b-4b00-91e7-15e3e7b6679c", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "?single_mzi" ] }, { "cell_type": "markdown", "id": "2b2bbe9f-29ac-413a-b0a4-6c51da8689cb", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "Let's 'execute' the circuit:" ] }, { "cell_type": "markdown", "id": "42a86f56-ab16-4c04-8738-2480ad378008", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "Note that we can also supply multimode models:" ] }, { "cell_type": "code", "execution_count": null, "id": "87bc9f40-e97c-4505-8362-311ce5770077", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "flatnet = recnet.root[mzi1_comp]\n", "single_mzi = _flat_circuit(\n", " flatnet.instances,\n", " flatnet.connections,\n", " flatnet.ports,\n", " models_mm,\n", " \"default\",\n", ")\n", "single_mzi()" ] }, { "cell_type": "markdown", "id": "c852f384-c830-47f5-aa75-ee274abbcc35", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "Now that we can handle flat circuits the extension to hierarchical circuits is not so difficult:" ] }, { "cell_type": "markdown", "id": "10321c72-bc74-453b-a87a-d5f89295217b", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "single mode simulation:" ] }, { "cell_type": "code", "execution_count": null, "id": "6d133b49-6f26-4e01-bcd9-7ca1b76dc55d", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "double_mzi, info = sax.circuit(recnet, models, backend=\"default\")\n", "double_mzi()" ] }, { "cell_type": "markdown", "id": "3f332edd-909e-452b-b7bd-3a68719a5c20", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "multi mode simulation:" ] }, { "cell_type": "code", "execution_count": null, "id": "277ec069-4b46-4d5e-9580-1dd0c28cd9a6", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "double_mzi, info = sax.circuit(\n", " recnet, models_mm, backend=\"default\", return_type=\"sdict\"\n", ")\n", "double_mzi()" ] }, { "cell_type": "markdown", "id": "e621a2e3-6918-4cfb-8a55-4cc754356020", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "source": [ "sometimes it's useful to get the required circuit model names to be able to create the circuit:" ] }, { "cell_type": "code", "execution_count": null, "id": "99bb5c8f-8094-426d-a5d6-ceb73d80f706", "metadata": { "papermill": { "duration": null, "end_time": null, "exception": null, "start_time": null, "status": "pending" }, "tags": [] }, "outputs": [], "source": [ "sax.get_required_circuit_models(recnet, models)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }