Skip to content

feat(field): the continuity rule on shape structures, through Result.field - #139

Draft
yonghakim wants to merge 3 commits into
mainfrom
claude/continuity-shape-path
Draft

yonghakim wants to merge 3 commits into
mainfrom
claude/continuity-shape-path

Conversation

@yonghakim

@yonghakim yonghakim commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

discontinuous='continuity' now works on shape structures, and Result.field takes it. The default stays 'fourier': fields, R, T and amplitudes hash the same as main on six raster and shape cases.

What changed

  • Shapes. The rule reads the line through each sample off the outlines, so a wall sits where the shape says, between any two samples, and the same arithmetic runs under jit with no plan.
  • Golden. golden_reticolo_2d_field_off_grid.npz holds RETICOLO champ=1 and champ=0 on walls no raster draws: 3.6e-11 at worst, where the Fourier sum's E_x is 0.43 away.
  • Public door. Result.field(discontinuous='continuity') answers what the solver's door answers, to rounding. It reads the result's own copy, so later edits and runs do not reach it.

The rule (Lalanne & Jurek 1998, RETICOLO champ=1) and the inverse rule (Li 1996) are cited in the guide.

What the rule reads

The solve now keeps a copy of its structure and the rule reads only that. Reading the live structure moved the field by 0.134 on a shape edit, 0.67 on a raster assignment and 0.28 on matrices handed to solve_conv, and none of those is the field of either scene. This replaces a digest guard, which is gone. The derivative runs through the copy exactly when it ran through the solve.

Limits

  • Refused by name: slanted and curved walls (so circles), a differentiated or traced angle, one order along an axis, a solve of sources, first-order layers, skew cells, tensor materials, merged layers, and matrices from solve_conv.
  • Raster under jit: explicit coordinates are refused, because choosing the pixel needs concrete values. Counts work (Continuity rule on a raster: explicit coordinates under jax.jit #137).
  • Negative lattice side: a shape is refused when the side is concrete. A raster gives what the positive basis gives. Under a trace, either answers NaN where the rule reads.
  • Sample on a wall: one more than about 60 periods from the cell can read the other side.
  • Metals: a singular inverse rule on a sampled line is NumericalError rcwa.singular, or NaN in E_x on JAX.
  • Memory: 16.8 MB per wavelength for each 1024 by 1024 raster layer, kept whether or not the rule is asked for.
  • Derivative: it holds each sample fixed, so an objective over an area takes 'fourier'.

The spelling discontinuous reaches the public API here for the first time, so this is the cheapest moment to rename it.

Verification

  • 111 field-related test files, not slow or research, on the final tree: 6114 passed, 70 skipped, 2 failed. The two are test_layer_selection_and_no_scattering_or_compat_import and test_canary_inductor_compiles_the_eye_write_in_order, and both fail the same way on 93cf7cb1 in this environment.
  • tools/run_verification.py --profile quick: 16 passed, 1 failed. test_installed_metadata_matches_the_checkout_distribution_contract fails on 93cf7cb1 too, because this environment has no installed wavebit metadata. So quick is not green locally.
  • tests/api/test_docs_contract.py: 166 passed. The Sphinx build was not run.
  • Second commit, on its own tree, which holds all of the first commit's source and tests: 210 passed on the rule suites and the golden.
  • 85 code mutants on the final code: 84 caught, with a no-change control run first. The survivor, one count: steps sign, does not change behaviour. Four of the 84 survived the first run and got tests that now catch them.

The slow and research tiers were not run locally. Mark Ready to run required CI.

Related

Follow-ups filed from review: #132, #133, #134, #135, #136, #137, #138, and a comment on #130 (one global phase). #120 also edits docs/guide/rcwa/fields.md, so whichever lands second rebases.

🤖 Generated with Claude Code

yonghakim and others added 3 commits October 31, 2026 07:32
…g x and y

reconstruct_field(discontinuous='continuity') refused shapes, because it read
its operators and eps off raster pixels. It now reads the line through each
sample off the outlines: where the edges cross it, with the exact staircase
coefficients _segment_series already gives, and a nonzero winding for inside.
A wall sits where the shape says, between any two samples, and the same
arithmetic runs under jit with no plan. The line is folded once to the
outline's lowest vertex and each vertex counts once, so a sample on a wall
reads one side at any distance from the cell up to about 60 periods.

The rule read the live structure and combined it with the last solve's modes.
An in-place shape edit or an assignment moved its field by 0.134, a raster
assignment by 0.67, and matrices handed to solve_conv by 0.28: the field of
neither scene. So the solve now keeps a copy of its structure and the rule reads
only that (continuity.solved_structure); a derivative runs through the copy
exactly when it ran through the solve. Matrices from solve_conv have no structure
to read and are refused by name. The Fourier sum reads none of this.

Against a raster drawing the same walls it agrees to 5e-12 on one and two
layers, painted and nested scenes, either winding, both sides and samples on
walls and outside the cell; on Li against RETICOLO champ=1 to 4e-11; and eps E
has no orders past the truncation on any factorization. The Fourier sum is
bit-identical to main.

Refused by name: slanted and curved walls, where D_n rather than D_x is
continuous and no reference ranks the rule yet; a differentiated angle, which
can turn a wall; one order along an axis, where the solve averages what the lines
read; and a negative lattice side on shapes. The derivative holds each sample
fixed, so an objective over an area takes 'fourier', and a test pins that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The same two-layer stack as golden_reticolo_2d_field.npz with its walls on no
pixel line, under champ=1 and champ=0. It is what pins the line-wise rule on a
shape structure on its own, since a raster cannot state these walls: 3.6e-11
at worst, where the Fourier sum's E_x is 0.43 from champ=1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Result.field(discontinuous='continuity') answers what the solver's door answers,
to rounding, on shapes, rasters, one-row rasters and either incidence side. The
default stays 'fourier' and is bit-identical to main; the rule is named in the
field's metadata under 'discontinuous' only when it was asked for.

A result answers from its own snapshot. FieldState keeps the solve's copy of the
structure, whether its materials are tensors, and a copy of the lattice, so an
edit to the solver or a later run does not reach it. All of that rides the pytree,
and pickle and deepcopy still work after a pass through one.

A raster on a negative lattice side now gives what the positive basis gives in every
component; under a trace, where the sign is not known, a negative side answers NaN
where the rule reads. A singular inverse rule on a line a sample is on fails as
NumericalError code rcwa.singular, and one no sample is on does not fail the read.
A uniform layer takes the rule's derivative, not the sum's, whenever a derivative
runs through it, and stays bit-for-bit the sum without one.

Refused by name, with code field.discontinuous and path discontinuous: a solve of
sources, first-order layers, skew cells, tensor materials, merged layers, and on a
raster under jit explicit coordinates, since choosing the pixel under a sample
needs concrete values. Counts always work.

The guide covers both this door and the shape path of the previous commits. Each
1024 by 1024 raster layer costs 16.8 MB of copy per wavelength, kept whether or not
the rule is asked for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant