lightsmith 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. lightsmith-0.1.0/.gitignore +37 -0
  2. lightsmith-0.1.0/AGENTS.md +30 -0
  3. lightsmith-0.1.0/CHANGELOG.md +50 -0
  4. lightsmith-0.1.0/CITATION.cff +29 -0
  5. lightsmith-0.1.0/LICENSE +21 -0
  6. lightsmith-0.1.0/PKG-INFO +137 -0
  7. lightsmith-0.1.0/README.md +94 -0
  8. lightsmith-0.1.0/docs/api/backends.md +3 -0
  9. lightsmith-0.1.0/docs/api/circuits.md +3 -0
  10. lightsmith-0.1.0/docs/api/core.md +9 -0
  11. lightsmith-0.1.0/docs/api/index.md +14 -0
  12. lightsmith-0.1.0/docs/api/inverse.md +3 -0
  13. lightsmith-0.1.0/docs/api/layout.md +3 -0
  14. lightsmith-0.1.0/docs/api/materials.md +7 -0
  15. lightsmith-0.1.0/docs/api/problems.md +3 -0
  16. lightsmith-0.1.0/docs/api/results.md +3 -0
  17. lightsmith-0.1.0/docs/api/spec.md +3 -0
  18. lightsmith-0.1.0/docs/changelog.md +1 -0
  19. lightsmith-0.1.0/docs/cli.md +43 -0
  20. lightsmith-0.1.0/docs/contributing.md +112 -0
  21. lightsmith-0.1.0/docs/conventions.md +74 -0
  22. lightsmith-0.1.0/docs/index.md +61 -0
  23. lightsmith-0.1.0/docs/install.md +113 -0
  24. lightsmith-0.1.0/docs/landscape.md +340 -0
  25. lightsmith-0.1.0/docs/materials.md +84 -0
  26. lightsmith-0.1.0/docs/mcp-overview.md +36 -0
  27. lightsmith-0.1.0/docs/problems/bands.md +56 -0
  28. lightsmith-0.1.0/docs/problems/circuit.md +48 -0
  29. lightsmith-0.1.0/docs/problems/device.md +61 -0
  30. lightsmith-0.1.0/docs/problems/index.md +38 -0
  31. lightsmith-0.1.0/docs/problems/layer-stack.md +52 -0
  32. lightsmith-0.1.0/docs/problems/mode.md +53 -0
  33. lightsmith-0.1.0/docs/problems/optimization.md +40 -0
  34. lightsmith-0.1.0/docs/problems/periodic.md +50 -0
  35. lightsmith-0.1.0/docs/problems/scattering.md +80 -0
  36. lightsmith-0.1.0/docs/quickstart.md +110 -0
  37. lightsmith-0.1.0/docs/solvers/circuit.md +85 -0
  38. lightsmith-0.1.0/docs/solvers/dda.md +65 -0
  39. lightsmith-0.1.0/docs/solvers/eme.md +66 -0
  40. lightsmith-0.1.0/docs/solvers/fde.md +73 -0
  41. lightsmith-0.1.0/docs/solvers/fdfd.md +65 -0
  42. lightsmith-0.1.0/docs/solvers/fdtd.md +120 -0
  43. lightsmith-0.1.0/docs/solvers/in-development/bem.md +14 -0
  44. lightsmith-0.1.0/docs/solvers/in-development/bpm.md +14 -0
  45. lightsmith-0.1.0/docs/solvers/in-development/fdtd-oblique.md +14 -0
  46. lightsmith-0.1.0/docs/solvers/in-development/index.md +14 -0
  47. lightsmith-0.1.0/docs/solvers/in-development/inverse-3d.md +14 -0
  48. lightsmith-0.1.0/docs/solvers/in-development/pdk-flow.md +14 -0
  49. lightsmith-0.1.0/docs/solvers/index.md +60 -0
  50. lightsmith-0.1.0/docs/solvers/inverse.md +78 -0
  51. lightsmith-0.1.0/docs/solvers/mie.md +64 -0
  52. lightsmith-0.1.0/docs/solvers/pwe.md +76 -0
  53. lightsmith-0.1.0/docs/solvers/quasistatic.md +54 -0
  54. lightsmith-0.1.0/docs/solvers/rcwa.md +72 -0
  55. lightsmith-0.1.0/docs/solvers/tmatrix.md +66 -0
  56. lightsmith-0.1.0/docs/solvers/tmm.md +54 -0
  57. lightsmith-0.1.0/examples/circuit_mzi.py +58 -0
  58. lightsmith-0.1.0/examples/circuit_ring.py +77 -0
  59. lightsmith-0.1.0/examples/circuit_splitter_tree.py +50 -0
  60. lightsmith-0.1.0/examples/drude_sphere.json +13 -0
  61. lightsmith-0.1.0/examples/inverse_design_bend.py +111 -0
  62. lightsmith-0.1.0/examples/metalens_rcwa.py +83 -0
  63. lightsmith-0.1.0/llms.txt +22 -0
  64. lightsmith-0.1.0/mkdocs.yml +141 -0
  65. lightsmith-0.1.0/pyproject.toml +109 -0
  66. lightsmith-0.1.0/src/lightsmith/__init__.py +44 -0
  67. lightsmith-0.1.0/src/lightsmith/api.py +110 -0
  68. lightsmith-0.1.0/src/lightsmith/backends/__init__.py +21 -0
  69. lightsmith-0.1.0/src/lightsmith/backends/_eme/__init__.py +290 -0
  70. lightsmith-0.1.0/src/lightsmith/backends/_fde/__init__.py +245 -0
  71. lightsmith-0.1.0/src/lightsmith/backends/_fde/grid.py +148 -0
  72. lightsmith-0.1.0/src/lightsmith/backends/_fde/modes.py +314 -0
  73. lightsmith-0.1.0/src/lightsmith/backends/_fde/section.py +201 -0
  74. lightsmith-0.1.0/src/lightsmith/backends/_fdfd/__init__.py +20 -0
  75. lightsmith-0.1.0/src/lightsmith/backends/_fdfd/engine.py +215 -0
  76. lightsmith-0.1.0/src/lightsmith/backends/_fdfd/grid.py +116 -0
  77. lightsmith-0.1.0/src/lightsmith/backends/_fdfd/linsolve.py +208 -0
  78. lightsmith-0.1.0/src/lightsmith/backends/_fdfd/modes.py +156 -0
  79. lightsmith-0.1.0/src/lightsmith/backends/_fdfd/operators.py +100 -0
  80. lightsmith-0.1.0/src/lightsmith/backends/_fdfd/ports.py +116 -0
  81. lightsmith-0.1.0/src/lightsmith/backends/_fdtd/__init__.py +7 -0
  82. lightsmith-0.1.0/src/lightsmith/backends/_fdtd/engine.py +450 -0
  83. lightsmith-0.1.0/src/lightsmith/backends/_fdtd/media.py +129 -0
  84. lightsmith-0.1.0/src/lightsmith/backends/_fdtd/options.py +42 -0
  85. lightsmith-0.1.0/src/lightsmith/backends/_fdtd/periodic.py +399 -0
  86. lightsmith-0.1.0/src/lightsmith/backends/_fdtd/scattering.py +235 -0
  87. lightsmith-0.1.0/src/lightsmith/backends/_fdtd/setup.py +251 -0
  88. lightsmith-0.1.0/src/lightsmith/backends/_fdtd/tfsf.py +191 -0
  89. lightsmith-0.1.0/src/lightsmith/backends/_pwe/__init__.py +7 -0
  90. lightsmith-0.1.0/src/lightsmith/backends/_pwe/engine.py +165 -0
  91. lightsmith-0.1.0/src/lightsmith/backends/_pwe/geometry.py +346 -0
  92. lightsmith-0.1.0/src/lightsmith/backends/_pwe/solvers.py +386 -0
  93. lightsmith-0.1.0/src/lightsmith/backends/_rcwa/__init__.py +8 -0
  94. lightsmith-0.1.0/src/lightsmith/backends/_rcwa/core.py +174 -0
  95. lightsmith-0.1.0/src/lightsmith/backends/_rcwa/fourier.py +302 -0
  96. lightsmith-0.1.0/src/lightsmith/backends/_rcwa/linalg.py +129 -0
  97. lightsmith-0.1.0/src/lightsmith/backends/base.py +109 -0
  98. lightsmith-0.1.0/src/lightsmith/backends/circuit.py +104 -0
  99. lightsmith-0.1.0/src/lightsmith/backends/dda.py +448 -0
  100. lightsmith-0.1.0/src/lightsmith/backends/eme.py +252 -0
  101. lightsmith-0.1.0/src/lightsmith/backends/fde.py +227 -0
  102. lightsmith-0.1.0/src/lightsmith/backends/fdfd.py +159 -0
  103. lightsmith-0.1.0/src/lightsmith/backends/fdtd.py +105 -0
  104. lightsmith-0.1.0/src/lightsmith/backends/mie.py +63 -0
  105. lightsmith-0.1.0/src/lightsmith/backends/mie_native.py +540 -0
  106. lightsmith-0.1.0/src/lightsmith/backends/pwe.py +417 -0
  107. lightsmith-0.1.0/src/lightsmith/backends/quasistatic.py +215 -0
  108. lightsmith-0.1.0/src/lightsmith/backends/rcwa.py +459 -0
  109. lightsmith-0.1.0/src/lightsmith/backends/tmatrix_treams.py +473 -0
  110. lightsmith-0.1.0/src/lightsmith/backends/tmm.py +299 -0
  111. lightsmith-0.1.0/src/lightsmith/circuits/__init__.py +40 -0
  112. lightsmith-0.1.0/src/lightsmith/circuits/engine.py +339 -0
  113. lightsmith-0.1.0/src/lightsmith/circuits/models.py +594 -0
  114. lightsmith-0.1.0/src/lightsmith/circuits/touchstone.py +183 -0
  115. lightsmith-0.1.0/src/lightsmith/cli.py +81 -0
  116. lightsmith-0.1.0/src/lightsmith/data/materials/Ag/Johnson.yml +62 -0
  117. lightsmith-0.1.0/src/lightsmith/data/materials/Ag/McPeak.yml +154 -0
  118. lightsmith-0.1.0/src/lightsmith/data/materials/Ag/Rakic-LD.yml +214 -0
  119. lightsmith-0.1.0/src/lightsmith/data/materials/Al/McPeak.yml +310 -0
  120. lightsmith-0.1.0/src/lightsmith/data/materials/Al/Rakic-LD.yml +1014 -0
  121. lightsmith-0.1.0/src/lightsmith/data/materials/Al/Rakic.yml +219 -0
  122. lightsmith-0.1.0/src/lightsmith/data/materials/Au/Johnson.yml +62 -0
  123. lightsmith-0.1.0/src/lightsmith/data/materials/Au/McPeak.yml +154 -0
  124. lightsmith-0.1.0/src/lightsmith/data/materials/Au/Rakic-LD.yml +214 -0
  125. lightsmith-0.1.0/src/lightsmith/data/materials/Cu/Johnson.yml +62 -0
  126. lightsmith-0.1.0/src/lightsmith/data/materials/Cu/McPeak.yml +154 -0
  127. lightsmith-0.1.0/src/lightsmith/data/materials/Cu/Rakic-LD.yml +214 -0
  128. lightsmith-0.1.0/src/lightsmith/data/materials/H2O/Daimon-24.0C.yml +16 -0
  129. lightsmith-0.1.0/src/lightsmith/data/materials/H2O/Hale.yml +185 -0
  130. lightsmith-0.1.0/src/lightsmith/data/materials/Si/Aspnes.yml +59 -0
  131. lightsmith-0.1.0/src/lightsmith/data/materials/Si/Green-2008.yml +136 -0
  132. lightsmith-0.1.0/src/lightsmith/data/materials/Si/Schinke.yml +139 -0
  133. lightsmith-0.1.0/src/lightsmith/data/materials/SiO2/Lemarchand.yml +466 -0
  134. lightsmith-0.1.0/src/lightsmith/data/materials/SiO2/Malitson.yml +20 -0
  135. lightsmith-0.1.0/src/lightsmith/data/materials/TiO2/Devore-o.yml +16 -0
  136. lightsmith-0.1.0/src/lightsmith/data/materials/TiO2/Sarkar.yml +991 -0
  137. lightsmith-0.1.0/src/lightsmith/data/materials/TiO2/Siefke.yml +1226 -0
  138. lightsmith-0.1.0/src/lightsmith/data/materials/manifest.json +541 -0
  139. lightsmith-0.1.0/src/lightsmith/device.py +138 -0
  140. lightsmith-0.1.0/src/lightsmith/inverse/__init__.py +95 -0
  141. lightsmith-0.1.0/src/lightsmith/inverse/backend.py +62 -0
  142. lightsmith-0.1.0/src/lightsmith/inverse/export.py +177 -0
  143. lightsmith-0.1.0/src/lightsmith/inverse/objectives.py +82 -0
  144. lightsmith-0.1.0/src/lightsmith/inverse/optimizers.py +116 -0
  145. lightsmith-0.1.0/src/lightsmith/inverse/parametrization.py +160 -0
  146. lightsmith-0.1.0/src/lightsmith/inverse/spec.py +123 -0
  147. lightsmith-0.1.0/src/lightsmith/inverse/topopt.py +368 -0
  148. lightsmith-0.1.0/src/lightsmith/layout.py +649 -0
  149. lightsmith-0.1.0/src/lightsmith/materials.py +567 -0
  150. lightsmith-0.1.0/src/lightsmith/materials_fit.py +321 -0
  151. lightsmith-0.1.0/src/lightsmith/mcp_server.py +63 -0
  152. lightsmith-0.1.0/src/lightsmith/optics/__init__.py +5 -0
  153. lightsmith-0.1.0/src/lightsmith/optics/propagation.py +84 -0
  154. lightsmith-0.1.0/src/lightsmith/problems.py +306 -0
  155. lightsmith-0.1.0/src/lightsmith/py.typed +0 -0
  156. lightsmith-0.1.0/src/lightsmith/raster.py +133 -0
  157. lightsmith-0.1.0/src/lightsmith/results.py +189 -0
  158. lightsmith-0.1.0/src/lightsmith/spec.py +269 -0
  159. lightsmith-0.1.0/tests/test_circuit.py +356 -0
  160. lightsmith-0.1.0/tests/test_core.py +95 -0
  161. lightsmith-0.1.0/tests/test_dda.py +64 -0
  162. lightsmith-0.1.0/tests/test_eme.py +143 -0
  163. lightsmith-0.1.0/tests/test_fde.py +192 -0
  164. lightsmith-0.1.0/tests/test_fdfd.py +242 -0
  165. lightsmith-0.1.0/tests/test_fdtd.py +169 -0
  166. lightsmith-0.1.0/tests/test_fdtd_periodic.py +121 -0
  167. lightsmith-0.1.0/tests/test_inverse.py +205 -0
  168. lightsmith-0.1.0/tests/test_layout.py +174 -0
  169. lightsmith-0.1.0/tests/test_materials.py +205 -0
  170. lightsmith-0.1.0/tests/test_mie_native.py +294 -0
  171. lightsmith-0.1.0/tests/test_pwe.py +300 -0
  172. lightsmith-0.1.0/tests/test_quasistatic.py +137 -0
  173. lightsmith-0.1.0/tests/test_rcwa.py +181 -0
  174. lightsmith-0.1.0/tests/test_tmatrix_treams.py +274 -0
  175. lightsmith-0.1.0/tests/test_tmm.py +207 -0
