Skip to content

Shorter start-up: optmat 0.9.1, lazy imports, no numba on the native backend - #15

Merged
jtravs merged 59 commits into
mainfrom
perf/startup-latency
Oct 24, 2026
Merged

jtravs merged 59 commits into
mainfrom
perf/startup-latency

Conversation

@jtravs

@jtravs jtravs commented Oct 24, 2026

Copy link
Copy Markdown
Contributor

Based on #9. Until #9 merges, this diff also shows #9's commits. Once #9 is merged (with a merge commit), only the five commits below remain, and this PR's CI already covers the combined code.

Shortens the latency before a run starts. Example: a 0.15 bar helium capillary with ionisation, 4 modes, 6 fs at 800 nm, on the native backend, with the fibre cut to 5 cm (setup doesn't depend on length).

main this branch
Warm start (best of 5, runs alternated) 5.49 s 3.13 s
Cold start (fresh install, first run; two rounds) 52.6 s, 54.4 s 19.5 s, 20.0 s
Modules loaded at the end of the run 1,745 1,415
numba imported yes no
optmat data files opened 8,849 81

Measured on an Apple M5 Max under Microsoft Defender, which scans every newly opened file; that's most of the cold cost on main. Precompiling bytecode at install (UV_COMPILE_BYTECODE=1) takes the branch's cold start to about 16 s (one sample).

Commits

  1. optmat 0.9.0. _resolve tells an atom from a molecule, and lists a family's model names, with ionisation_rate_coverage / molecular_rate_coverage, instead of building both rate registries. Only the model asked for is built. optmat 0.9.0 also reads only the data a model needs (Read only the data a model needs optmat#7).
  2. Lazy imports. scipy.signal (half a second) loads only for analytic_signal and the spectrogram's gate. matplotlib.pyplot loads only in the functions that draw. matplotlib's types stay imported, so the test suite's runtime type checks still see Axes and Figure.
  3. No numba on the native backend. The set-up z-table evaluations (clenshaw, called about 1,000 times from build_phi) compiled or loaded the numba kernel, so they imported numba, which a native-backend run needs for nothing else. Until numba is loaded, clenshaw uses NumPy's chebval, which gives the same values bit for bit; after that the compiled kernel is used as before. A new test checks the two paths agree exactly.
  4. CHANGELOG line.
  5. optmat 0.9.1. Ionisation parameters read one gas's fit, not all fifteen (Read one gas's fit where only its fit is needed optmat#8). The floor is raised to 0.9.1.

test_tdse_entry.py's recorded structure hash is re-recorded. It moves with the optmat version string in the citations, and 0.9.0 also corrected the wording of the helium rate's description. No number changed.

Checks

Targeted tests passed locally: translate, plotting, processing, the Fourier helpers and the optmat-rate tests. Ruff and basedpyright are clean. The full gate is left to CI.

🤖 Generated with Claude Code

jtravs and others added 30 commits October 23, 2026 23:46
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SpatialPulse.incident gives the beam its requested energy or peak power in
the paraxial measure n eps0 c dA/2 of the launch medium, over the closed-form
plane integral of its profile, and only then samples it at given points, so
light a receiver misses still counts as incident. It agrees with build on a
radial grid to the beam's obliquity.

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

from_waveguide and prop_waveguide take incident=SpatialPulse in place of the
node-sampled transverse= profile. The beam is normalised over its own plane
in the core medium, sampled at the guide's nodes and projected; the captured
amplitudes are launched without rescaling to energy=, and a zero capture is a
valid zero launch. IncidentLaunch records the incident, captured and
uncaptured energy per frequency in configuration["launch"], replacing the
3 % uncaptured warning and LAUNCH_UNCAPTURED_WARNING.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reword the general modal, supplied-guide and solid-fibre pages and the
graded-index tutorial for incident=: energy= is the physical energy over the
whole beam in the core medium, the channels launch what they capture, and
the account is recorded in the run's configuration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
FreeSpace (exact angular spectrum on the radial QDHT or Cartesian DFT, with
direct angular or Fresnel summation onto an independent output plane),
ThinLens, Aperture, TransmissionMask and Alignment as certified thin screens,
PolarisationTransform, Mirror with explicit p = s x k frames, the
normal-incidence PlanarInterface and SpectralTransfer for optics transfer
functions, chains and compressor responses. Each returns the new plane and a
joule-ledger report measured with SpatialFlux. The package __init__ is a
placeholder for the main SEP 31 branch's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
transport() applies elements in order, adapts a Chain or known transfer
function as one frequency-only block at its position, and sums losses,
removals and approximation errors in joules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Gaussian waist, curvature and Gouy phase; direct Fresnel against the angular
spectrum and adaptive quadrature; first-order convergence to Fraunhofer; the
Airy pattern; the thin-lens Fourier-plane waist; radial against Cartesian;
alignment, mirror and Jones signs; Fresnel R + T = 1; spectral attenuation,
efficiencies and calibration records; thin-screen certificates; refusals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
A physics-layer package for plane exchange: FieldPlane with its
reference, geometry, metric, basis identity and provenance; InputPort
and the bound CouplingLaunch; the same_basis identity model; and
regrid, the direct finite-time Fourier remap with its loss report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Runs persist the plane-reference record (frame integral, carrier,
inherited origin, parent and basis identity per planned plane);
processing.field_plane reads a saved plane back; Simulation.input_port
describes the entrance and with_input installs a bound launch,
recording its coupling provenance and drawing no new noise. The layers
figure draws the new modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
A draft coupling chapter (field planes, regrid, same-basis relaunch),
the plane-reference record on the provenance and output pages, the
architecture's package table and layer edges, the API index and the
changelog. Cross-references use the home modules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
transport() now takes a radial or Cartesian FieldPlane and returns the
transported plane with its ledger. The frame term each FreeSpace removes
is added to the plane's reference (from the plane's Frame when given,
otherwise beta1(omega0) L, recorded as such); spectral calibration
records clear the reference terms an element drops; a mirror turns the
plane's axes with its polarisation; the metric is resolved again with
SpatialFlux.resolve in the output medium and grid; each operation is
appended to the provenance history. A modal plane is refused in favour
of Outcouple.

