├── .gitattributes ├── .gitignore ├── README.md ├── Training Deep Neural Networks for the Inverse Design of Nanophotonic Structures.pptx ├── cy_funcs.pyx ├── environment-gpu.yml ├── environment.yml ├── forward_model.ipynb ├── forward_model ├── Arch4_Epochs1000_Adam0001_Sigmoid.h5 ├── Arch4_Epochs2500_Adam0001_Sigmoid.h5 ├── Arch4_Epochs4000_Adam0001_Sigmoid.h5 ├── Arch4_Epochs400_Adam0001_Sigmoid.h5 └── Arch4_Epochs6000_Adam0001_Sigmoid.h5 ├── grating.py ├── inverse_model.ipynb ├── inverse_model ├── Epochs400_Adam0001_Sigmoid.h5 ├── InverseNet15EpochsTandem.h5 └── TandemNN_Epochs400_Adam0001_Sigmoid.h5 ├── produce_data.ipynb ├── produce_data.py ├── py_funcs.py ├── requirements.txt └── test_grating.py /.gitattributes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/.gitattributes -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/usage.statistics.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | 13 | # Generated files 14 | .idea/**/contentModel.xml 15 | 16 | # Sensitive or high-churn files 17 | .idea/**/dataSources/ 18 | .idea/**/dataSources.ids 19 | .idea/**/dataSources.local.xml 20 | .idea/**/sqlDataSources.xml 21 | .idea/**/dynamic.xml 22 | .idea/**/uiDesigner.xml 23 | .idea/**/dbnavigator.xml 24 | 25 | # Gradle 26 | .idea/**/gradle.xml 27 | .idea/**/libraries 28 | 29 | # Gradle and Maven with auto-import 30 | # When using Gradle or Maven with auto-import, you should exclude module files, 31 | # since they will be recreated, and may cause churn. Uncomment if using 32 | # auto-import. 33 | # .idea/artifacts 34 | # .idea/compiler.xml 35 | # .idea/jarRepositories.xml 36 | # .idea/modules.xml 37 | # .idea/*.iml 38 | # .idea/modules 39 | # *.iml 40 | # *.ipr 41 | 42 | # CMake 43 | cmake-build-*/ 44 | 45 | # Mongo Explorer plugin 46 | .idea/**/mongoSettings.xml 47 | 48 | # File-based project format 49 | *.iws 50 | 51 | # IntelliJ 52 | out/ 53 | 54 | # mpeltonen/sbt-idea plugin 55 | .idea_modules/ 56 | 57 | # JIRA plugin 58 | atlassian-ide-plugin.xml 59 | 60 | # Cursive Clojure plugin 61 | .idea/replstate.xml 62 | 63 | # Crashlytics plugin (for Android Studio and IntelliJ) 64 | com_crashlytics_export_strings.xml 65 | crashlytics.properties 66 | crashlytics-build.properties 67 | fabric.properties 68 | 69 | # Editor-based Rest Client 70 | .idea/httpRequests 71 | 72 | # Android studio 3.1+ serialized cache file 73 | .idea/caches/build_file_checksums.ser 74 | 75 | ### Python template 76 | # Byte-compiled / optimized / DLL files 77 | __pycache__/ 78 | *.py[cod] 79 | *$py.class 80 | 81 | # C extensions 82 | *.so 83 | 84 | # Distribution / packaging 85 | .Python 86 | build/ 87 | develop-eggs/ 88 | dist/ 89 | downloads/ 90 | eggs/ 91 | .eggs/ 92 | lib/ 93 | lib64/ 94 | parts/ 95 | sdist/ 96 | var/ 97 | wheels/ 98 | share/python-wheels/ 99 | *.egg-info/ 100 | .installed.cfg 101 | *.egg 102 | MANIFEST 103 | 104 | # PyInstaller 105 | # Usually these files are written by a python script from a template 106 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 107 | *.manifest 108 | *.spec 109 | 110 | # Installer logs 111 | pip-log.txt 112 | pip-delete-this-directory.txt 113 | 114 | # Unit test / coverage reports 115 | htmlcov/ 116 | .tox/ 117 | .nox/ 118 | .coverage 119 | .coverage.* 120 | .cache 121 | nosetests.xml 122 | coverage.xml 123 | *.cover 124 | *.py,cover 125 | .hypothesis/ 126 | .pytest_cache/ 127 | cover/ 128 | 129 | # Translations 130 | *.mo 131 | *.pot 132 | 133 | # Django stuff: 134 | *.log 135 | local_settings.py 136 | db.sqlite3 137 | db.sqlite3-journal 138 | 139 | # Flask stuff: 140 | instance/ 141 | .webassets-cache 142 | 143 | # Scrapy stuff: 144 | .scrapy 145 | 146 | # Sphinx documentation 147 | docs/_build/ 148 | 149 | # PyBuilder 150 | .pybuilder/ 151 | target/ 152 | 153 | # Jupyter Notebook 154 | .ipynb_checkpoints 155 | 156 | # IPython 157 | profile_default/ 158 | ipython_config.py 159 | 160 | # pyenv 161 | # For a library or package, you might want to ignore these files since the code is 162 | # intended to run in multiple environments; otherwise, check them in: 163 | # .python-version 164 | 165 | # pipenv 166 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 167 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 168 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 169 | # install all needed dependencies. 170 | #Pipfile.lock 171 | 172 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 173 | __pypackages__/ 174 | 175 | # Celery stuff 176 | celerybeat-schedule 177 | celerybeat.pid 178 | 179 | # SageMath parsed files 180 | *.sage.py 181 | 182 | # Environments 183 | .env 184 | .venv 185 | env/ 186 | venv/ 187 | ENV/ 188 | env.bak/ 189 | venv.bak/ 190 | 191 | # Spyder project settings 192 | .spyderproject 193 | .spyproject 194 | 195 | # Rope project settings 196 | .ropeproject 197 | 198 | # mkdocs documentation 199 | /site 200 | 201 | # mypy 202 | .mypy_cache/ 203 | .dmypy.json 204 | dmypy.json 205 | 206 | # Pyre type checker 207 | .pyre/ 208 | 209 | # pytype static type analyzer 210 | .pytype/ 211 | 212 | # Cython debug symbols 213 | cython_debug/ 214 | 215 | # These are too large 216 | database.npz 217 | dataset_snapshots 218 | 219 | # ppt temp files 220 | ~$Training Deep Neural Networks for the Inverse Design of Nanophotonic Structures.pptx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Neural Network for Inverse Design in Nanophotonics 2 | ================================================== 3 | This project demonstates training a neural network for inverse design of nanophotonic gratings. 4 | The project is inspired by the [work](https://doi.org/10.1021/acsphotonics.7b01377) of Liu et al. in ACS Photonics journal. 5 | 6 | Generating Data 7 | ------------------ 8 | * To be able to run DNN training scripts, you should have dataset `dataset.npz` file in the project root folder. 9 | * If you want to use pre-generated dataset, you can download the dataset file from [here](https://drive.google.com/file/d/1D0fJ815a0pgtrE-_lObbhYUnVooeIZIe/view?usp=sharing). **Note**: The file size is ~1 GB. 10 | * In case you wish to generate the dataset from scratch, you can run either [produce_data.py](./produce_data.py) python script or [produce_data.ipynb](./produce_data.py) Jupyter notebook. 11 | 12 | Forward Model 13 | ------------- 14 | * See [forward_model.ipynb](./forward_model.ipynb) Jupyter notebook for loading, training, and saving forward model 15 | * Saved forward model states can be found in [forward_model](./forward_model/) folder. 16 | 17 | Inverse Model 18 | -------------- 19 | * See [inverse_model.ipynb](./inverse_model.ipynb) Jupyter notebook for loading, training, and saving inverse model 20 | * Saved inverse model states can be found in [inverse_model](./inverse_model/) folder. 21 | -------------------------------------------------------------------------------- /Training Deep Neural Networks for the Inverse Design of Nanophotonic Structures.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/Training Deep Neural Networks for the Inverse Design of Nanophotonic Structures.pptx -------------------------------------------------------------------------------- /cy_funcs.pyx: -------------------------------------------------------------------------------- 1 | # cython: infer_types=True 2 | import numpy as np 3 | cimport numpy as np 4 | cimport cython 5 | import scipy.linalg as spla 6 | from libc.math cimport sqrt, exp 7 | 8 | 9 | ex = np.array([1, 0]) 10 | exex = np.outer(ex, ex) 11 | ey = np.array([0, 1]) 12 | eyey = np.outer(ey, ey) 13 | I = np.eye(2) 14 | I4 = np.eye(4, dtype=np.complex128) 15 | 16 | 17 | @cython.boundscheck(False) 18 | @cython.wraparound(False) 19 | cpdef matrix_M(double eps, double b=0): 20 | M = np.empty((4, 4), dtype=np.double) 21 | cdef double[:, ::1] M_view = M 22 | M_view[:, :] = 0 23 | M_view[0, 2] = eps - b ** 2 24 | M_view[1, 3] = eps 25 | M_view[2, 0] = 1 26 | M_view[3, 1] = 1 - (b ** 2 / eps) 27 | return M 28 | 29 | 30 | @cython.boundscheck(False) 31 | @cython.wraparound(False) 32 | cpdef eigvalsM(double eps, double b=0.): 33 | cdef double n = sqrt(eps - b ** 2) 34 | return np.array([-n, -n, n, n], dtype=np.double) 35 | 36 | 37 | @cython.boundscheck(False) 38 | @cython.wraparound(False) 39 | cpdef eigvecsM(double eps, double b=0.): 40 | cdef double sqrtepsb = sqrt(eps - b ** 2) 41 | cdef double c1 = sqrtepsb 42 | cdef double c2 = eps * sqrtepsb / (b ** 2 - eps) 43 | cdef double c3 = sqrtepsb / (2 * eps) 44 | cdef double c4 = 0.5 / sqrtepsb 45 | return np.array([[[0, -c1, 0, c1], 46 | [c2, 0, -c2, 0], 47 | [0, 1, 0, 1], 48 | [1, 0, 1, 0]], 49 | [[0, -c3, 0, 0.5], 50 | [-c4, 0, 0.5, 0], 51 | [0, c3, 0, 0.5], 52 | [c4, 0, 0.5, 0]]], dtype=np.double) 53 | 54 | 55 | @cython.boundscheck(False) 56 | @cython.wraparound(False) 57 | cpdef propagator_layer(f, double eps, double d, double b=0): 58 | """Propagator, or evolution operator, or transfer matrix of 59 | a uniform layer with permittivity eps and thickness d 60 | at frequency f. 61 | """ 62 | scalar_input = np.isscalar(f) 63 | cdef double[::1] freqs = np.atleast_1d(f) 64 | cdef Py_ssize_t i, N = freqs.shape[0] 65 | cdef double complex ik0d 66 | cdef np.ndarray[dtype=np.complex_t, ndim=3] res 67 | res = np.empty((N, 4, 4), dtype=np.complex_) 68 | cdef np.ndarray[dtype=np.double_t, ndim=1] w = eigvalsM(eps, b) 69 | cdef np.ndarray[dtype=np.double_t, ndim=3] vr = eigvecsM(eps, b) 70 | for i in range(N): 71 | ik0d = 2j * np.pi * freqs[i] * d 72 | res[i, :, :] = np.linalg.multi_dot( 73 | [vr[0], np.diag(np.exp(ik0d * w)), vr[1]] 74 | ) 75 | if scalar_input: 76 | return res[0] 77 | return res 78 | 79 | 80 | @cython.boundscheck(False) 81 | @cython.wraparound(False) 82 | def propagator_grating(f, double eps1, double eps2, double[:] D, double b=0): 83 | scalar_input = np.isscalar(f) 84 | cdef size_t n, N = D.shape[0] 85 | if scalar_input: 86 | propagator = propagator_layer(f, eps1, D[0], b) 87 | for n in range(1, N): 88 | eps = eps1 if n % 2 == 0 else eps2 89 | propagator = np.dot(propagator_layer(f, eps, D[n], b), propagator) 90 | return propagator 91 | cdef np.ndarray[np.complex128_t, ndim=3] prop_layer 92 | propagator = propagator_layer(f, eps1, D[0], b) 93 | for n in range(1, N): 94 | eps = eps1 if n % 2 == 0 else eps2 95 | prop_layer = propagator_layer(f, eps, D[n], b) 96 | for i in range(propagator.shape[0]): 97 | propagator[i] = np.dot(prop_layer[i], propagator[i]) 98 | return propagator 99 | 100 | 101 | cpdef gamma(eps, b=0): 102 | return np.diag([1 / np.sqrt(eps - b ** 2), 103 | np.sqrt(eps - b ** 2) / eps]) 104 | 105 | 106 | cpdef operator_r(propagator, eps_left=1, eps_right=None, b=0): 107 | """Reflection operator of a multilayer characterized by propagator 108 | surrounded by media with eps_left at left and eps_right at right. 109 | """ 110 | if not eps_right: 111 | eps_right = eps_left 112 | gamma_left = gamma(eps_left, b) 113 | gamma_right = gamma(eps_right, b) 114 | factor1 = (np.bmat([gamma_right, -I]). 115 | dot(propagator). 116 | dot(np.bmat([I, -gamma_left]).T)) 117 | factor2 = (np.bmat([gamma_right, -I]). 118 | dot(propagator). 119 | dot(np.bmat([I, gamma_left]).T)) 120 | return spla.inv(factor1).dot(factor2) 121 | 122 | 123 | cpdef operator_t(propagator, eps_left=1, eps_right=None, b=0): 124 | """Transmission operator of a multilayer characterized by propagator 125 | surrounded by media with eps_left at left and eps_right at right. 126 | """ 127 | if not eps_right: 128 | eps_right = eps_left 129 | gamma_left = gamma(eps_left, b) 130 | gamma_right = gamma(eps_right, b) 131 | return 2 * spla.inv((np.bmat([gamma_left, I]). 132 | dot(spla.inv(propagator)). 133 | dot(np.bmat([I, gamma_right]).T))).dot(gamma_left) 134 | -------------------------------------------------------------------------------- /environment-gpu.yml: -------------------------------------------------------------------------------- 1 | name: tf-gpu 2 | channels: 3 | - defaults 4 | dependencies: 5 | - _tflow_select=2.3.0=gpu 6 | - absl-py=0.11.0=pyhd3eb1b0_1 7 | - aiohttp=3.7.3=py38h2bbff1b_1 8 | - argon2-cffi=20.1.0=py38h2bbff1b_1 9 | - astunparse=1.6.3=py_0 10 | - async-timeout=3.0.1=py38haa95532_0 11 | - async_generator=1.10=pyhd3eb1b0_0 12 | - attrs=20.3.0=pyhd3eb1b0_0 13 | - backcall=0.2.0=pyhd3eb1b0_0 14 | - blas=1.0=mkl 15 | - bleach=3.3.0=pyhd3eb1b0_0 16 | - blinker=1.4=py38haa95532_0 17 | - brotlipy=0.7.0=py38h2bbff1b_1003 18 | - ca-certificates=2021.1.19=haa95532_0 19 | - cachetools=4.2.1=pyhd3eb1b0_0 20 | - certifi=2020.12.5=py38haa95532_0 21 | - cffi=1.14.4=py38hcd4344a_0 22 | - chardet=3.0.4=py38haa95532_1003 23 | - click=7.1.2=pyhd3eb1b0_0 24 | - colorama=0.4.4=pyhd3eb1b0_0 25 | - cryptography=2.9.2=py38h7a1dbc1_0 26 | - cycler=0.10.0=py38_0 27 | - cython=0.29.21=py38hd77b12b_0 28 | - decorator=4.4.2=pyhd3eb1b0_0 29 | - defusedxml=0.6.0=pyhd3eb1b0_0 30 | - entrypoints=0.3=py38_0 31 | - freetype=2.10.4=hd328e21_0 32 | - gast=0.4.0=py_0 33 | - google-auth=1.24.0=pyhd3eb1b0_0 34 | - google-auth-oauthlib=0.4.2=pyhd3eb1b0_2 35 | - google-pasta=0.2.0=py_0 36 | - graphviz=2.38=hfd603c8_2 37 | - grpcio=1.35.0=py38hc60d5dd_0 38 | - h5py=2.10.0=py38h5e291fa_0 39 | - hdf5=1.10.4=h7ebc959_0 40 | - icc_rt=2019.0.0=h0cc432a_1 41 | - icu=58.2=ha925a31_3 42 | - idna=2.10=pyhd3eb1b0_0 43 | - importlib-metadata=2.0.0=py_1 44 | - importlib_metadata=2.0.0=1 45 | - intel-openmp=2020.2=254 46 | - ipykernel=5.3.4=py38h5ca1d4c_0 47 | - ipython=7.20.0=py38hd4e2768_1 48 | - ipython_genutils=0.2.0=pyhd3eb1b0_1 49 | - ipywidgets=7.6.3=pyhd3eb1b0_1 50 | - jedi=0.17.0=py38_0 51 | - jinja2=2.11.3=pyhd3eb1b0_0 52 | - joblib=1.0.0=pyhd3eb1b0_0 53 | - jpeg=9b=hb83a4c4_2 54 | - jsonschema=3.2.0=py_2 55 | - jupyter=1.0.0=py38_7 56 | - jupyter_client=6.1.7=py_0 57 | - jupyter_console=6.2.0=py_0 58 | - jupyter_core=4.7.1=py38haa95532_0 59 | - jupyterlab_pygments=0.1.2=py_0 60 | - jupyterlab_widgets=1.0.0=pyhd3eb1b0_1 61 | - keras-applications=1.0.8=py_1 62 | - keras-preprocessing=1.1.2=pyhd3eb1b0_0 63 | - kiwisolver=1.3.1=py38hd77b12b_0 64 | - libpng=1.6.37=h2a8f88b_0 65 | - libprotobuf=3.14.0=h23ce68f_0 66 | - libsodium=1.0.18=h62dcd97_0 67 | - libtiff=4.1.0=h56a325e_1 68 | - lz4-c=1.9.3=h2bbff1b_0 69 | - m2w64-gcc-libgfortran=5.3.0=6 70 | - m2w64-gcc-libs=5.3.0=7 71 | - m2w64-gcc-libs-core=5.3.0=7 72 | - m2w64-gmp=6.1.0=2 73 | - m2w64-libwinpthread-git=5.0.0.4634.697f757=2 74 | - markdown=3.3.3=py38haa95532_0 75 | - markupsafe=1.1.1=py38he774522_0 76 | - matplotlib=3.3.2=haa95532_0 77 | - matplotlib-base=3.3.2=py38hba9282a_0 78 | - mistune=0.8.4=py38he774522_1000 79 | - mkl=2020.2=256 80 | - mkl-service=2.3.0=py38h196d8e1_0 81 | - mkl_fft=1.2.0=py38h45dec08_0 82 | - mkl_random=1.1.1=py38h47e9c7a_0 83 | - msys2-conda-epoch=20160418=1 84 | - multidict=4.7.6=py38he774522_1 85 | - nbclient=0.5.1=py_0 86 | - nbconvert=6.0.7=py38_0 87 | - nbformat=5.1.2=pyhd3eb1b0_1 88 | - nest-asyncio=1.4.3=pyhd3eb1b0_0 89 | - notebook=6.2.0=py38haa95532_0 90 | - numpy=1.19.2=py38hadc3359_0 91 | - numpy-base=1.19.2=py38ha3acd2a_0 92 | - oauthlib=3.1.0=py_0 93 | - olefile=0.46=py_0 94 | - openssl=1.1.1i=h2bbff1b_0 95 | - opt_einsum=3.1.0=py_0 96 | - packaging=20.9=pyhd3eb1b0_0 97 | - pandoc=2.11=h9490d1a_0 98 | - pandocfilters=1.4.3=py38haa95532_1 99 | - parso=0.8.1=pyhd3eb1b0_0 100 | - pickleshare=0.7.5=pyhd3eb1b0_1003 101 | - pillow=8.1.0=py38h4fa10fc_0 102 | - pip=20.3.3=py38haa95532_0 103 | - prometheus_client=0.9.0=pyhd3eb1b0_0 104 | - prompt-toolkit=3.0.8=py_0 105 | - prompt_toolkit=3.0.8=0 106 | - protobuf=3.14.0=py38hd77b12b_1 107 | - pyasn1=0.4.8=py_0 108 | - pyasn1-modules=0.2.8=py_0 109 | - pycparser=2.20=py_2 110 | - pydot=1.4.1=py38_0 111 | - pygments=2.7.4=pyhd3eb1b0_0 112 | - pyjwt=2.0.1=py38haa95532_0 113 | - pyopenssl=20.0.1=pyhd3eb1b0_1 114 | - pyparsing=2.4.7=pyhd3eb1b0_0 115 | - pyqt=5.9.2=py38ha925a31_4 116 | - pyreadline=2.1=py38_1 117 | - pyrsistent=0.17.3=py38he774522_0 118 | - pysocks=1.7.1=py38haa95532_0 119 | - python=3.8.5=h5fd99cc_1 120 | - python-dateutil=2.8.1=pyhd3eb1b0_0 121 | - pywin32=227=py38he774522_1 122 | - pywinpty=0.5.7=py38_0 123 | - pyzmq=20.0.0=py38hd77b12b_1 124 | - qt=5.9.7=vc14h73c81de_0 125 | - qtconsole=5.0.2=pyhd3eb1b0_0 126 | - qtpy=1.9.0=py_0 127 | - requests=2.25.1=pyhd3eb1b0_0 128 | - requests-oauthlib=1.3.0=py_0 129 | - rsa=4.7=pyhd3eb1b0_1 130 | - scikit-learn=0.23.2=py38h47e9c7a_0 131 | - scipy=1.6.0=py38h14eb087_0 132 | - send2trash=1.5.0=pyhd3eb1b0_1 133 | - setuptools=52.0.0=py38haa95532_0 134 | - sip=4.19.13=py38ha925a31_0 135 | - six=1.15.0=py38haa95532_0 136 | - sqlite=3.33.0=h2a8f88b_0 137 | - tensorboard=2.3.0=pyh4dce500_0 138 | - tensorboard-plugin-wit=1.6.0=py_0 139 | - tensorflow=2.3.0=mkl_py38h8557ec7_0 140 | - tensorflow-base=2.3.0=eigen_py38h75a453f_0 141 | - tensorflow-estimator=2.3.0=pyheb71bc4_0 142 | - tensorflow-gpu=2.3.0=he13fc11_0 143 | - termcolor=1.1.0=py38_1 144 | - terminado=0.9.2=py38haa95532_0 145 | - testpath=0.4.4=pyhd3eb1b0_0 146 | - threadpoolctl=2.1.0=pyh5ca1d4c_0 147 | - tk=8.6.10=he774522_0 148 | - tornado=6.1=py38h2bbff1b_0 149 | - traitlets=5.0.5=pyhd3eb1b0_0 150 | - typing-extensions=3.7.4.3=hd3eb1b0_0 151 | - typing_extensions=3.7.4.3=pyh06a4308_0 152 | - urllib3=1.26.3=pyhd3eb1b0_0 153 | - vc=14.2=h21ff451_1 154 | - vs2015_runtime=14.27.29016=h5e58377_2 155 | - wcwidth=0.2.5=py_0 156 | - webencodings=0.5.1=py38_1 157 | - werkzeug=1.0.1=pyhd3eb1b0_0 158 | - wheel=0.36.2=pyhd3eb1b0_0 159 | - widgetsnbextension=3.5.1=py38_0 160 | - win_inet_pton=1.1.0=py38haa95532_0 161 | - wincertstore=0.2=py38_0 162 | - winpty=0.4.3=4 163 | - wrapt=1.12.1=py38he774522_1 164 | - xz=5.2.5=h62dcd97_0 165 | - yarl=1.5.1=py38he774522_0 166 | - zeromq=4.3.3=ha925a31_3 167 | - zipp=3.4.0=pyhd3eb1b0_0 168 | - zlib=1.2.11=h62dcd97_4 169 | - zstd=1.4.5=h04227a9_0 170 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: tf 2 | channels: 3 | - defaults 4 | dependencies: 5 | - _tflow_select=2.3.0=eigen 6 | - absl-py=0.11.0=pyhd3eb1b0_1 7 | - aiohttp=3.7.3=py38h2bbff1b_1 8 | - argon2-cffi=20.1.0=py38h2bbff1b_1 9 | - astunparse=1.6.3=py_0 10 | - async-timeout=3.0.1=py38haa95532_0 11 | - async_generator=1.10=pyhd3eb1b0_0 12 | - attrs=20.3.0=pyhd3eb1b0_0 13 | - backcall=0.2.0=pyhd3eb1b0_0 14 | - blas=1.0=mkl 15 | - bleach=3.3.0=pyhd3eb1b0_0 16 | - blinker=1.4=py38haa95532_0 17 | - brotlipy=0.7.0=py38h2bbff1b_1003 18 | - ca-certificates=2021.1.19=haa95532_0 19 | - cachetools=4.2.1=pyhd3eb1b0_0 20 | - certifi=2020.12.5=py38haa95532_0 21 | - cffi=1.14.4=py38hcd4344a_0 22 | - chardet=3.0.4=py38haa95532_1003 23 | - click=7.1.2=pyhd3eb1b0_0 24 | - colorama=0.4.4=pyhd3eb1b0_0 25 | - cryptography=2.9.2=py38h7a1dbc1_0 26 | - decorator=4.4.2=pyhd3eb1b0_0 27 | - defusedxml=0.6.0=pyhd3eb1b0_0 28 | - entrypoints=0.3=py38_0 29 | - gast=0.4.0=py_0 30 | - google-auth=1.24.0=pyhd3eb1b0_0 31 | - google-auth-oauthlib=0.4.2=pyhd3eb1b0_2 32 | - google-pasta=0.2.0=py_0 33 | - grpcio=1.35.0=py38hc60d5dd_0 34 | - h5py=2.10.0=py38h5e291fa_0 35 | - hdf5=1.10.4=h7ebc959_0 36 | - icc_rt=2019.0.0=h0cc432a_1 37 | - icu=58.2=ha925a31_3 38 | - idna=2.10=pyhd3eb1b0_0 39 | - importlib-metadata=2.0.0=py_1 40 | - importlib_metadata=2.0.0=1 41 | - intel-openmp=2020.2=254 42 | - ipykernel=5.3.4=py38h5ca1d4c_0 43 | - ipython=7.20.0=py38hd4e2768_1 44 | - ipython_genutils=0.2.0=pyhd3eb1b0_1 45 | - ipywidgets=7.6.3=pyhd3eb1b0_1 46 | - jedi=0.17.0=py38_0 47 | - jinja2=2.11.3=pyhd3eb1b0_0 48 | - jpeg=9b=hb83a4c4_2 49 | - jsonschema=3.2.0=py_2 50 | - jupyter=1.0.0=py38_7 51 | - jupyter_client=6.1.7=py_0 52 | - jupyter_console=6.2.0=py_0 53 | - jupyter_core=4.7.1=py38haa95532_0 54 | - jupyterlab_pygments=0.1.2=py_0 55 | - jupyterlab_widgets=1.0.0=pyhd3eb1b0_1 56 | - keras-applications=1.0.8=py_1 57 | - keras-preprocessing=1.1.2=pyhd3eb1b0_0 58 | - libpng=1.6.37=h2a8f88b_0 59 | - libprotobuf=3.14.0=h23ce68f_0 60 | - libsodium=1.0.18=h62dcd97_0 61 | - m2w64-gcc-libgfortran=5.3.0=6 62 | - m2w64-gcc-libs=5.3.0=7 63 | - m2w64-gcc-libs-core=5.3.0=7 64 | - m2w64-gmp=6.1.0=2 65 | - m2w64-libwinpthread-git=5.0.0.4634.697f757=2 66 | - markdown=3.3.3=py38haa95532_0 67 | - markupsafe=1.1.1=py38he774522_0 68 | - mistune=0.8.4=py38he774522_1000 69 | - mkl=2020.2=256 70 | - mkl-service=2.3.0=py38h196d8e1_0 71 | - mkl_fft=1.2.0=py38h45dec08_0 72 | - mkl_random=1.1.1=py38h47e9c7a_0 73 | - msys2-conda-epoch=20160418=1 74 | - multidict=4.7.6=py38he774522_1 75 | - nbclient=0.5.1=py_0 76 | - nbconvert=6.0.7=py38_0 77 | - nbformat=5.1.2=pyhd3eb1b0_1 78 | - nest-asyncio=1.4.3=pyhd3eb1b0_0 79 | - notebook=6.2.0=py38haa95532_0 80 | - numpy=1.19.2=py38hadc3359_0 81 | - numpy-base=1.19.2=py38ha3acd2a_0 82 | - oauthlib=3.1.0=py_0 83 | - openssl=1.1.1i=h2bbff1b_0 84 | - opt_einsum=3.1.0=py_0 85 | - packaging=20.9=pyhd3eb1b0_0 86 | - pandoc=2.11=h9490d1a_0 87 | - pandocfilters=1.4.3=py38haa95532_1 88 | - parso=0.8.1=pyhd3eb1b0_0 89 | - pickleshare=0.7.5=pyhd3eb1b0_1003 90 | - pip=20.3.3=py38haa95532_0 91 | - prometheus_client=0.9.0=pyhd3eb1b0_0 92 | - prompt-toolkit=3.0.8=py_0 93 | - prompt_toolkit=3.0.8=0 94 | - protobuf=3.14.0=py38hd77b12b_1 95 | - pyasn1=0.4.8=py_0 96 | - pyasn1-modules=0.2.8=py_0 97 | - pycparser=2.20=py_2 98 | - pygments=2.7.4=pyhd3eb1b0_0 99 | - pyjwt=2.0.1=py38haa95532_0 100 | - pyopenssl=20.0.1=pyhd3eb1b0_1 101 | - pyparsing=2.4.7=pyhd3eb1b0_0 102 | - pyqt=5.9.2=py38ha925a31_4 103 | - pyreadline=2.1=py38_1 104 | - pyrsistent=0.17.3=py38he774522_0 105 | - pysocks=1.7.1=py38haa95532_0 106 | - python=3.8.5=h5fd99cc_1 107 | - python-dateutil=2.8.1=pyhd3eb1b0_0 108 | - pywin32=227=py38he774522_1 109 | - pywinpty=0.5.7=py38_0 110 | - pyzmq=20.0.0=py38hd77b12b_1 111 | - qt=5.9.7=vc14h73c81de_0 112 | - qtconsole=5.0.2=pyhd3eb1b0_0 113 | - qtpy=1.9.0=py_0 114 | - requests=2.25.1=pyhd3eb1b0_0 115 | - requests-oauthlib=1.3.0=py_0 116 | - rsa=4.7=pyhd3eb1b0_1 117 | - scipy=1.6.0=py38h14eb087_0 118 | - send2trash=1.5.0=pyhd3eb1b0_1 119 | - setuptools=52.0.0=py38haa95532_0 120 | - sip=4.19.13=py38ha925a31_0 121 | - six=1.15.0=py38haa95532_0 122 | - sqlite=3.33.0=h2a8f88b_0 123 | - tensorboard=2.3.0=pyh4dce500_0 124 | - tensorboard-plugin-wit=1.6.0=py_0 125 | - tensorflow=2.3.0=mkl_py38h8c0d9a2_0 126 | - tensorflow-base=2.3.0=eigen_py38h75a453f_0 127 | - tensorflow-estimator=2.3.0=pyheb71bc4_0 128 | - termcolor=1.1.0=py38_1 129 | - terminado=0.9.2=py38haa95532_0 130 | - testpath=0.4.4=pyhd3eb1b0_0 131 | - tornado=6.1=py38h2bbff1b_0 132 | - traitlets=5.0.5=pyhd3eb1b0_0 133 | - typing-extensions=3.7.4.3=hd3eb1b0_0 134 | - typing_extensions=3.7.4.3=pyh06a4308_0 135 | - urllib3=1.26.3=pyhd3eb1b0_0 136 | - vc=14.2=h21ff451_1 137 | - vs2015_runtime=14.27.29016=h5e58377_2 138 | - wcwidth=0.2.5=py_0 139 | - webencodings=0.5.1=py38_1 140 | - werkzeug=1.0.1=pyhd3eb1b0_0 141 | - wheel=0.36.2=pyhd3eb1b0_0 142 | - widgetsnbextension=3.5.1=py38_0 143 | - win_inet_pton=1.1.0=py38haa95532_0 144 | - wincertstore=0.2=py38_0 145 | - winpty=0.4.3=4 146 | - wrapt=1.12.1=py38he774522_1 147 | - yarl=1.5.1=py38he774522_0 148 | - zeromq=4.3.3=ha925a31_3 149 | - zipp=3.4.0=pyhd3eb1b0_0 150 | - zlib=1.2.11=h62dcd97_4 151 | -------------------------------------------------------------------------------- /forward_model.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "incorporated-exhaust", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%matplotlib inline\n", 11 | "import os\n", 12 | "import numpy as np\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "import tensorflow as tf\n", 15 | "from tensorflow import keras\n", 16 | "from tensorflow.keras import layers\n", 17 | "from sklearn.model_selection import train_test_split\n", 18 | "from grating import *" 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 2, 24 | "id": "raised-interference", 25 | "metadata": {}, 26 | "outputs": [ 27 | { 28 | "name": "stdout", 29 | "output_type": "stream", 30 | "text": [ 31 | "Num GPUs Available: 0\n" 32 | ] 33 | } 34 | ], 35 | "source": [ 36 | "print(\"Num GPUs Available: \", len(tf.config.list_physical_devices('GPU')))" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 3, 42 | "id": "bulgarian-surrey", 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "%config IPCompleter.use_jedi = False" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "id": "incorrect-scotland", 52 | "metadata": {}, 53 | "source": [ 54 | "# Loading the Dataset" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 4, 60 | "id": "traditional-functionality", 61 | "metadata": {}, 62 | "outputs": [ 63 | { 64 | "name": "stdout", 65 | "output_type": "stream", 66 | "text": [ 67 | "Train set contains 588429 samples\n", 68 | "Validation set contains 65381 samples\n" 69 | ] 70 | } 71 | ], 72 | "source": [ 73 | "fname = 'dataset.npz'\n", 74 | "with np.load(fname) as data:\n", 75 | " designs = data['D']\n", 76 | " responses = data['R']\n", 77 | " \n", 78 | "n_grating_layers = designs.shape[-1]\n", 79 | "n_freqs = responses.shape[-1]\n", 80 | "Dtrain, Dtest, Rtrain, Rtest = train_test_split(designs, responses,\n", 81 | " test_size=0.1,\n", 82 | " random_state=42)\n", 83 | "print(\"Train set contains {} samples\".format(Dtrain.shape[0]))\n", 84 | "print(\"Validation set contains {} samples\".format(Dtest.shape[0]))" 85 | ] 86 | }, 87 | { 88 | "cell_type": "markdown", 89 | "id": "periodic-adelaide", 90 | "metadata": {}, 91 | "source": [ 92 | "# Initializing the Model" 93 | ] 94 | }, 95 | { 96 | "cell_type": "code", 97 | "execution_count": 5, 98 | "id": "opening-appearance", 99 | "metadata": {}, 100 | "outputs": [ 101 | { 102 | "name": "stdout", 103 | "output_type": "stream", 104 | "text": [ 105 | "Model: \"ForwardNet\"\n", 106 | "_________________________________________________________________\n", 107 | "Layer (type) Output Shape Param # \n", 108 | "=================================================================\n", 109 | "F1 (Dense) (None, 500) 8000 \n", 110 | "_________________________________________________________________\n", 111 | "F2 (Dense) (None, 200) 100200 \n", 112 | "_________________________________________________________________\n", 113 | "F3 (Dense) (None, 200) 40200 \n", 114 | "_________________________________________________________________\n", 115 | "F4 (Dense) (None, 200) 40200 \n", 116 | "_________________________________________________________________\n", 117 | "R (Dense) (None, 200) 40200 \n", 118 | "=================================================================\n", 119 | "Total params: 228,800\n", 120 | "Trainable params: 228,800\n", 121 | "Non-trainable params: 0\n", 122 | "_________________________________________________________________\n" 123 | ] 124 | } 125 | ], 126 | "source": [ 127 | "activation = keras.activations.sigmoid\n", 128 | "# Architecture 4\n", 129 | "model = keras.Sequential([layers.Input((n_grating_layers,), name='D'),\n", 130 | " layers.Dense(500, activation=activation, name='F1'),\n", 131 | " layers.Dense(200, activation=activation, name='F2'),\n", 132 | " layers.Dense(200, activation=activation, name='F3'),\n", 133 | " layers.Dense(200, activation=activation, name='F4'),\n", 134 | " layers.Dense(n_freqs, activation='sigmoid', name='R')],\n", 135 | " name='ForwardNet')\n", 136 | "model.summary()" 137 | ] 138 | }, 139 | { 140 | "cell_type": "markdown", 141 | "id": "coupled-mining", 142 | "metadata": {}, 143 | "source": [ 144 | "# Loading Weights" 145 | ] 146 | }, 147 | { 148 | "cell_type": "markdown", 149 | "id": "vietnamese-cargo", 150 | "metadata": {}, 151 | "source": [ 152 | "Let us load previous model state to continue trai" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 6, 158 | "id": "hungarian-commons", 159 | "metadata": {}, 160 | "outputs": [], 161 | "source": [ 162 | "model.load_weights(os.path.join('forward_model',\n", 163 | " 'Arch4_Epochs4000_Adam0001_Sigmoid.h5'))" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": 7, 169 | "id": "assured-crack", 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "def loss(y_true, y_pred):\n", 174 | " return n_freqs * keras.losses.mse(y_true, y_pred)\n", 175 | "\n", 176 | "model.compile(loss=loss, optimizer='adam')" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 8, 182 | "id": "protecting-comparison", 183 | "metadata": {}, 184 | "outputs": [ 185 | { 186 | "name": "stdout", 187 | "output_type": "stream", 188 | "text": [ 189 | "Epoch 4001/4001\n", 190 | "4598/4598 [==============================] - 17s 4ms/step - loss: 0.5009\n" 191 | ] 192 | } 193 | ], 194 | "source": [ 195 | "initial_epoch = 4000\n", 196 | "\n", 197 | "info = model.fit(Dtrain, Rtrain,\n", 198 | " batch_size=128, epochs=initial_epoch + 1,\n", 199 | " validation_data=(Dtest, Rtest),\n", 200 | " validation_freq=5,\n", 201 | " initial_epoch=initial_epoch)\n", 202 | "initial_epoch = model.history.epoch[-1]" 203 | ] 204 | }, 205 | { 206 | "cell_type": "code", 207 | "execution_count": 9, 208 | "id": "regular-medicine", 209 | "metadata": { 210 | "scrolled": true 211 | }, 212 | "outputs": [], 213 | "source": [ 214 | "epochs = initial_epoch + 1\n", 215 | "model.save(\n", 216 | " os.path.join('forward_model',\n", 217 | " 'Arch4_Epochs{}_Adam0001_Sigmoid.h5'.format(epochs)))" 218 | ] 219 | }, 220 | { 221 | "cell_type": "code", 222 | "execution_count": 10, 223 | "id": "marine-terrorism", 224 | "metadata": { 225 | "scrolled": true 226 | }, 227 | "outputs": [], 228 | "source": [ 229 | "# # Uncomment after training:\n", 230 | "# # ======================== \n", 231 | "# val_loss = model.history.history['val_loss']\n", 232 | "# loss = model.history.history['loss']\n", 233 | "# fig, ax = plt.subplots()\n", 234 | "# ax.plot(info.epoch, loss, label='Loss')\n", 235 | "# ax.plot(info.epoch[::5], val_loss, label='Validation Loss')\n", 236 | "# ax.legend()\n", 237 | "# ax.set_xlabel('Epoch')" 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "execution_count": 11, 243 | "id": "flexible-winning", 244 | "metadata": {}, 245 | "outputs": [ 246 | { 247 | "data": { 248 | "image/png": "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\n", 249 | "text/plain": [ 250 | "
" 251 | ] 252 | }, 253 | "metadata": { 254 | "needs_background": "light" 255 | }, 256 | "output_type": "display_data" 257 | } 258 | ], 259 | "source": [ 260 | "idx = np.random.randint(0, Dtest.shape[0], 1)\n", 261 | "dnn_responses = model(Dtest[idx]).numpy()\n", 262 | "responses = Rtest[idx]\n", 263 | "for o, r in zip(dnn_responses, responses):\n", 264 | " line, = plt.plot(o, '--')\n", 265 | " plt.plot(r, '-', color=line.get_color())" 266 | ] 267 | }, 268 | { 269 | "cell_type": "code", 270 | "execution_count": 12, 271 | "id": "korean-display", 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [ 275 | "load_dir = 'forward_model'\n", 276 | "def loss(y_true, y_pred):\n", 277 | " return n_freqs * keras.losses.mse(y_true, y_pred)\n", 278 | "\n", 279 | "fmodel400 = keras.models.load_model(\n", 280 | " os.path.join(load_dir, 'Arch4_Epochs400_Adam0001_Sigmoid.h5'),\n", 281 | " custom_objects={'loss': loss})\n", 282 | "fmodel1000 = keras.models.load_model(\n", 283 | " os.path.join(load_dir, 'Arch4_Epochs1000_Adam0001_Sigmoid.h5'),\n", 284 | " custom_objects={'loss': loss})\n", 285 | "fmodel2500 = keras.models.load_model(\n", 286 | " os.path.join(load_dir, 'Arch4_Epochs2500_Adam0001_Sigmoid.h5'),\n", 287 | " custom_objects={'loss': loss})\n", 288 | "fmodel4000 = keras.models.load_model(\n", 289 | " os.path.join(load_dir, 'Arch4_Epochs4000_Adam0001_Sigmoid.h5'),\n", 290 | " custom_objects={'loss': loss})" 291 | ] 292 | }, 293 | { 294 | "cell_type": "code", 295 | "execution_count": 13, 296 | "id": "moral-equipment", 297 | "metadata": {}, 298 | "outputs": [ 299 | { 300 | "data": { 301 | "text/plain": [ 302 | "" 303 | ] 304 | }, 305 | "execution_count": 13, 306 | "metadata": {}, 307 | "output_type": "execute_result" 308 | }, 309 | { 310 | "data": { 311 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAASEAAAD4CAYAAACjW1BIAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8vihELAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGzklEQVR4nO3bz4vtdR3H8ddbb5JakXBtkUpjEJUEYUhYQosMKoraFhTRuh8WQVh/QYuIWkQQVgRJLcxFRGSLWkv3alBmgfjzlpESlURwEz8tZgIJszvO+d7XbXo8VjPnfDnf92fOd55zvt85Z9ZaAWi5qD0A8P9NhIAqEQKqRAioEiGg6sRhNj558uTa29s75+3PPvXHrPXMYWc6lL9fckXOZ0svPfunJNv/R/Eo63r+Ged57jv6LEf5+bzQNR/1OTnqMbTVMbHFsb2rWWcuyiUvfcU5b3/69Okn11pXPtd9h4rQ3t5eTp06dc7bP3TXFw7z8C/IvXsf2Xwfz3b9w98+L/s5yrp2PeNhZjnKvl/omo+63qMeQ1sdE1sc27uc9dp33nrO287MI//pPqdjQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUCVCQJUIAVUiBFSJEFAlQkCVCAFVIgRUiRBQJUJAlQgBVSIEVIkQUDVrrXPfeOaJJI8c4vFPJnnysEP9D7LO48U6d+9Va60rn+uOQ0XosGbm1Frrhs12cIGwzuPFOs8vp2NAlQgBVVtH6OsbP/6FwjqPF+s8jza9JgTw3zgdA6pECKjaJEIz866Z+e3MPDAzt26xjwvBzFwzMz+bmftn5r6ZuaU901Zm5uKZuXdmftieZUsz8/KZuWNmfnPwvL6lPdOuzcynD47XX83Md2fmxc15dh6hmbk4yVeTvDvJdUk+ODPX7Xo/F4ink3xmrfX6JDcm+dgxXustSe5vD3EefCXJj9dar0vyxhyzNc/MVUk+meSGtdYbklyc5APNmbZ4JfTmJA+stR5ca51N8r0k799gP3VrrcfXWvccfP1U9g/Yq7pT7d7MXJ3kPUlua8+ypZl5WZK3JflGkqy1zq61/lwdahsnklw6MyeSXJbk981htojQVUkee9b3Z3IMfzH/3czsJbk+yd3lUbbw5SSfTfJMeY6tvTrJE0m+dXDqedvMXN4eapfWWr9L8sUkjyZ5PMlf1lo/ac60RYTmOW471u8DmJmXJPl+kk+ttf7anmeXZua9Sf641jrdnuU8OJHkTUm+tta6Psnfkhyra5ozc0X2z0yuTfLKJJfPzIeaM20RoTNJrnnW91en/HJvSzPzouwH6Pa11p3teTZwU5L3zczD2T+1fvvMfKc70mbOJDmz1vrXq9k7sh+l4+QdSR5aaz2x1vpHkjuTvLU50BYR+nmS18zMtTNzSfYvev1gg/3Uzcxk//rB/WutL7Xn2cJa63NrravXWnvZfy5/utaq/uXcylrrD0kem5nXHtx0c5JfF0fawqNJbpyZyw6O35tTvvh+YtcPuNZ6emY+nuSu7F95/+Za675d7+cCcVOSDyf55cz84uC2z6+1ftQbiSP6RJLbD/6APpjko+V5dmqtdffM3JHknuz/d/felD++4WMbQJV3TANVIgRUiRBQJUJAlQgBVSIEVIkQUPVPXXwoIjol4D4AAAAASUVORK5CYII=\n", 312 | "text/plain": [ 313 | "
" 314 | ] 315 | }, 316 | "metadata": { 317 | "needs_background": "light" 318 | }, 319 | "output_type": "display_data" 320 | }, 321 | { 322 | "data": { 323 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAfEAAAFzCAYAAAAuSjCuAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8vihELAAAACXBIWXMAAAsTAAALEwEAmpwYAACEMUlEQVR4nOzdd3xUVdrA8d+ZPumdFiD03nsRQSyoiIK9rV3R1S2ubXfV1d1XV13XtevaXRc7dlkbvSm9d0ILAdLrZPp5/7hJSEgFMmk8Xz/5JHPvnXufGUOeOeeecx6ltUYIIYQQLY+pqQMQQgghxImRJC6EEEK0UJLEhRBCiBZKkrgQQgjRQkkSF0IIIVooSeJCCCFEC2Vp6gCOV0JCgk5JSWnqMIQQQohGs3r16iytdeKx21tcEk9JSWHVqlVNHYYQQgjRaJRS+6rbLt3pQgghRAslSVwIIYRooSSJCyGEEC1Ui7snXh2fz0daWhput7upQxGnIIfDQXJyMlartalDEUKcYlpFEk9LSyMyMpKUlBSUUk0djjiFaK3Jzs4mLS2NLl26NHU4QohTTKvoTne73cTHx0sCF41OKUV8fLz0AgkhmkSrSOKAJHDRZOR3TwjRVFpNEm9KeXl5vPzyyyG/zhdffMGWLVtCfh0hhBAtgyTxBnC8SVxrTTAYPO7rSBIXQghRkSTxBvDAAw+we/duBg8ezO9//3smT57M0KFDGTBgAF9++SUAe/fupU+fPtxxxx0MHTqUAwcO8Le//Y3evXtz1llnceWVV/L0008DsHv3bqZMmcKwYcM47bTT2LZtG8uWLeOrr77i3nvvZfDgwezevbspX7IQQohmoFWMTq/o0a83syW9oEHP2bd9FH+5oF+N+5944gk2bdrEunXr8Pv9uFwuoqKiyMrKYvTo0UybNg2A7du38/bbb/Pyyy+zatUqZs+ezdq1a/H7/QwdOpRhw4YBcOutt/Lqq6/So0cPfvnlF+644w7mzZvHtGnTmDp1KpdcckmDvj4hhBAtU6tL4k1Na82f/vQnFi1ahMlk4uDBgxw5cgSAzp07M3r0aACWLFnChRdeiNPpBOCCCy4AoKioiGXLlnHppZeWn9Pj8TTyqxBCNLb8Eh8l3gBtox1NHYpoQVpdEq+txdwYZs2aRWZmJqtXr8ZqtZKSklI+/Sg8PLz8OK11tc8PBoPExMSwbt26xghXCNFMPPndNtbuz+N/vz2tqUMRLYjcE28AkZGRFBYWApCfn09SUhJWq5X58+ezb1+1hWcYP348X3/9NW63m6KiIr799lsAoqKi6NKlC5988glgJPv169dXuY4QonXJKPBwOL+kqcMQLYwk8QYQHx/PuHHj6N+/P+vWrWPVqlUMHz6cWbNm0bt372qfM2LECKZNm8agQYOYMWMGw4cPJzo6GjBa82+++SaDBg2iX79+5YPjrrjiCv7xj38wZMgQGdgmRCtT4vNT4PbX2EsnRHVUS/uFGT58uD62nvjWrVvp06dPE0V04oqKioiIiMDlcjFhwgRee+01hg4d2tRhiRPQUn8HRfMx/eWlrN2fx+ZHzyHc3urudIqTpJRarbUefux2+U1pQrfeeitbtmzB7XZz3XXXSQIX4hRW4g0AUOj2SxIX9Sa/KU3o/fffb+oQhBDNhKs0iRe4fTJCXdSb3BMXQohmwFXeEvc1cSSiJZEkLoQQzUCJ1w9AQYm/iSMRLYkkcSGEaGJaa1y+o93pQtRXSJO4UmqKUmq7UmqXUuqBavbfq5RaV/q1SSkVUErFhTImIYRobjz+IGUThQrc0hIX9ReyJK6UMgMvAecCfYErlVJ9Kx6jtf6H1nqw1now8EdgodY6J1QxtRQLFixg6tSpAHz11Vc88cQTNR57bAW19PT0Bl1b/ZJLLiE1NbXStmnTptG/f//yxx6Ph8svv5zu3bszatQo9u7dW77v3XffpUePHvTo0YN33323weI6HikpKWRlZdXr2MzMTKZMmRLiiISorOx+OMg9cXF8QtkSHwns0lqnaq29wIfAhbUcfyXwQQjjaXKBQKDug44xbdo0HnigSidGuWOTePv27fn0009PKL5jbd68mUAgQNeuXcu3ffbZZ0RERFQ67s033yQ2NpZdu3bx+9//nvvvvx+AnJwcHn30UX755RdWrFjBo48+Sm5uboPEFiqJiYm0a9eOpUuXNnUo4hTi8h5tfcs9cXE8QpnEOwAHKjxOK91WhVIqDJgCzA5hPCGzd+9eevfuzXXXXcfAgQO55JJLcLlcgNEK/Otf/8r48eP55JNP+OGHHxgzZgxDhw7l0ksvpaioCIDvvvuO3r17M378eD777LPyc7/zzjvceeedABw5coTp06czaNAgBg0axLJlyyqVQb333nvZu3dveSvZ7XZzww03MGDAAIYMGcL8+fPLzzljxgymTJlCjx49uO+++6p9XbNmzeLCC49+7ioqKuKZZ57hwQcfrHTcl19+yXXXXQcYLfe5c+eiteb777/nrLPOIi4ujtjYWM466yy+++67KteprvQqwPXXX8/MmTM57bTT6NmzJ998802trysQCHDPPfcwYMAABg4cyAsvvFB+jRdeeKG8PGzZ+RcuXMjgwYMZPHgwQ4YMKV/S9qKLLmLWrFl1/F8XouGUSEtcnKBQzhNX1WyraXm4C4ClNXWlK6VuBW4F6NSpU91Xfvv8qtv6XQQjbwGvC2ZdWnX/4KtgyNVQnA0f/6ryvhu+rfOS27dv580332TcuHHceOONvPzyy9xzzz0AOBwOlixZQlZWFjNmzOCnn34iPDycJ598kmeeeYb77ruPW265hXnz5tG9e3cuv/zyaq/xm9/8htNPP53PP/+cQCBAUVFRpTKoQKWu7JdeegmAjRs3sm3bNs4++2x27NgBwLp161i7di12u51evXpx11130bFjx0rXW7p0KVdeeWX544ceeog//OEPhIWFVTru4MGD5c+1WCxER0eTnZ1daTtAcnIyBw8erPK6aiq9WvZ6Fi5cyO7du5k0aRK7du2q8XW9/fbb7Nmzh7Vr12KxWMjJOfrrlJCQwJo1a3j55Zd5+umneeONN3j66ad56aWXGDduHEVFRTgcxtzc4cOHV/mgIkQoVexOl3vi4niEsiWeBlTMCslAeg3HXkEtXela69e01sO11sMTExMbMMSG07FjR8aNGwfANddcw5IlS8r3lSXln3/+mS1btjBu3DgGDx7Mu+++y759+9i2bRtdunShR48eKKW45pprqr3GvHnzuP322wEwm83la63XZMmSJVx77bUA9O7dm86dO5cn8cmTJxMdHY3D4aBv377VFmo5dOgQZe/3unXr2LVrF9OnT69yXHVL9yqlatxeUcXSq4MHD+a2227j0KFD5fsvu+wyTCYTPXr0oGvXrmzbtq3G1/XTTz8xc+ZMLBbjs2lc3NExkjNmzABg2LBh5R90xo0bx913383zzz9PXl5e+fOSkpJIT6/pV1WIhif3xMWJCmVLfCXQQynVBTiIkaivOvYgpVQ0cDpQfeY6EbW1nG1hte8Pj69Xy/tYxyanio/LSpBqrTnrrLP44IPKn1fWrVtX5fkNobZ18e12e/nP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\n", 324 | "text/plain": [ 325 | "
" 326 | ] 327 | }, 328 | "metadata": { 329 | "needs_background": "light" 330 | }, 331 | "output_type": "display_data" 332 | } 333 | ], 334 | "source": [ 335 | "seed = 8\n", 336 | "np.random.seed(seed)\n", 337 | "\n", 338 | "epsilon_Si = 13.491\n", 339 | "epsilon_SiO2 = 2.085136\n", 340 | "n_freqs = 200\n", 341 | "freqs = np.linspace(0.15, 0.25, n_freqs)\n", 342 | "n_grating_layers = 15\n", 343 | "\n", 344 | "D = np.random.random_sample((1, n_grating_layers))\n", 345 | "gr = Grating(epsilon_Si, epsilon_SiO2, D[0])\n", 346 | "\n", 347 | "# plot grating design and save file\n", 348 | "gr.plot()\n", 349 | "# plt.savefig('grating_seed{}.png'.format(seed), dpi=200)\n", 350 | "\n", 351 | "# plot target and prediction responses and save file\n", 352 | "fig, ax = plt.subplots(figsize=(8,6))\n", 353 | "line, = ax.plot(freqs, gr.transmittivity(freqs), label='target')\n", 354 | "ax.plot(freqs, fmodel400(D)[0].numpy(), '--', label='prediction (400 epochs)') #color=line.get_color()\n", 355 | "ax.plot(freqs, fmodel1000(D)[0].numpy(), '--', label='prediction (1000 epochs)')\n", 356 | "# ax.plot(freqs, fmodel2500(D)[0].numpy(), '--', label='prediction (2500 epochs)')\n", 357 | "ax.plot(freqs, fmodel4000(D)[0].numpy(), '--', label='prediction (4000 epochs)')\n", 358 | "ax.set_xlabel('Frequency [c/a]')\n", 359 | "ax.set_ylabel('Transmission')\n", 360 | "ax.legend()\n", 361 | "# plt.savefig('forward_model_seed{}.png'.format(seed), dpi=200)" 362 | ] 363 | } 364 | ], 365 | "metadata": { 366 | "kernelspec": { 367 | "display_name": "Python 3", 368 | "language": "python", 369 | "name": "python3" 370 | }, 371 | "language_info": { 372 | "codemirror_mode": { 373 | "name": "ipython", 374 | "version": 3 375 | }, 376 | "file_extension": ".py", 377 | "mimetype": "text/x-python", 378 | "name": "python", 379 | "nbconvert_exporter": "python", 380 | "pygments_lexer": "ipython3", 381 | "version": "3.8.5" 382 | } 383 | }, 384 | "nbformat": 4, 385 | "nbformat_minor": 5 386 | } 387 | -------------------------------------------------------------------------------- /forward_model/Arch4_Epochs1000_Adam0001_Sigmoid.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/forward_model/Arch4_Epochs1000_Adam0001_Sigmoid.h5 -------------------------------------------------------------------------------- /forward_model/Arch4_Epochs2500_Adam0001_Sigmoid.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/forward_model/Arch4_Epochs2500_Adam0001_Sigmoid.h5 -------------------------------------------------------------------------------- /forward_model/Arch4_Epochs4000_Adam0001_Sigmoid.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/forward_model/Arch4_Epochs4000_Adam0001_Sigmoid.h5 -------------------------------------------------------------------------------- /forward_model/Arch4_Epochs400_Adam0001_Sigmoid.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/forward_model/Arch4_Epochs400_Adam0001_Sigmoid.h5 -------------------------------------------------------------------------------- /forward_model/Arch4_Epochs6000_Adam0001_Sigmoid.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/forward_model/Arch4_Epochs6000_Adam0001_Sigmoid.h5 -------------------------------------------------------------------------------- /grating.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | 4 | 5 | import pyximport 6 | pyximport.install( 7 | setup_args={'include_dirs': np.get_include()}, 8 | language_level=3, 9 | reload_support=True 10 | ) 11 | from cy_funcs import * 12 | 13 | 14 | class Layer(object): 15 | def __init__(self, eps, width): 16 | self.eps = eps 17 | self.width = width 18 | 19 | 20 | class Grating(object): 21 | def __init__(self, eps1, eps2, widths): 22 | self.eps1 = eps1 23 | self.eps2 = eps2 24 | self.widths = widths 25 | self.layers = [] 26 | for i, d in enumerate(widths): 27 | eps = self.eps1 if i % 2 == 0 else self.eps2 28 | self.layers.append(Layer(eps, d)) 29 | 30 | def props_layers(self, f, b=0): 31 | for i, d in enumerate(self.widths): 32 | eps = self.eps1 if i % 2 == 0 else self.eps2 33 | yield propagator_layer(f, eps, d, b) 34 | 35 | def propagator(self, f, b=0): 36 | return propagator_grating(f, self.eps1, self.eps2, self.widths, b) 37 | 38 | def transmittivity(self, f, b=0., pol='x'): 39 | propagators = self.propagator(f, b) 40 | op_t = np.array([operator_t(p, 1., 1., b) for p in propagators]) 41 | if pol == 'x': 42 | return np.abs(op_t[:, 0, 0]) ** 2 43 | if pol == 'y': 44 | return np.abs(op_t[:, 1, 1]) ** 2 45 | if pol == 'xy': 46 | return np.abs(op_t[:, 0, 1]) ** 2 47 | if pol == 'yx': 48 | return np.abs(op_t[:, 1, 0]) ** 2 49 | 50 | def plot(self, ax=None, colors=('burlywood', 'lightblue')): 51 | if ax is None: 52 | fig, ax = plt.subplots() 53 | # height = sum(l.width for l in self.layers) 54 | xmin = 0 55 | for i, l in enumerate(self.layers): 56 | ax.axvspan(xmin, xmin + l.width, color=colors[i % 2]) 57 | xmin += l.width 58 | ax.set_ylim(0, len(self.layers) / 2) 59 | ax.set_yticks([]) 60 | ax.set_aspect('equal') 61 | return ax 62 | -------------------------------------------------------------------------------- /inverse_model/Epochs400_Adam0001_Sigmoid.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/inverse_model/Epochs400_Adam0001_Sigmoid.h5 -------------------------------------------------------------------------------- /inverse_model/InverseNet15EpochsTandem.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/inverse_model/InverseNet15EpochsTandem.h5 -------------------------------------------------------------------------------- /inverse_model/TandemNN_Epochs400_Adam0001_Sigmoid.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FiodarM/InvDesignNet/c07ae0ae89fd2a3f527227984bf0b3a14e0c5d83/inverse_model/TandemNN_Epochs400_Adam0001_Sigmoid.h5 -------------------------------------------------------------------------------- /produce_data.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "vertical-portsmouth", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "%matplotlib inline\n", 11 | "import numpy as np\n", 12 | "import scipy.linalg as spla\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "from tqdm.notebook import tqdm\n", 15 | "import os\n", 16 | "from grating import Grating\n", 17 | "import gc" 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "id": "united-prerequisite", 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "epsilon_Si = 13.491\n", 28 | "epsilon_SiO2 = 2.085136\n", 29 | "n_freqs = 200\n", 30 | "freqs = np.linspace(0.15, 0.25, n_freqs)\n", 31 | "n_grating_layers = 15\n", 32 | "\n", 33 | "np.random.seed(42)\n", 34 | "D = np.random.random_sample(n_grating_layers)\n", 35 | "gr = Grating(epsilon_Si, epsilon_SiO2, D)\n", 36 | "freqs = np.linspace(0.15, 0.25, n_freqs)\n", 37 | "ts = gr.transmittivity(freqs)\n", 38 | "plt.plot(freqs, ts)\n", 39 | "plt.ylim(0, 1)" 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "execution_count": null, 45 | "id": "shared-framework", 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "# Benchmarking\n", 50 | "%timeit gr.propagator(0.2)\n", 51 | "%timeit gr.propagator(freqs)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": null, 57 | "id": "fossil-witness", 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "batch_size = 1000\n", 62 | "n_grating_layers = 15\n", 63 | "\n", 64 | "a = 1\n", 65 | "freqs = np.linspace(0.15 / a, 0.25 / a, n_freqs)\n", 66 | "\n", 67 | "\n", 68 | "from produce_data import save_samples\n", 69 | "fname = 'dataset.npz'\n", 70 | "samples = dict(D=[], R=[])\n", 71 | "try:\n", 72 | " i = 0\n", 73 | " with tqdm(total=batch_size, leave=False) as pbar:\n", 74 | " while True:\n", 75 | " D = np.random.random_sample(n_grating_layers)\n", 76 | " gr = Grating(epsilon_Si, epsilon_SiO2, D)\n", 77 | " R = gr.transmittivity(freqs)\n", 78 | " i += 1\n", 79 | " pbar.update(1)\n", 80 | " samples['D'].append(D)\n", 81 | " samples['R'].append(R)\n", 82 | " if i == batch_size:\n", 83 | " save_samples(fname, samples)\n", 84 | " samples = dict(D=[], R=[])\n", 85 | " gc.collect()\n", 86 | " pbar.reset()\n", 87 | " i = 0\n", 88 | "except KeyboardInterrupt:\n", 89 | " save_samples(fname, samples)" 90 | ] 91 | } 92 | ], 93 | "metadata": { 94 | "kernelspec": { 95 | "display_name": "Python 3", 96 | "language": "python", 97 | "name": "python3" 98 | }, 99 | "language_info": { 100 | "codemirror_mode": { 101 | "name": "ipython", 102 | "version": 3 103 | }, 104 | "file_extension": ".py", 105 | "mimetype": "text/x-python", 106 | "name": "python", 107 | "nbconvert_exporter": "python", 108 | "pygments_lexer": "ipython3", 109 | "version": "3.7.9" 110 | } 111 | }, 112 | "nbformat": 4, 113 | "nbformat_minor": 5 114 | } 115 | -------------------------------------------------------------------------------- /produce_data.py: -------------------------------------------------------------------------------- 1 | import gc 2 | 3 | import numpy as np 4 | from grating import Grating 5 | import os 6 | from tqdm import tqdm 7 | 8 | 9 | batch_size = 1000 10 | n_grating_layers = 15 11 | n_freqs = 200 12 | a = 1. 13 | freqs = np.linspace(0.15 / a, 0.25 / a, n_freqs) 14 | epsilon_Si = 13.491 15 | epsilon_SiO2 = 2.085136 16 | 17 | 18 | def save_samples(fname, samples): 19 | n_samples = len(samples['R']) 20 | if os.path.exists(fname): 21 | with np.load(fname) as existing: 22 | for k in samples.keys(): 23 | samples[k] = np.vstack((existing[k], samples[k])) 24 | try: 25 | np.savez(fname, **samples) 26 | except KeyboardInterrupt: 27 | pass 28 | print("Saved {0} samples to {1}. The dataset contains {2} samples." 29 | .format(n_samples, fname, len(samples['R']))) 30 | 31 | 32 | fname = 'dataset.npz' 33 | 34 | 35 | if __name__ == '__main__': 36 | samples = dict(D=[], R=[]) 37 | try: 38 | i = 0 39 | with tqdm(total=batch_size, leave=False) as pbar: 40 | while True: 41 | D = np.random.random_sample(n_grating_layers) 42 | gr = Grating(epsilon_Si, epsilon_SiO2, D) 43 | R = gr.transmittivity(freqs) 44 | i += 1 45 | pbar.update(1) 46 | samples['D'].append(D) 47 | samples['R'].append(R) 48 | if i == batch_size: 49 | save_samples(fname, samples) 50 | samples = dict(D=[], R=[]) 51 | gc.collect() 52 | pbar.reset() 53 | i = 0 54 | except KeyboardInterrupt: 55 | print('Interrupting calculation...') 56 | answer = input('Do you want to save calculated data? y/[n]: ') 57 | if answer == 'y': 58 | save_samples(fname, samples) 59 | finally: 60 | print('Exiting script') 61 | exit(0) 62 | -------------------------------------------------------------------------------- /py_funcs.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.linalg as spla 3 | 4 | 5 | ex = np.array([1, 0]) 6 | exex = np.outer(ex, ex) 7 | ey = np.array([0, 1]) 8 | eyey = np.outer(ey, ey) 9 | I = np.eye(2) 10 | I4 = np.eye(4, dtype=np.complex128) 11 | 12 | 13 | def eigvalsM(eps, b=0.): 14 | n = np.sqrt(eps - b ** 2) 15 | return np.array([-n, -n, n, n]) 16 | 17 | 18 | def eigvecsM(eps, b=0.): 19 | sqrtepsb = np.sqrt(eps - b ** 2) 20 | c1 = sqrtepsb 21 | c2 = eps * sqrtepsb / (b ** 2 - eps) 22 | c3 = sqrtepsb / (2 * eps) 23 | c4 = 0.5 / sqrtepsb 24 | return np.array([[[0, -c1, 0, c1], 25 | [c2, 0, -c2, 0], 26 | [0, 1, 0, 1], 27 | [1, 0, 1, 0]], 28 | [[0, -c3, 0, 0.5], 29 | [-c4, 0, 0.5, 0], 30 | [0, c3, 0, 0.5], 31 | [c4, 0, 0.5, 0]]]) 32 | 33 | 34 | def propagator_layer(f, eps, d, b=0): 35 | """Propagator, or evolution operator, or transfer matrix of 36 | a uniform layer with permittivity eps and thickness d 37 | at frequency f. 38 | """ 39 | scalar_input = np.isscalar(f) 40 | freqs = np.atleast_1d(f) 41 | N = freqs.shape[0] 42 | res = np.empty((N, 4, 4), dtype=np.complex_) 43 | w = eigvalsM(eps, b) 44 | vr = eigvecsM(eps, b) 45 | for i in range(N): 46 | ik0d = 2j * np.pi * freqs[i] * d 47 | res[i, :, :] = np.linalg.multi_dot( 48 | [vr[0], np.diag(np.exp(ik0d * w)), vr[1]] 49 | ) 50 | if scalar_input: 51 | return res[0] 52 | return res 53 | 54 | 55 | def propagator_grating(f, eps1, eps2, D, b=0): 56 | scalar_input = np.isscalar(f) 57 | N = D.shape[0] 58 | if scalar_input: 59 | propagator = propagator_layer(f, eps1, D[0], b) 60 | for n in range(1, N): 61 | eps = eps1 if n % 2 == 0 else eps2 62 | propagator = np.dot(propagator_layer(f, eps, D[n], b), propagator) 63 | return propagator 64 | propagator = propagator_layer(f, eps1, D[0], b) 65 | for n in range(1, N): 66 | eps = eps1 if n % 2 == 0 else eps2 67 | prop_layer = propagator_layer(f, eps, D[n], b) 68 | for i in range(propagator.shape[0]): 69 | propagator[i] = np.dot(prop_layer[i], propagator[i]) 70 | return propagator 71 | 72 | 73 | def gamma(eps, b=0): 74 | return np.diag([1 / np.sqrt(eps - b ** 2), 75 | np.sqrt(eps - b ** 2) / eps]) 76 | 77 | 78 | def operator_r(propagator, eps_left=1, eps_right=None, b=0): 79 | """Reflection operator of a multilayer characterized by propagator 80 | surrounded by media with eps_left at left and eps_right at right. 81 | """ 82 | if not eps_right: 83 | eps_right = eps_left 84 | gamma_left = gamma(eps_left, b) 85 | gamma_right = gamma(eps_right, b) 86 | factor1 = (np.bmat([gamma_right, -I]). 87 | dot(propagator). 88 | dot(np.bmat([I, -gamma_left]).T)) 89 | factor2 = (np.bmat([gamma_right, -I]). 90 | dot(propagator). 91 | dot(np.bmat([I, gamma_left]).T)) 92 | return spla.inv(factor1).dot(factor2) 93 | 94 | 95 | def operator_t(propagator, eps_left=1, eps_right=None, b=0): 96 | """Transmission operator of a multilayer characterized by propagator 97 | surrounded by media with eps_left at left and eps_right at right. 98 | """ 99 | if not eps_right: 100 | eps_right = eps_left 101 | gamma_left = gamma(eps_left, b) 102 | gamma_right = gamma(eps_right, b) 103 | return 2 * spla.inv((np.bmat([gamma_left, I]). 104 | dot(spla.inv(propagator)). 105 | dot(np.bmat([I, gamma_right]).T))).dot(gamma_left) 106 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | tensorflow>=2.3 2 | numpy>=1.19.5 3 | tqdm 4 | matplotlib 5 | cython 6 | jupyter 7 | scikit-learn -------------------------------------------------------------------------------- /test_grating.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib.pyplot as plt 3 | from grating import Grating 4 | 5 | 6 | if __name__ == '__main__': 7 | n_grating_layers = 15 8 | n_freqs = 200 9 | epsilon_Si = 13.491 10 | epsilon_SiO2 = 2.085136 11 | np.random.seed(42) 12 | D = np.random.random_sample(15) 13 | gr = Grating(epsilon_Si, epsilon_SiO2, D) 14 | freqs = np.linspace(0.15, 0.25, n_freqs) 15 | ts = gr.transmittivity(freqs) 16 | plt.plot(freqs, ts) 17 | plt.show() --------------------------------------------------------------------------------