@@ -0,0 +1,37 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Tooling caches and scratch output
13
+ .pytest_cache/
14
+ .ruff_cache/
15
+ prof
16
+ *.prof
17
+
18
+ # Node / Next.js / Tauri
19
+ node_modules/
20
+ .next/
21
+ out/
22
+ src-tauri/target/
23
+ *.tsbuildinfo
24
+
25
+ # Local secrets and databases
26
+ .env
27
+ .env.*
28
+ !.env.example
29
+ *.db
30
+ *.sqlite3
31
+
32
+ # layout files written by ad-hoc runs at the repo root
33
+ /*.gds
34
+ /*.oas
35
+
36
+ # MkDocs build output
37
+ /site/
@@ -0,0 +1,30 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI agents (and humans) using or developing lightsmith.
4
+
5
+ ## Using the library
6
+
7
+ 1. Build a problem of one of the kinds in `lightsmith/problems.py` (scattering, layer_stack, periodic, mode, device, bands, circuit, optimization); `lightsmith schema` prints the JSON Schema of all of them. Units are **nm**; wavelengths are **vacuum** wavelengths.
8
+ 2. Call `estimate` before `solve` for grid-based solvers (DDA, FDTD, FDFD, RCWA, PWE): it reports problem size, precision and memory without solving.
9
+ 3. `solve(problem, method="auto")` picks the preferred applicable method for the problem kind (see `AUTO_ORDER` in `api.py`). Errors explain why a backend was skipped and what to change.
10
+ 4. `compare(problem, methods=[...])` cross-checks methods; Mie is the reference for single spheres, otherwise the first method listed.
11
+ 5. Results carry `provenance` (backend versions, device, precision, iterations, residuals) and `warnings`. Check `warnings` before trusting numbers.
12
+
13
+ Interfaces, all JSON in / JSON out:
14
+
15
+ - Python: `import lightsmith as ls`
16
+ - CLI: `lightsmith {doctor,backends,schema,estimate,solve,compare}` — errors are JSON on stderr, exit code 1.
17
+ - MCP: `lightsmith-mcp` (stdio), tools `get_schema`, `list_backends`, `check_environment`, `estimate`, `solve`, `compare`.
18
+
19
+ ## Conventions (do not change without a schema version bump)
20
+
21
+ - Time dependence `exp(-iωt)`: lossy materials have `Im(eps) > 0`, `Im(n) > 0`. Backends using `exp(+iωt)` conjugate at their boundary.
22
+ - Cross sections in nm². Efficiencies are normalised by `π r_eq²`, with `r_eq` the volume-equivalent radius of all particles.
23
+ - `Simulation.options[<method>]` holds method-specific options, validated by that backend's own options model.
24
+
25
+ ## Developing
26
+
27
+ - `uv sync --extra gpu --extra mcp`, then `uv run pytest`. GPU tests are marked `gpu` and skip without CUDA.
28
+ - New backends subclass `lightsmith.backends.Backend` and implement `supports`, `solve` (and ideally `estimate`, `available`). Register with `@register`, or from another package via the `lightsmith.backends` entry-point group. GPL-licensed solvers must live in separate plugin packages, never in this MIT core.
29
+ - Native solvers use the array API (`device.namespace()`), never `numpy`/`torch` directly in hot loops, so one code path serves CPU and GPU.
30
+ - Every new backend needs a test against Mie (or another reference) with a stated tolerance.
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ All notable changes to the `lightsmith` library are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
5
+ [semantic versioning](https://semver.org/) (while the version is 0.x, minor releases may break the API).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - unreleased
10
+
11
+ First public release.
12
+
13
+ ### Added
14
+
15
+ - **One problem spec for every method.** Pydantic models with a `kind` discriminator: `scattering`,
16
+ `layer_stack`, `periodic`, `mode`, `device`, `bands`, `circuit`, `optimization`. Any of them can be
17
+ passed as a model, dict or JSON string. `lightsmith schema` prints the JSON Schema. Lengths are in nm,
18
+ wavelengths are vacuum wavelengths, and the time convention is `exp(-iωt)`.
19
+ - **Entry points** `solve`, `estimate`, `compare`, `choose_method` and `doctor`. `method="auto"` picks
20
+ the preferred applicable solver and explains why the others were skipped.
21
+ - **Solvers.** All are MIT, and all are native unless noted.
22
+ - Scattering: Mie (native, homogeneous and layered spheres, GPU and autodiff), Mie through
23
+ `miepython`, T-matrix through `treams` (`tmatrix` extra), DDA, FDTD, and the quasistatic dipole
24
+ approximation.
25
+ - Thin films and periodic structures: TMM (coherent and incoherent layers, differentiable), RCWA
26
+ (1D/2D lattices, conical incidence, Li and normal-vector factorisation, differentiable), and
27
+ FDTD for periodic and layer-stack problems at normal incidence.
28
+ - Waveguides and devices: FDE (full-vector, bends, PML), EME, and 2D FDFD with adjoint gradients.
29
+ - Photonic crystals: PWE (2D TE/TM and 3D, dense and LOBPCG solvers).
30
+ - Circuits: S-matrix netlist solver with compact models and Touchstone I/O, differentiable.
31
+ - Inverse design: density-based topology optimisation on FDFD (filters, projection, minimum
32
+ feature size, MMA, Adam and L-BFGS-B), exported to polygons and GDS.
33
+ - **Materials.** Bundled CC0 datasets from refractiveindex.info (Au, Ag, Al, Cu, Si, SiO2, TiO2,
34
+ water), online fetch of any database entry, and passive Drude–Lorentz fitting for time-domain solvers.
35
+ - **Layout.** Built-in GDSII reader and writer, extrusion of GDS layers into device problems, and an
36
+ optional gdsfactory bridge (`layout` extra).
37
+ - **Interfaces.** The `lightsmith` CLI (JSON on stdout, JSON errors on stderr) and the `lightsmith-mcp`
38
+ MCP server (`mcp` extra) for AI agents.
39
+ - **Plugins.** The `lightsmith.backends` entry-point group, so GPL-licensed solvers can live in separate
40
+ packages (`lightsmith-adda`, `lightsmith-scattnlay`).
41
+ - **Typing.** A `py.typed` marker; the public API is annotated.
42
+ - **Documentation** site (MkDocs Material), `CITATION.cff` and `.zenodo.json`.
43
+
44
+ ### Notes
45
+
46
+ - The `mcp` extra is pinned to `mcp>=1.2,<2`: the server uses the mcp 1.x `FastMCP` API, which mcp 2.x
47
+ renamed.
48
+
49
+ [Unreleased]: https://github.com/lightsmith-photonics/lightsmith/compare/v0.1.0...HEAD
50
+ [0.1.0]: https://github.com/lightsmith-photonics/lightsmith/releases/tag/v0.1.0
@@ -0,0 +1,29 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use Lightsmith in your work, please cite it as below, and also cite the solver backends you used (see `lightsmith backends` and `Result.provenance`)."
3
+ type: software
4
+ title: "Lightsmith: an open, GPU-native photonics suite built for AI agents"
5
+ abstract: >-
6
+ Lightsmith describes a photonics problem once, as typed JSON, and solves it
7
+ with Mie, T-matrix, DDA, FDTD, TMM, RCWA, FDE, EME, FDFD, PWE and S-matrix
8
+ circuit solvers on CPU or GPU, with differentiable inverse design. Every
9
+ result uses the same units and conventions and carries provenance, so methods
10
+ can be cross-checked directly. Python, CLI and MCP interfaces are JSON in,
11
+ JSON out.
12
+ authors:
13
+ - family-names: Feghali
14
+ given-names: Alexander
15
+ version: 0.1.0
16
+ license: MIT
17
+ repository-code: "https://github.com/lightsmith-photonics/lightsmith"
18
+ url: "https://lightsmith-photonics.github.io/lightsmith/"
19
+ keywords:
20
+ - photonics
21
+ - nanophotonics
22
+ - electromagnetics
23
+ - scattering
24
+ - Mie theory
25
+ - discrete dipole approximation
26
+ - FDTD
27
+ - RCWA
28
+ - inverse design
29
+ - GPU
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexander Feghali and the Lightsmith contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,137 @@
1
+ Metadata-Version: 2.5
2
+ Name: lightsmith
3
+ Version: 0.1.0
4
+ Summary: Open photonics suite: one typed JSON problem spec solved by Mie, T-matrix, DDA, FDTD, TMM, RCWA, FDE, EME, FDFD, PWE and circuit solvers on CPU or GPU, with differentiable inverse design, built for humans and AI agents.
5
+ Project-URL: Homepage, https://lightsmith-photonics.github.io/lightsmith/
6
+ Project-URL: Documentation, https://lightsmith-photonics.github.io/lightsmith/
7
+ Project-URL: Repository, https://github.com/lightsmith-photonics/lightsmith
8
+ Project-URL: Issues, https://github.com/lightsmith-photonics/lightsmith/issues
9
+ Project-URL: Changelog, https://github.com/lightsmith-photonics/lightsmith/blob/main/CHANGELOG.md
10
+ Author-email: Alexander Feghali <alexander.al-feghali@mail.mcgill.ca>
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: ai-agents,dda,electromagnetics,eme,fdfd,fdtd,gpu,inverse-design,mcp,mie,nanophotonics,photonic-circuit,photonic-crystal,photonics,plasmonics,rcwa,scattering,t-matrix,tmm,waveguide
14
+ Classifier: Development Status :: 2 - Pre-Alpha
15
+ Classifier: Environment :: GPU :: NVIDIA CUDA
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Scientific/Engineering :: Physics
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.10
27
+ Requires-Dist: array-api-compat>=1.11
28
+ Requires-Dist: miepython>=3.0
29
+ Requires-Dist: numpy>=2.0
30
+ Requires-Dist: pydantic>=2.6
31
+ Requires-Dist: scipy>=1.10
32
+ Provides-Extra: gpu
33
+ Requires-Dist: torch>=2.7; extra == 'gpu'
34
+ Provides-Extra: layout
35
+ Requires-Dist: gdsfactory>=9; (python_version >= '3.11') and extra == 'layout'
36
+ Provides-Extra: mcp
37
+ Requires-Dist: mcp<2,>=1.2; extra == 'mcp'
38
+ Provides-Extra: tmatrix
39
+ Requires-Dist: treams<0.5,>=0.4.7; (python_version < '3.14') and extra == 'tmatrix'
40
+ Provides-Extra: xarray
41
+ Requires-Dist: xarray>=2024.1; extra == 'xarray'
42
+ Description-Content-Type: text/markdown
43
+
44
+ # Lightsmith
45
+
46
+ **The open photonics suite — GPU-native, differentiable, and built for AI agents.**
47
+
48
+ Describe a photonics problem once, as typed JSON, and solve it with the method that fits: from nanoparticles and thin films to metasurfaces, waveguides, photonic crystals, devices and circuits. Every result comes back in the same units and conventions, with provenance, so methods can be cross-checked directly. Many solvers run on GPU and can be differentiated, which is what powers inverse design.
49
+
50
+ Documentation: <https://lightsmith-photonics.github.io/lightsmith/>
51
+
52
+ > Status: pre-alpha (v0.1). Validation numbers for every solver are in `tests/` and in the technical report (`lightsmith-paper` repository).
53
+
54
+ ## Install
55
+
56
+ ```bash
57
+ uv add lightsmith # CPU
58
+ uv add "lightsmith[gpu]" # + PyTorch for GPU solvers
59
+ uv add "lightsmith[mcp]" # + MCP server for AI agents
60
+ uv add "lightsmith[tmatrix]" # + treams T-matrix backend
61
+ uv add "lightsmith[layout]" # + gdsfactory import (Python >= 3.11)
62
+ ```
63
+
64
+ From a clone (pulls CUDA 12.8 PyTorch wheels on Windows/Linux):
65
+
66
+ ```bash
67
+ uv sync --extra gpu --extra mcp
68
+ uv run lightsmith doctor
69
+ ```
70
+
71
+ ## Quick start
72
+
73
+ ```python
74
+ import lightsmith as ls
75
+
76
+ sim = ls.Simulation(
77
+ particles=[ls.Particle(shape=ls.Sphere(radius_nm=40),
78
+ material=ls.LibraryMaterial(name="Au"))], # Johnson & Christy
79
+ medium_index=1.33,
80
+ wavelengths=ls.WavelengthRange(start_nm=400, stop_nm=800, num=81),
81
+ )
82
+
83
+ ls.estimate(sim, method="fdtd", device="gpu") # grid, steps, memory; never solves
84
+ res = ls.solve(sim) # auto -> exact Mie for a sphere
85
+ cmp = ls.compare(sim, ["mie", "dda", "fdtd"]) # cross-check methods
86
+ ```
87
+
88
+ The same from the shell, JSON in and out:
89
+
90
+ ```bash
91
+ uv run lightsmith solve examples/drude_sphere.json --method dda --device gpu
92
+ ```
93
+
94
+ ## Problems and solvers
95
+
96
+ | Problem kind | Solvers (auto order first) | GPU | Gradients |
97
+ |---|---|---|---|
98
+ | `scattering` — particles, clusters | Mie (native, miepython), T-matrix (treams), DDA, FDTD, quasistatic | ✅ | Mie |
99
+ | `layer_stack` — thin films | TMM, RCWA, FDTD | ✅ | TMM, RCWA |
100
+ | `periodic` — gratings, metasurfaces | RCWA, FDTD (normal incidence) | ✅ | RCWA |
101
+ | `mode` — waveguide cross-sections | FDE (full vector, bends) | — | — |
102
+ | `device` — S-parameters between ports | EME, FDFD (2D) | FDFD | FDFD |
103
+ | `bands` — photonic crystals | PWE (2D TE/TM, 3D) | ✅ | dense path |
104
+ | `circuit` — netlists of compact models | S-matrix circuit solver | ✅ | ✅ |
105
+ | `optimization` — inverse design | topology optimisation on FDFD | ✅ | — |
106
+
107
+ `lightsmith backends` lists what is installed. Materials: bundled CC0 datasets (Au, Ag, Al, Cu, Si, SiO2, TiO2, water) from the refractiveindex.info database, online fetch of any entry, and passive Drude–Lorentz fitting for time-domain solvers.
108
+
109
+ GPL-licensed solvers live in separate plugin packages discovered through the `lightsmith.backends` entry point (`lightsmith-adda`, `lightsmith-scattnlay`), keeping this core MIT.
110
+
111
+ ## Conventions
112
+
113
+ - Lengths in nm; wavelengths are vacuum wavelengths.
114
+ - Time dependence `exp(-iωt)`: absorbing materials have `Im(ε) > 0`.
115
+ - Scattering cross sections in nm²; efficiencies use the volume-equivalent radius, `Q = C / (π r_eq²)`.
116
+
117
+ ## For AI agents
118
+
119
+ - `lightsmith schema` prints the JSON Schema of every problem kind.
120
+ - Every CLI command prints JSON; failures print `{"error", "message"}` to stderr with exit code 1.
121
+ - `lightsmith-mcp` serves `get_schema`, `list_backends`, `check_environment`, `estimate`, `solve` and `compare`.
122
+ - See [AGENTS.md](https://github.com/lightsmith-photonics/lightsmith/blob/main/AGENTS.md) and [llms.txt](https://github.com/lightsmith-photonics/lightsmith/blob/main/llms.txt).
123
+
124
+ ## Licence
125
+
126
+ MIT. Backends keep their own licences; cite the ones you use (`Result.provenance`, `lightsmith backends`).
127
+
128
+ ## Related repositories
129
+
130
+ Lightsmith is split across sibling repositories:
131
+
132
+ | Repository | Contents |
133
+ |---|---|
134
+ | `lightsmith` (this one) | Core library, solvers, benchmarks |
135
+ | `lightsmith-adda`, `lightsmith-scattnlay` | GPL solver plugins (separate licence) |
136
+ | `lightsmith-paper` | arXiv technical report |
137
+ | `lightsmith-studio`, `lightsmith-community`, `lightsmith-cloud`, `lightsmith-materials`, `lightsmith-deploy` | GUI, web platform, compute service, materials database, full-stack deployment |
@@ -0,0 +1,94 @@
1
+ # Lightsmith
2
+
3
+ **The open photonics suite — GPU-native, differentiable, and built for AI agents.**
4
+
5
+ Describe a photonics problem once, as typed JSON, and solve it with the method that fits: from nanoparticles and thin films to metasurfaces, waveguides, photonic crystals, devices and circuits. Every result comes back in the same units and conventions, with provenance, so methods can be cross-checked directly. Many solvers run on GPU and can be differentiated, which is what powers inverse design.
6
+
7
+ Documentation: <https://lightsmith-photonics.github.io/lightsmith/>
8
+
9
+ > Status: pre-alpha (v0.1). Validation numbers for every solver are in `tests/` and in the technical report (`lightsmith-paper` repository).
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ uv add lightsmith # CPU
15
+ uv add "lightsmith[gpu]" # + PyTorch for GPU solvers
16
+ uv add "lightsmith[mcp]" # + MCP server for AI agents
17
+ uv add "lightsmith[tmatrix]" # + treams T-matrix backend
18
+ uv add "lightsmith[layout]" # + gdsfactory import (Python >= 3.11)
19
+ ```
20
+
21
+ From a clone (pulls CUDA 12.8 PyTorch wheels on Windows/Linux):
22
+
23
+ ```bash
24
+ uv sync --extra gpu --extra mcp
25
+ uv run lightsmith doctor
26
+ ```
27
+
28
+ ## Quick start
29
+
30
+ ```python
31
+ import lightsmith as ls
32
+
33
+ sim = ls.Simulation(
34
+ particles=[ls.Particle(shape=ls.Sphere(radius_nm=40),
35
+ material=ls.LibraryMaterial(name="Au"))], # Johnson & Christy
36
+ medium_index=1.33,
37
+ wavelengths=ls.WavelengthRange(start_nm=400, stop_nm=800, num=81),
38
+ )
39
+
40
+ ls.estimate(sim, method="fdtd", device="gpu") # grid, steps, memory; never solves
41
+ res = ls.solve(sim) # auto -> exact Mie for a sphere
42
+ cmp = ls.compare(sim, ["mie", "dda", "fdtd"]) # cross-check methods
43
+ ```
44
+
45
+ The same from the shell, JSON in and out:
46
+
47
+ ```bash
48
+ uv run lightsmith solve examples/drude_sphere.json --method dda --device gpu
49
+ ```
50
+
51
+ ## Problems and solvers
52
+
53
+ | Problem kind | Solvers (auto order first) | GPU | Gradients |
54
+ |---|---|---|---|
55
+ | `scattering` — particles, clusters | Mie (native, miepython), T-matrix (treams), DDA, FDTD, quasistatic | ✅ | Mie |
56
+ | `layer_stack` — thin films | TMM, RCWA, FDTD | ✅ | TMM, RCWA |
57
+ | `periodic` — gratings, metasurfaces | RCWA, FDTD (normal incidence) | ✅ | RCWA |
58
+ | `mode` — waveguide cross-sections | FDE (full vector, bends) | — | — |
59
+ | `device` — S-parameters between ports | EME, FDFD (2D) | FDFD | FDFD |
60
+ | `bands` — photonic crystals | PWE (2D TE/TM, 3D) | ✅ | dense path |
61
+ | `circuit` — netlists of compact models | S-matrix circuit solver | ✅ | ✅ |
62
+ | `optimization` — inverse design | topology optimisation on FDFD | ✅ | — |
63
+
64
+ `lightsmith backends` lists what is installed. Materials: bundled CC0 datasets (Au, Ag, Al, Cu, Si, SiO2, TiO2, water) from the refractiveindex.info database, online fetch of any entry, and passive Drude–Lorentz fitting for time-domain solvers.
65
+
66
+ GPL-licensed solvers live in separate plugin packages discovered through the `lightsmith.backends` entry point (`lightsmith-adda`, `lightsmith-scattnlay`), keeping this core MIT.
67
+
68
+ ## Conventions
69
+
70
+ - Lengths in nm; wavelengths are vacuum wavelengths.
71
+ - Time dependence `exp(-iωt)`: absorbing materials have `Im(ε) > 0`.
72
+ - Scattering cross sections in nm²; efficiencies use the volume-equivalent radius, `Q = C / (π r_eq²)`.
73
+
74
+ ## For AI agents
75
+
76
+ - `lightsmith schema` prints the JSON Schema of every problem kind.
77
+ - Every CLI command prints JSON; failures print `{"error", "message"}` to stderr with exit code 1.
78
+ - `lightsmith-mcp` serves `get_schema`, `list_backends`, `check_environment`, `estimate`, `solve` and `compare`.
79
+ - See [AGENTS.md](https://github.com/lightsmith-photonics/lightsmith/blob/main/AGENTS.md) and [llms.txt](https://github.com/lightsmith-photonics/lightsmith/blob/main/llms.txt).
80
+
81
+ ## Licence
82
+
83
+ MIT. Backends keep their own licences; cite the ones you use (`Result.provenance`, `lightsmith backends`).
84
+
85
+ ## Related repositories
86
+
87
+ Lightsmith is split across sibling repositories:
88
+
89
+ | Repository | Contents |
90
+ |---|---|
91
+ | `lightsmith` (this one) | Core library, solvers, benchmarks |
92
+ | `lightsmith-adda`, `lightsmith-scattnlay` | GPL solver plugins (separate licence) |
93
+ | `lightsmith-paper` | arXiv technical report |
94
+ | `lightsmith-studio`, `lightsmith-community`, `lightsmith-cloud`, `lightsmith-materials`, `lightsmith-deploy` | GUI, web platform, compute service, materials database, full-stack deployment |
@@ -0,0 +1,3 @@
1
+ # Backends
2
+
3
+ ::: lightsmith.backends.base
@@ -0,0 +1,3 @@
1
+ # Circuits
2
+
3
+ ::: lightsmith.circuits
@@ -0,0 +1,9 @@
1
+ # Top level
2
+
3
+ ::: lightsmith.api
4
+ options:
5
+ members: [solve, estimate, compare, choose_method, AUTO_ORDER]
6
+
7
+ ::: lightsmith.device
8
+ options:
9
+ members: [doctor, resolve_device, Device]
@@ -0,0 +1,14 @@
1
+ # API reference
2
+
3
+ Generated from the docstrings. The most-used names are re-exported from the top-level package
4
+ (`import lightsmith as ls`):
5
+
6
+ - [Top level](core.md): `solve`, `estimate`, `compare`, `choose_method`, `doctor`
7
+ - [Problems](problems.md): every problem kind, plus `parse_problem` and `problem_json_schema`
8
+ - [Scattering spec](spec.md): `Simulation`, shapes, illumination, output requests
9
+ - [Materials](materials.md): material models, the bundled library, refractiveindex.info, Drude–Lorentz fits
10
+ - [Results](results.md): result models for every problem kind
11
+ - [Backends](backends.md): the `Backend` interface and registry
12
+ - [Circuits](circuits.md): compact models, netlists, Touchstone files
13
+ - [Inverse design](inverse.md): optimisation spec, driver, export
14
+ - [Layout](layout.md): GDS import and export, the gdsfactory bridge
@@ -0,0 +1,3 @@
1
+ # Inverse design
2
+
3
+ ::: lightsmith.inverse
@@ -0,0 +1,3 @@
1
+ # Layout
2
+
3
+ ::: lightsmith.layout
@@ -0,0 +1,7 @@
1
+ # Materials
2
+
3
+ ::: lightsmith.materials
4
+
5
+ ::: lightsmith.materials_fit
6
+ options:
7
+ members: [fit_drude_lorentz, fit_quality]
@@ -0,0 +1,3 @@
1
+ # Problems
2
+
3
+ ::: lightsmith.problems
@@ -0,0 +1,3 @@
1
+ # Results
2
+
3
+ ::: lightsmith.results
@@ -0,0 +1,3 @@
1
+ # Scattering spec
2
+
3
+ ::: lightsmith.spec
@@ -0,0 +1 @@
1
+ --8<-- "CHANGELOG.md"
@@ -0,0 +1,43 @@
1
+ # Command line
2
+
3
+ The `lightsmith` command wraps the Python API. It is built for scripts and agents:
4
+
5
+ - every command prints JSON to stdout;
6
+ - every failure prints `{"error": "<ExceptionType>", "message": "..."}` to stderr and exits with code 1.
7
+
8
+ ```text
9
+ lightsmith doctor
10
+ lightsmith backends
11
+ lightsmith schema
12
+ lightsmith estimate SPEC [--method auto] [--device auto]
13
+ lightsmith solve SPEC [--method auto] [--device auto] [-o result.json]
14
+ lightsmith compare SPEC [--methods mie,dda] [--device auto] [-o comparison.json]
15
+ ```
16
+
17
+ | Command | Output |
18
+ |---|---|
19
+ | `doctor` | NumPy, PyTorch and CuPy versions, the CUDA GPUs, and the device `auto` would pick |
20
+ | `backends` | One entry per backend: `name`, `method`, `problem_kinds`, `license`, `gpu`, `differentiable`, `available`, `unavailable_reason`, `citation` |
21
+ | `schema` | The JSON Schema of every problem kind |
22
+ | `estimate` | Backend, device, problem size and memory, without solving |
23
+ | `solve` | The result model as JSON |
24
+ | `compare` | `reference`, `results` (one per method) and `relative_difference` |
25
+
26
+ `SPEC` is a JSON file of any [problem kind](problems/index.md), or `-` to read from stdin.
27
+
28
+ | Option | Values |
29
+ |---|---|
30
+ | `--device` | `auto`, `cpu`, `gpu`, `cuda:N` or `torch-cpu` |
31
+ | `--method` | `auto`, a method such as `dda`, or a backend name such as `lightsmith-mie` (see [Solvers](solvers/index.md)) |
32
+ | `--methods` | Comma-separated methods or backend names. Default: every backend that applies. |
33
+
34
+ ## Examples
35
+
36
+ ```bash
37
+ lightsmith estimate examples/drude_sphere.json --method dda --device gpu
38
+ lightsmith solve examples/drude_sphere.json -o result.json
39
+ cat spec.json | lightsmith solve - --method rcwa
40
+ lightsmith compare examples/drude_sphere.json --methods mie,dda,fdtd
41
+ ```
42
+
43
+ The [MCP server](mcp-overview.md) exposes the same operations to AI agents.
@@ -0,0 +1,112 @@
1
+ # Contributing
2
+
3
+ Contributions are welcome: solver backends, validation tests, materials and documentation. Lightsmith is
4
+ MIT-licensed. By contributing, you agree that your contribution is released under the same licence.
5
+
6
+ ## Development setup
7
+
8
+ ```bash
9
+ git clone https://github.com/lightsmith-photonics/lightsmith
10
+ cd lightsmith
11
+ uv sync --extra gpu --extra mcp
12
+ uv run pytest # GPU tests are marked `gpu` and skip without CUDA
13
+ uv run pytest -m slow # long convergence checks
14
+ uv run --group docs mkdocs serve # documentation preview
15
+ ```
16
+
17
+ ## Ground rules
18
+
19
+ - **Conventions are fixed.** Use nm, vacuum wavelengths and `exp(-iωt)`, and normalise efficiencies by
20
+ π r_eq². See [Conventions](conventions.md). Changing any of these needs a schema version bump.
21
+ - **Every backend is validated.** A new backend needs a test against Mie or another reference, with a
22
+ stated tolerance. Add the numbers to the backend's page under [Solvers](solvers/index.md).
23
+ - **One code path for CPU and GPU.** Native solvers use the array API (`device.namespace()`) and never
24
+ call `numpy` or `torch` directly in hot loops.
25
+ - **The core stays MIT.** GPL-licensed solvers must live in separate plugin packages.
26
+ - **Optional dependencies are imported lazily**, inside methods, so that a missing extra never breaks
27
+ `import lightsmith`.
28
+
29
+ ## Writing a backend
30
+
31
+ A backend wraps one implementation of one method. Subclass `lightsmith.backends.Backend`:
32
+
33
+ ```python
34
+ from typing import ClassVar
35
+
36
+ from lightsmith.backends import Backend, register, unsupported_outputs
37
+ from lightsmith.device import Device
38
+ from lightsmith.results import Result
39
+ from lightsmith.spec import Simulation
40
+
41
+
42
+ @register
43
+ class MySolver(Backend):
44
+ name: ClassVar[str] = "my-solver" # unique implementation id
45
+ method: ClassVar[str] = "dda" # the method family it implements
46
+ license: ClassVar[str] = "MIT"
47
+ citation: ClassVar[str] = "Author et al., Journal (year)"
48
+ gpu: ClassVar[bool] = False
49
+ differentiable: ClassVar[bool] = False
50
+ problem_kinds: ClassVar[tuple[str, ...]] = ("scattering",)
51
+
52
+ @classmethod
53
+ def available(cls) -> tuple[bool, str]:
54
+ try:
55
+ import my_dependency # noqa: F401 (import optional dependencies lazily)
56
+ except ImportError:
57
+ return False, "my_dependency is not installed (pip install my_dependency)"
58
+ return True, ""
59
+
60
+ @classmethod
61
+ def supports(cls, sim: Simulation) -> tuple[bool, str]:
62
+ why = unsupported_outputs(sim, near_field=False, far_field=False)
63
+ return (not why), why
64
+
65
+ @classmethod
66
+ def estimate(cls, sim: Simulation, device: Device) -> dict:
67
+ return {"unknowns": 0, "memory_bytes": 0} # cheap; never solves
68
+
69
+ @classmethod
70
+ def solve(cls, sim: Simulation, device: Device) -> Result:
71
+ ...
72
+ ```
73
+
74
+ | Member | Role |
75
+ |---|---|
76
+ | `available()` | Whether the dependencies are installed, and if not, why. It is checked before `supports`. |
77
+ | `supports(problem)` | Whether this backend can solve the problem, and if not, an actionable reason. The reason appears in `auto` selection errors. |
78
+ | `estimate(problem, device)` | A cheap pre-flight report of size and memory. Optional, but expected for grid-based solvers. |
79
+ | `solve(problem, device)` | Returns the result model for the problem kind: `Result`, `SpectrumResult`, `ModeResult`, `SParameterResult` or `BandResult`. Fill in `provenance` and `warnings`. |
80
+
81
+ Options: read `options_for(problem, cls.name, cls.method)` from `lightsmith.problems` and validate it with a
82
+ pydantic model that uses `extra="forbid"`.
83
+
84
+ To make `method="auto"` consider a new method, add it to `AUTO_ORDER` in `lightsmith/api.py`. Methods
85
+ not listed there are tried after the listed ones.
86
+
87
+ ## Plugin packages
88
+
89
+ A backend can live in its own package. This is required for GPL-licensed solvers. Register it through
90
+ the `lightsmith.backends` entry-point group in the plugin's `pyproject.toml`:
91
+
92
+ ```toml
93
+ [project]
94
+ name = "lightsmith-mysolver"
95
+ dependencies = ["lightsmith>=0.1"]
96
+
97
+ [project.entry-points."lightsmith.backends"]
98
+ mysolver = "lightsmith_mysolver:MySolver"
99
+ ```
100
+
101
+ Once the plugin is installed, `lightsmith backends` lists it, and `solve`, `compare` and the MCP server
102
+ can use it. `lightsmith-adda` and `lightsmith-scattnlay` are working examples.
103
+
104
+ ## Documentation
105
+
106
+ - The documentation lives in `docs/` and builds with MkDocs Material: `uv run --group docs mkdocs build --strict`.
107
+ - The API reference is generated from docstrings by mkdocstrings.
108
+ - New pages must be added to `nav` in `mkdocs.yml`; the strict build fails on pages missing from it.
109
+
110
+ ## Releases
111
+
112
+ The maintainers' checklist is `RELEASING.md` in the repository root.