Every refusal raises CouplingError. Rays leaving the window now refuse
unless beyond_window='remove' declares the angular band removal, which
is reported. Aperture gains weighting='amplitude'|'quadrature' with the
meaning of the in-run aperture statistics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
…rt core

The coupling chapter gains a self-contained transport section: the
elements, their order, the plane record, free-space output methods, the
thin-screen certificate, mirrors and Jones frames, the Fresnel interface,
spectral optics with Chain, and a lens-to-focus example. The structure
figure draws coupling/transport and coupling/elements solid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
transport, the function, shadows its module in solprop.coupling, as
physdata.species does; TransportReport gets a hand-written API page.
The element module's callable aliases become private so the generated
reference lists no undocumented targets, and a docstring line is
rewrapped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
…e 3)

couple(plane, port, model=CouplingModel(kind, model_error=...)) is the one
public path from a field plane to a target-bound launch, with an immutable
CouplingReport. same_basis (exact copy, declared basis change, F^-1 B
recovery) replaces the phase-2 function; scalar_paraxial fits scalar beams
and capillary or weak-guidance step-index planes onto HE1m/LP0m profiles in
the common paraxial measure; vector_paraxial recovers coordinates in a
general modal guide with the flux pairing and the forward H closure.
Admission items: angular tails, contrast screen or recorded pair envelope,
metric defects, H closure, passive-map certificate and numerical errors.
Ports carry scalar profiles, the core reference index and contrast; field
planes from analytic fields record domain and resolution checks; modal
planes read with their simulation carry their profiles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
from_waveguide(provider=..., incident=..., coupling=CouplingModel(
'vector_paraxial', model_error=...)) builds the beam's radial plane in the
core index and launches what couple recovers, through with_input; the
CouplingReport replaces IncidentLaunch as configuration['launch']. A
strongly guiding core is refused by the contrast screen. The incident tests
move to a weakly guiding strand; the graded-index benchmark declares its
model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
…owed

