Repository navigation
rcwa: a polarization word takes the pure 1D TE/TM path - #142
Merged
Merged
Conversation
A public rcwa.run of a one-row raster grating lit by PlaneWave(polarization='s') or 'p' now solves on the engine's pure TE/TM path (grating_type 0) instead of the conical one. No public field is added. The routing principle stays -- the declaration selects the path, and nothing else does -- and what counts as a declaration widens. The words 's' / 'p' declare the polarization fixed. A Jones pair, (1, 0) included, is a value and stays conical. The word takes the pure path only where that path answers the same light. Every test is structural, so jit and vmap decide it the same way: - every layer a one-row scalar raster on an axis-aligned cell (the routing's own one_dimensional test), with no tensor; - phi and ky absent, or a zero that nothing traces or differentiates; - the formulation is not first_order. Everywhere else the word solves bit for bit as the pair it names, silently. Unlike tetm it refuses nothing. Engine: - polarization accepts the words. The pair is stored as before, and the word in _polarization_word beside it. - _assign_grating_type keeps its refusals and assigns _routed_grating_type, which refuses and warns nothing, so a report can read it before any solve. - _polarization_word_holds is the eligibility test, and tetm_index reads a routed word. - _solve_path(planned=) names the path. - The JAX pytree carries the word in aux. - phi, kx and ky are stored under _eager(), as the material setters are. Without that a stated phi=0. was staged into a tracer inside jax.jit, and the jitted run of an eligible problem took the conical path (measured). The eligibility comparison runs under _eager() for the same reason. Adapter: the word goes to the engine as written, except under Outputs(scattering=True). The public matrix is 2N x 2N with channel 'sp', and the pure path's is N x N 'te'/'tm', so that run hands over the pair as before. The path is recorded: CheckReport.resolved['solve_path'] (planned) and Result.diagnostics['solve_path'] (per wavelength, as taken). The values are '1d_te', '1d_tm', '1d_conical' or '2d'. Nothing recorded the path before. Coherent-source runs report '2d', the layout their side-resolved first-order cascade is written on (CoherentSolver._solve_path). Measured through the public rcwa.run (NumPy, a 10-core laptop; docs/guide/rcwa/light.md "Cost"): 2.7x, 4.1x and 5.3x faster at orders (50, 0), (200, 0) and (800, 0), with a peak of 0.39 GB against 5.9 GB at the last; sum(R) 5e-13, 8e-12 and 4e-10 apart. Checks: - tests/api/test_unified_rcwa_pure_1d.py (new): the regression matrix holds the word against the pair to 1e-11 of each observable family; the worst measured gap is 1.9e-13. It also covers jax/torch backends, single precision, jax.grad, torch autograd and prepare -> jit / vmap / jit(vmap(grad)), with a kernel call counter. Fallback pins (skewed cell, nonzero phi and ky, first_order, scattering=True, traced or differentiated phi) are bit for bit against the pair. - The fallback cases ran on origin/main 93cf7cb and on this commit, through the same script: 184 arrays, all bit-identical. - tests/rcwa/test_pytree_round_trip.py lists the new aux key and checks that the word survives the round trip. Not fixed here: jax.jit of a public run whose Jones pair is exactly (0, 1) aborts the process. It does so on main too, under jaxlib 0.11.0: XLA's algebraic simplifier reads the runtime guard's complex constant as an iota and rewrites its abs. The new test writes that pair as (0, 2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…its path Public 1D runs are no longer always conical, so the statements that said they were are rewritten, in English and in Korean where a translation exists. - reference/keywords.md: the tetm row now points to the words. The closing paragraph no longer sends readers to the earlier interface, which was retired on 2026-10-15. - guide/rcwa/light.md: new section "The words are a declaration, the pair is a value", with the conditions, the fallbacks, the round-off agreement, the cost, and resolved/diagnostics['solve_path'], including what a traced run reports. - guide/rcwa/scene.md, fields.md (the pure path's exact zeros) and numerics.md (the path is not a setting). - guide/rcwa/anisotropic-materials.md section 8: the routing account and the "which path ran" requirement, which the result now meets. - learn/fundamentals light, overview and index, with their Korean pages. - development/source-map (and its Korean page) and tests/README name the new test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yonghakim
marked this pull request as ready for review
November 3, 2026 17:24
yonghakim
enabled auto-merge (rebase)
November 3, 2026 17:25
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes
A public
rcwa.runof a one-row raster grating lit byPlaneWave(polarization='s')or'p'now solves on the engine's pure 1D TE/TM path (grating_type0) instead of the conical one. No public field is added.The engine's routing principle, "the declaration selects the path, and nothing else does" (
_assign_grating_type), stays. What counts as a declaration widens:'s'or'p', is a declaration: the polarization is fixed, not a parameter anyone sweeps or differentiates.(1, 0)included.The word takes the pure path only where that path answers the same light. Every test is structural, so it is decided the same way under
jax.jitandvmap:one_dimensionaltest);phiandkyare absent, or a zero that nothing traces or differentiates;first_order, and there is no tensor.Everywhere else the word solves bit for bit as the pair it names, silently. Unlike
tetmit refuses nothing.Outputs(scattering=True)keeps the conical path: the public matrix is2N x 2Nwith channel'sp', and the pure path's isN x N,'te'/'tm'. That one fact the engine cannot see, so the adapter hands the pair over when a scattering matrix is requested (_adapt.plane_wave_polarization). Everything else is decided in the engine (_routed_grating_type,_polarization_word_holds).The chosen path is now recorded:
CheckReport.resolved['solve_path'], planned before any solve;Result.diagnostics['solve_path'], one entry per wavelength, as taken.Values are
'1d_te','1d_tm','1d_conical'or'2d'. Before this change nothing recorded the path. Coherent-source runs report'2d', the layout their side-resolved first-order cascade is written on.phi,kxandkyare stored under the solver's_eager(), as the material setters already are, so a statedphi=0.stays a concrete zero insidejax.jit. Without that the jitted run of an eligible problem took the conical path (measured).Why
Before this change a one-row raster grating always solved on the conical path. The results are identical, but the conical path is 2.8–5.8x slower and needs 5–14x more memory. No decision record ever dropped
tetmfrom the public lifecycle. The documented workaround pointed atwavebit.compat, which was retired on 2026-10-15.Measurements
Public
rcwa.run, NumPy, a 10-core laptop, 2026-11-03. The scene is a 1000 nm cell with a 560 nm bar of permittivity 12.25 in air, 200 nm thick, at 1363 nm and normal incidence, on a 50-pixel raster withfourier_coefficients='integrated'. Each'p'run is measured against the pair(0, 1), one process per run. Times are the best of 7/3/1 runs; memory is the peak footprint above the process baseline.orders'p'(pure TM)(0, 1)(conical)sum(R)apart(50, 0)(200, 0)(800, 0)These agree with an earlier out-of-tree prototype measured on the same machine: 0.017 / 0.28 / 10.5 s, and 25 → 5 MB, 258 → 48 MB, 5.98 → 0.39 GB.
Verification
tests/api/test_unified_rcwa_pure_1d.py, 66 tests, all passing locally.phi=0, the kx spelling, raster mu, a lossy bar, half-space merge, a spectrum, ETM, detuning, nvf, laurent+integrated, explicitsecond_order, magnetic half-spaces, a negative-index substrate, a Rayleigh point and evanescent incidence; fields and boundary fields on both sides.jax.gradwith respect to thickness, theta and permittivity; torch autograd.preparethenjit,vmapandjit(vmap(grad)), with a kernel call counter confirming that the pure kernel ran.phi, a nonzeroky,first_orderandscattering=True(2N x 2N,'sp'). A Jones pair stays conical. A traced or differentiated azimuth stays conical underjit,grad,jit(grad),vmapand torchrequires_grad, bit for bit with the pair. A traced run reports the plan'1d_te'and records'1d_conical'.'2d', planned and taken.git archive origin/main(93cf7cb) and on this branch. All 184 arrays are bit-identical. The arrays cover amplitudes, efficiencies, powers, fields, boundary fields, S blocks, jit/grad/vmap values, torch gradients and single precision.not slow and not research, 4 workers): 2011 passed, 1 failed. The failure istest_torch_compile.py::test_canary_inductor_compiles_the_eye_write_in_order, caused by a stale Inductor precompiled header in this machine's temp directory (mtime changed). With a cleanTMPDIR, all 20 torch-compile tests pass. The 3 slow tests in those files were not run locally.tools/run_verification.py --profile quick: 16 passed, 1 failed. The failure istest_installed_metadata_matches_the_checkout_distribution_contract, which needs installed package metadata; this run usedPYTHONPATHonsrc.sphinx-build -W -b html docsbuilds clean, andtests/api/test_docs_contract.pypasses (166).wavebit, Python 3.12, numpy 2.5.1, jax/jaxlib 0.11.0, torch 2.14.1, macOS arm64.Found, not fixed here
jax.jitof a public run whose Jones pair is exactly(0, 1)aborts the Python process. It happens on main as well, with jax/jaxlib 0.11.0: XLA's algebraic simplifier reads the runtime guard's complex constant[0, 1]as an iota and rewrites itsabsto a value of the wrong type.(1, 0)and the word'p'are unaffected. The new test writes that pair as(0, 2), which normalizes to exactly(0, 1).migration.py:81still calls.nameon the normalizedtetmstring, so migrating a v10 document that declarestetmraisesAttributeError.🤖 Generated with Claude Code