The package exports couple(); a submodule of the same name made
solprop.coupling.couple ambiguous and the API reference unable to document
both. Also makes the incident beam grid's constants private and fixes the
envelope module's cross-references.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
The coupling chapter gains the models, the admission budget and report, a
Gaussian into a capillary, a capillary-radius change by direct overlap and
through Outcouple, a solid-fibre overlap and the vector entry; the general
modal, solid-fibre, supplied-guide and interior-launch pages, architecture,
the API index and the changelog follow; Part II records the phase 3 report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
…ificate

field_plane now takes the certificate from flux_from_record's FluxPlan, so a
FieldPlane, CouplingReport and TransportReport carry the frozen SEP 34 record
instead of the raw output dict; the coupling report prints it. Also sorts the
package's imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
A beam focused through 20 mm of 3 bar argon at B_lin = 0.3 rad (12.1 uJ),
propagated linearly and with Kerr, each entrance plane coupled into the
same 75 um capillary with couple(scalar_paraxial) and run through 5 mm.
Kerr preparation moves 2.4 % of the incident energy from HE11 to HE12.
Runs in about 5 s on a docs build.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
The capillary envelope compared the scalar J0 profile with MarcatiliMode.field,
which is the same function; that check is now labelled as the quadrature and
implementation consistency test it is. The envelope is re-measured against
Marcatili's leaky HE1m core field J0(u(1 - i delta)r/a), delta = |nu_n|c/(omega a),
over its declared range (delta to 1e-2): largest 2.9e-3, bound 6e-3, allowance
scaled linearly by the pair's energy-weighted delta. Bins outside an envelope's
range (a capillary's delta, a fibre's V across the band) are counted in full.

Also: the modal overlap quadrature enters as 2 delta + delta^2; the angular items
say they are cone-rule allocations; a test pins the launch phase of a displaced,
tilted beam; the package docstring's transport reference resolves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
jtravs and others added 20 commits October 24, 2026 10:10
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Cover the remaining refusals and edge branches of the coupling plane,
port, regrid and outcoupling, the saved-plane extraction, the
translated port metric, the planar input port and the incident beam.
A spatial run's writing simulation always holds its medium, so the
extraction resolves its flux without a dead None branch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
Cover every refusal and edge branch of solprop.coupling.coupler and
solprop.coupling.models: declared basis changes per bin and of the wrong
shape, the vector model's Cartesian, modal and zero planes and its
refusals, the F^-1 B recovery of a general plane, and the scalar profile
sets. The F^-1 B recovery now solves only the occupied bins: off a
guide's support its stored E/H are zeros and F is singular there. Two
branches the scalar path cannot reach are folded away.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
…ndow edge

field_plane refuses a run with the modified noise model, whose auxiliary
noise phase is not reconstructed into a plane. FreeSpace gains
edge_tolerance (default 1e-4): on the same-grid route a propagated field
filling the window's outer band is refused, suggesting a larger window or
beyond_window='remove'. SEP 31 records both maintainer decisions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
…an check

scalar_paraxial now couples a radial or Cartesian plane into a step-index
fibre's LP01 within STEP_INDEX_BEAM_ENVELOPE: the scalar capture against the
field's full-vector flux coupling into the exact HE11 pair (H from its own
plane-wave spectrum), maximised over every field in the widest admitted cone.
The discrepancy is 0.467 Delta at V 1.5, so the bound, 1e-2 at contrast 0.01,
scales with the fibre's contrast; contrast and V outside the range are refused
by name, and band bins beyond the V range are counted in full.

same_basis's F^-1 B recovery checked that a field lies in the port's span
against the port's metric defect only; it now also allows the plane's own
measured defect against its flux on the common nodes, and reports both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
… guides

VECTOR_STEP_INDEX_ENVELOPE bounds the reflection the vector model omits at a
step-index entrance facet against an independent full-vector mode-matching
solution (tests/facet_mode_matching.py): bound 1.4e-3 at contrast 0.05, V 1.5
to 8, scaled by the squared contrast. A step-index port carries its normalised
frequency; the envelope item is counted, a guide outside the range is refused
by name, and a graded-index guide keeps an uncounted item without a value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
…-coupling

# Conflicts:
#	CHANGELOG.md
#	docs/dev/seps/sep-0031-simulation-coupling.md
#	src/solprop/coupling/coupler.py
#	src/solprop/coupling/envelopes.py
#	tests/coupling/test_envelopes.py
The vector step-index facet envelope sampled x-polarised Gaussians in an 8a
cylinder and missed guided modes just above cutoff and wide beams, so its
largest recorded term (6.9e-4) was understated about twofold. It is now
measured as the beam envelope is: the largest eigenvalue modulus, per
azimuthal order, of the summed exact-minus-model capture forms inside the
widest admitted cone, in a 30a cylinder with each order's guided count held
to the exact cutoffs. Largest resolved 1.371e-3 at V 3.905; modes near cutoff
approach the normal-incidence Fresnel ceiling 1.39e-3, and the bound is
twice that, 3.0e-3 at contrast 0.05. Also corrects the beam envelope's facet
aside (0.28 Delta^2), the scope of its V 1.5 checks, and the fibre-pair order
claim.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
…lation-coupling

# Conflicts:
#	docs/dev/seps/index.md
The target angular item built the whole cone by node phase matrix at once; a
fine node table and a wide cone made it 6.3 GiB on the Windows runner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
… roadmap

The closing section records what the SEP delivered, the end-of-SEP CI evidence on
pull request #9 and the fate of every phase 5 open question, and lists what moves
to the roadmap: regrid's band loss in joules, field planes in the output schema,
the general guide's angular item at every bin, a public per-bin spatial energy
spectrum, a graded-index facet envelope, modified-noise planes, the envelopes
beyond their measured range, other exit interfaces for Outcouple, vector coupling
into a moving general guide and two slow envelope tests. The SEP is archived, the
pages it produced name it, and architecture records the processing to
translate.flux edge as deliberate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019qNYqtCKh4iTKj7544yARb
optmat 0.9.0 reads only the data a model needs. _resolve tells an atom
from a molecule, and lists a family's model names, with optmat's
ionisation_rate_coverage and molecular_rate_coverage instead of building
both rate registries; only the model asked for is built.

The capillary configuration's structure hash moves with optmat's helium
rate: its reasons text was corrected in 0.9.0 (no number changed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scipy.signal (half a second to import) served one hilbert call and the
spectrogram's gate, and matplotlib.pyplot (its backends) only the
functions that draw; both now load there, so importing solprop.driver or
solprop.plotting no longer pays for them. matplotlib's types stay
imported, so the runtime type checks still see Axes and Figure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first z-table evaluation compiled (or loaded) the numba Clenshaw
kernel, and so imported numba, which a run on the native backend needs
for nothing else. Until numba is loaded, clenshaw now evaluates with
NumPy's chebval, the same values bit for bit; once it is, the compiled
kernel is used as before. A test checks the two paths agree exactly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
optmat 0.9.1 builds only the fitted model an ionisation rate's
polarizability needs. The capillary configuration's structure hash
moves with the optmat version in its citations (no other change).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 24, 2026 14:43

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

optmat ships a py.typed marker since 0.8.1, so basedpyright holds imports
to optmat's __all__: optmat.gas.air uses air_equation_of_state but does
not export it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jtravs added a commit that referenced this pull request Oct 24, 2026
@codecov

codecov Bot commented Oct 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@jtravs
jtravs merged commit 986e58f into main Oct 24, 2026
14 checks passed
@jtravs
jtravs deleted the perf/startup-latency branch November 6, 2026 12:13
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.

2 participants