Builder and Data Contracts
Builder inputs, data contracts, pack creation, and the main integration flow.
For sampled-input semantics, see Choosing the Input Hold.
Builder is TDSE Runtime's Authoring API and pack-creation workflow, not a
separate product, package, or library. Use this chapter when you already have
validated H data, optional IR data, or source data that Builder can convert,
and you need to produce a pack that Runtime can load. It explains the Builder
setup flow, the shape rules that matter at the handoff, and the checks worth
running before you blame Runtime for a bad pack. This chapter stops at the
pack-to-runtime handoff; the full runtime lifecycle and step loop live in their
own chapters.
If your starting point is already FRF data, impulse-response data, or a Builder-ready workflow output, start here. If your starting point is a circuit netlist, RAW case, or Touchstone-driven circuit flow, start with TDSE Circuit instead.
Purpose
Use Builder to turn validated time-domain H data, optional IR data, or
Circuit-produced spectra into a binary pack that Runtime can load. Builder owns
shape validation, pack metadata, and serialization; Runtime owns stepping after
the pack bytes are loaded.
5-Minute Path
tdse_builder_t* b = NULL;
tdse_builder_create(&b);
tdse_builder_configure(b, cfg);
h_desc.dt = cfg.dt;
tdse_builder_apply_h(b, &h_desc);
tdse_builder_write_pack(b, "model.pack");
tdse_builder_destroy(b);
Expected Output Sample:
builder: configured np=1 nq=1 nh=128 dt=1e-05
builder: wrote model.pack
runtime: pack validation OK
Production Path
For release builds, validate inputs before tdse_builder_apply_h, write the
pack to a controlled artifact directory, run tdse_pack_validate, inspect pack
summary metadata, and keep a small Runtime create/step smoke test beside the
pack-producing job.
Troubleshooting
- If
tdse_builder_configurefails, checknp,nq,nh, anddtfirst. - If
tdse_builder_write_packfails, verify the output directory exists. - If Runtime rejects the pack, run
tdse_pack_validatebefore debugging the host step loop.
Related Docs
Related Chapters For Runtime lifecycle semantics after pack creation, see Runtime Lifecycle. For step execution inside the simulation loop, see Step Execution.
Use this chapter when your integration starts from one of these Builder-side artifacts:
| Starting artifact | Builder role |
|---|---|
validated H tensor | attach H, write pack, hand off to Runtime |
validated H plus optional IR | attach both, preserve shape and horizon contracts |
| matrix or circuit workflow output | accept Builder-ready handoff from Circuit or workflow API |
Prerequisites
- The complete TDSE installation (
tdselibrary plus Builder headers). - A generated pack input (
Hrequired,IRoptional). - Build/test workspace ready:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release --parallel 4
Quick Start
Both paths below end with a written pack and a small handoff check. Use the minimal path for prototyping; switch to the production path before release.
Minimal path
tdse_builder_t* b = NULL;
tdse_builder_options_t cfg = tdse_builder_options_init();
cfg.dt = dt;
cfg.nh = nh;
cfg.port_count = np;
cfg.output_count = nq;
tdse_builder_create(&b);
tdse_builder_configure_ex(b, &cfg); /* port_count/output_count/nh/dt */
h_desc.dt = cfg.dt; /* must match configured Builder dt */
tdse_builder_apply_h(b, &h_desc); /* required */
tdse_builder_apply_ir(b, &ir_desc); /* optional */
tdse_builder_write_pack(b, "model.pack");
tdse_builder_destroy(b);
Then hand the pack to Runtime Lifecycle for create/destroy behavior and to Step Execution for the simulation loop.
Minimal host-side interpretation:
- Builder owns pack construction and pack metadata
- Runtime owns create, step, and destroy after pack bytes are handed off
- the host owns artifact selection, pack storage, and when to start Runtime
Production path
For production integration, add these practices on top of the minimal flow:
- Validate pack bytes before runtime create (
tdse_pack_validate). - Inspect the pack summary before create so shape and metadata mismatches are visible early.
- Archive a Builder snapshot (
tdse_builder_info(...)) next to the pack or release artifact. - Run one deterministic runtime smoke check: create the model, read
tdse_model_info(...), and verify a known-good step path. - Archive
tdse_model_create_diagnostics_ton non-OK create paths. - Run threading stress and contract tests before promoting binaries.
Runtime handoff note:
- use Runtime Lifecycle for
create,close,destroy, andrelease - use Step Execution for prime, trial, commit, and post-commit queries
- use Runtime API Summary when you need a fast symbol map instead of a narrative walkthrough
Smallest Supported Builder -> Runtime Handoff
If your team needs the shortest credible integration contract, treat the handoff as these four checks:
-
Builder writes one pack successfully
-
tdse_pack_validateaccepts that pack -
Runtime create succeeds and
tdse_model_info(...)matches expectednp,nq,nh, anddt -
one prime-and-step smoke path completes without status errors
Validation Checklist
-
tdse_pack_validatepasses on release pack. - Minimal step loop returns
TDSE_STATUS_OKend-to-end. - One failure-injection case per major status is covered.
- Threading stress case is green on target build.
- Runtime outputs are stable across repeated runs with same input.
- Linux deployment claims match the current TDSE Runtime Linux support scope when shipping on Linux.
Parameter Cookbook
| Parameter | Meaning | Recommended Baseline | Notes |
|---|---|---|---|
np | primary input count | system-dependent | Must match host primary vector width. |
nq | operator row count | >= np | Use full rectangular view when nq > np. |
nh | history taps | start with the model-required value | Higher nh raises history cost. |
dt | step interval | fixed per model | Runtime Pack 1.0 requires an exact positive whole number of nanoseconds. |
IR length | independent sequence horizon | cover whole simulation window | Out-of-range returns TDSE_STATUS_OUT_OF_RANGE. |
| runtime handles | parallel threads | one handle per worker | Never step same handle concurrently. |
Data Contracts
TDSE Runtime is strict about dimensions, layout, and ownership. Most early integration failures are shape mistakes rather than numerical mistakes.
Primary Dimensions
Four dimensions define most Builder-to-Runtime compatibility:
| Symbol | Meaning | Practical Rule |
|---|---|---|
np | primary input count | must match host primary vector width |
nq | output-equation count | must satisfy nq >= np |
nh | history tap length | must match the supplied H tensor depth |
dt | model step interval | must be consistent across Builder and Runtime expectations |
Runtime Pack 1.0 Time-Step Contract
Runtime Pack 1.0 stores the simulation step as a positive integer number of
nanoseconds. For a pack-producing Builder flow, configured dt must therefore
be finite, positive, at least 1 ns, and exactly representable as whole
nanoseconds. Values such as 10 ns, 100 ns, 1 us, and 10 us are valid;
sub-nanosecond and fractional-nanosecond values such as 0.5 ns and 1.5 ns
are rejected. The writer does not silently round them.
The written header h_dt, the TIME_CONTRACT nominal step, and
TIME_CONTRACT.dt_ns all describe the same canonical value:
dt = dt_ns / 1e9. Pack inspection reports this integer as dt_ns. Verify it
before Runtime create whenever the host time step is safety- or
reproducibility-critical.
API Families
At the Builder boundary, the main API families are:
Builder lifecycle
tdse_builder_createtdse_builder_configure_extdse_builder_apply_htdse_builder_apply_irtdse_builder_write_packtdse_builder_destroy
Canonical selector boundaries
tdse_builder_set_pack_metaaccepts rawint32_tform/domain valuestdse_builder_h_from_spectrumaccepts a fixed-width request containing correction, parallel-mode, and tail-preset valuestdse_builder_irc_profile_selectandtdse_builder_irc_compressaccept fixed-width IRC selectors
Use these entrypoints when selector values originate in a file, RPC, plugin, language binding, or other untrusted integer boundary. They validate the raw representation before converting it to an enum. The older enum-by-value APIs remain available for source compatibility and for callers that already hold valid C enum values; they are not the canonical raw-integer trust boundary.
Pack validation and inspection
tdse_pack_validatetdse_pack_inspecttdse_runtime_pack_inspect_summarytdse_pack_error_token
Runtime handoff surface
tdse_model_createtdse_model_info
For full Runtime lifecycle and step APIs, switch to Runtime Lifecycle, Step Execution, or Runtime API Summary.
Ownership Model
- Builder descriptors such as
tdse_h_desc_tandtdse_ir_desc_tare borrowed views - Pack-validation and pack-inspection outputs are caller-owned result structs
- No Core API transfers ownership of caller-owned
H,IR, or step-output buffers
Failure Atomicity
Builder commits state and caller outputs only after validation, finite-result checks, and required allocations succeed:
- failed
tdse_builder_apply_h(...)ortdse_builder_apply_ir(...)calls leave the handle's previously attached payload and pack-producing state unchanged - failed uniform-
Hconversion leavesh_out, and for the piecewise helper bothtau_outandh_out, unchanged - failed Spectrum conversion, including a fatal Tail-processing failure, leaves
h_outand the optional effective-nhoutput unchanged - failed IRC compression leaves a previously initialized/reusable output buffer unchanged
Finite inputs that overflow while deriving weights, moments, or time-axis
values return TDSE_STATUS_NUMERIC; they are never reported as successful
non-finite output. Allocation failures return TDSE_STATUS_OUT_OF_MEMORY.
Pack-file commit has a separate three-outcome contract described under
Write Gate Checklist.
Compatibility Equation
Builder-side and Runtime-side assumptions are compatible only when all of these are true:
- Builder
np,nq,nh, anddtmatch the intended runtime model His laid out exactly as declared by its descriptor- optional
IRis laid out exactly as declared by its descriptor - runtime host buffers are sized from runtime-reported dimensions rather than from memory or guesswork
H Tensor Contract
H uses tap-major row-major storage: shape [nh][nq][np], linear index tap * nq * np + row * np + col, layout enum TDSE_H_LAYOUT_TAP_MAJOR_ROW_MAJOR.
H[0]is the instantaneous operator returned bytdse_step_op(...)H[1..nh-1]contribute to the delayed-history term returned bytdse_step_hr(...)
Explicit-tau contract:
h_desc.tau == NULLmeans delayed taps live on the implicit uniform axistau[k] = k * dth_desc.tau != NULLmeans Runtime evaluates history on the supplied explicit time axis- when
tauis explicit,H[1..nh-1]must already be weighted for that axis - if your source taps came from a uniform grid, convert them first with
tdse_builder_h_uniform_to_tau_1d(...)ortdse_builder_h_uniform_to_piecewise_tau_1d(...)
IR Sequence Contract
IR is step-major: shape [nsteps][nq], linear index step * nq + row, layout enum TDSE_IR_LAYOUT_STEP_MAJOR. Runtime queries IR by current step time. If the query falls outside the configured support window, tdse_step_ir(...) returns TDSE_STATUS_OUT_OF_RANGE.
Dense Operator Contract
tdse_step_op(...) writes into a tdse_dense_block_t. Supported views: square (np x np) or full (nq x np). cols must always equal np; rows must equal either np or nq.
Shape Worked Example
Assume np = 3, nq = 4, nh = 8, ir_nsteps = 100. Then:
| Query | Required Length / Shape |
|---|---|
primary vector passed to commit | length np = 3 |
hr_out buffer | length nq = 4 |
ir_out buffer | length nq = 4 |
square op view | 3 x 3 |
full op view | 4 x 3 |
committed dr_out buffer | length nq = 4 |
The common bug: sizing hr, ir, or dr buffers to np because the host thinks in terms of ports rather than equations. These buffers are nq-sized outputs.
Step-Term Contract
For the mathematical definitions, see Theory and Concepts.
| Term | Meaning | Side Effect |
|---|---|---|
op | instantaneous operator | none |
hr | delayed-history contribution | none |
ir | independent-response contribution | none |
dr | committed-step direct response | query-only, post-commit |
Read these equations literally: y_trial = op * primary_trial + hr + ir; dr[n] = op * primary_accepted[n]. dr is the direct-response slice on the committed step, not "the whole committed output."
Runtime Handoff Contract
Builder is done once the pack is internally consistent, validated, and clearly labeled. The runtime handoff should answer three questions before any simulation work starts:
- does the pack validate cleanly
- do inspected dimensions and metadata match what the host expects
- does Runtime create the model without reporting pack or compatibility errors
After that point, move to Runtime Lifecycle and Step Execution instead of continuing to reason about Runtime behavior from the Builder chapter.
Host-Side Assertions
In production, assert these once near the integration boundary:
model_info.npmatches host primary-vector widthmodel_info.nqmatches host equation/output widthmodel_info.nhmatches intended history depthmodel_info.dtmatches host time-step contract
Common Shape Mistakes
- host primary vector width does not match
np hr/irbuffers sized tonpinstead ofnq- square operator storage used when host required
nq x np - Builder
dtand simulationdtassumed to match without verification - inferring
np/nqfrom old code paths instead oftdse_model_info(...) - treating
IRas a runtime side channel instead of packaged model content
Power Systems Guide
Typical Parameters by Scenario
| Scenario | dt (s) | nh | nfft | np | nq | Notes |
|---|---|---|---|---|---|---|
| Transmission line (100 km, 500 kV) | 1e-6 to 10e-6 | 1000-5000 | 2 x nh | 2 (1 port) or 4 (2-port) | np or np+1 | Large nh for propagation delay |
| Transformer (50 MVA) | 1e-6 to 50e-6 | 200-1000 | 2 x nh | 2-6 | np | Moderate nh for winding capacitance |
| Distribution cable (underground) | 1e-6 to 10e-6 | 500-2000 | 2 x nh | 2-4 | np | nh depends on cable length |
| EMI/EMC (wideband) | 10e-9 to 100e-9 | 1000-4000 | 4 x nh | 1-10 | np | Very fine dt for high-frequency content |
| Power electronics (switching ~100 kHz) | 10e-9 to 100e-9 | 500-2000 | 2 x nh | 1-4 | np | Fine dt for switching transients |
Choosing nh
Choose history depth nh by convergence against a trusted reference, not by a
universal last-tap threshold:
- Build the pack with a conservative initial
nhestimate. - Inspect the tail and compare the resulting host output with a longer-history pack or other trusted reference.
- Increase
nhuntil the application metrics meet their stated tolerance. - For transmission lines,
nh >= round(propagation_delay / dt) + marginis a starting estimate; reflections and the selected accuracy metric determine whether it is sufficient.
For a 500 kV, 100 km line with propagation speed ~2.8x10^8m/s and dt = 50 us: propagation delay >=357 us ->nh >=9. For dt = 1 us: nh >=367.
When to Use nq > np
Set nq > np when the model needs measurement equations beyond the port count: multi-port models with internal measurements, mixed Y/Z representations, or host simulators needing both terminal currents and internal state outputs. For standard Y+ISC or Z+VOC, nq = np is typical.
Builder Flow
Builder is the handoff layer between validated source artifacts and Runtime. A clear Builder boundary makes later debugging much easier: you can tell whether a problem comes from the source data, Builder configuration, pack generation, or Runtime execution.
Builder Responsibility
Builder owns the configured dimensions and dt, attachment of required H and optional IR, optional conversion from frequency-domain data into time-domain H, optional Builder-side shaping such as IRC, and the final pack metadata and pack write.
Builder does not own upstream artifact validity beyond descriptor and shape checks, host-specific port order or matrix-family interpretation, Runtime step execution, or Runtime shutdown and concurrency policy.
The main rule is simple: Builder should emit the final artifact, and Runtime should execute it as-is. Runtime is not expected to reconstruct or repair pack content later.
Builder State Machine
Important rules: tdse_builder_configure_ex(...) is the recommended configure entrypoint, re-configuring replaces dimensions and clears previously attached H and IR, tdse_builder_info(...) is the best snapshot of the current Builder state, and a successful pack write does not transfer handle ownership.
Builder Contract Table
| Contract Item | Set By | Why It Matters Downstream |
|---|---|---|
dt | tdse_builder_configure_ex(...) | Runtime timing and IR step addressing |
np | tdse_builder_configure_ex(...) | host primary vector width must match |
nq | tdse_builder_configure_ex(...) | hr, ir, dr, and full op row count |
nh | tdse_builder_configure_ex(...) | delayed-history horizon and H tensor depth |
H layout | tdse_h_desc_t | wrong layout produces plausible but incorrect packs |
explicit tau axis | tdse_h_desc_t | malformed nonuniform timing rejected before pack write |
IR layout and horizon | tdse_ir_desc_t | runtime tdse_step_ir(...) legality |
| pack form/domain metadata | tdse_builder_set_pack_meta(...) | support and consuming hosts interpret the pack |
Direct H Ingestion
Use when the upstream artifact is already a validated time-domain kernel. Sequence: configure -> populate tdse_h_desc_t -> tdse_builder_apply_h(...) -> snapshot with tdse_builder_info(...) -> write pack. Post-apply: verify info.configured, info.has_h, and dimension/tau expectations match.
Spectrum-to-H Conversion
Use tdse_builder_h_from_spectrum(...) when the upstream artifact is frequency-domain data.
The sequence: define positive-frequency grid -> map source matrix into tdse_builder_cplx_mat_view_t -> choose correction method -> run conversion -> attach -> snapshot before write.
Initialize tdse_builder_h_from_spectrum_request_t with
tdse_builder_h_from_spectrum_request_init(), then set its fixed-width
correction_method, parallel_mode, and tail_preset fields. Invalid raw
selector values return TDSE_STATUS_INVALID_ARG without loading an invalid C
enum representation.
The pre-1.0 spectrum conversion alias with its unused alpha argument was
removed. tdse_builder_h_from_spectrum(...) is the sole supported entry
point, so callers cannot accidentally rely on a parameter that has no product
semantics.
Correction methods:
TDSE_BUILDER_CORRECTION_NONE_RECONSTRUCT_FROM_REAL_RECONSTRUCT_FROM_IMAG_RECONSTRUCT_FROM_MAG_RECONSTRUCT_FROM_PHASE
These options change how Builder computes H; Runtime only sees the
finished pack. When this path looks wrong, ask two separate questions:
did Builder produce the intended H, and did Runtime execute that pack
correctly?
Grid Planning: dt, nh, and nfft
The planner derives a power-of-two nfft no smaller than 2 * nh (and no
smaller than the requested positive-frequency grid requires). This makes the
discrete spectrum grid compatible with the conversion contract; it does not by
itself establish adequate source bandwidth, physical decay capture, or output
accuracy. Choose dt and nh * dt from the source and validate the resulting
pack against a trusted reference. The planner takes separate input and output
structures:
tdse_builder_grid_planning_input_t grid_in =
tdse_builder_grid_planning_input_init();
tdse_builder_grid_planning_output_t grid_out =
tdse_builder_grid_planning_output_init();
grid_in.f_max_hz = 5.0e6;
grid_in.target_dt = 100.0e-9; /* optional; 0 selects automatic dt */
grid_in.target_time_window_s = 1.0e-3;
tdse_status_t rc =
tdse_builder_compute_consistent_grid(&grid_in, &grid_out);
An explicit non-zero target_dt must satisfy Nyquist and the exact positive
integer-nanosecond Runtime Pack contract. In automatic mode (target_dt == 0),
the planner derives a Nyquist-safe value and floors it to whole nanoseconds;
flooring makes the step no larger than the raw result. If the derived value is
below 1 ns, planning fails instead of emitting a step that the Pack writer
cannot represent.
| Scenario | dt | nh | nfft | Rationale |
|---|---|---|---|---|
| Power electronics (~100 kHz) | 10-100 ns | 500-2000 | 2 x nh | Fine dt for switching edges |
| Transmission line (long delay) | 1-10 us | 2000-10000 | 2 x nh | Large nh for delay + reflections |
| Structural dynamics (< 1 kHz) | 1-10 us | 200-1000 | 2 x nh | Coarse dt sufficient |
| EMI/EMC (wideband) | 10-100 ns | 1000-4000 | 4 x nh | Large nfft for spectral resolution |
Common mistakes: nh too small for the application's convergence target;
dt too large for the source bandwidth; and a spectrum grid incompatible with
the requested nh (which Builder rejects rather than silently converting).
FRF Data Layout
tdse_builder_cplx_mat_view_t is an interleaved complex view. For frequency
f, row r, and column c, Builder reads real and imaginary values from
ri[2 * (f * stride_freq + r * ld + c)] and the following element. A tightly
packed row-major matrix uses ld = port_count and
stride_freq = output_count * port_count, but callers may provide a larger
leading dimension or frequency stride that meets the view contract.
The frequency grid must begin at DC and match Builder's expected uniform grid; the converter rejects incompatible grids. Builder converts the supplied matrix values as given. Confirm any required Y/Z representation, reference impedance, and multi-port ordering before calling it; it does not infer or convert those upstream semantics.
Builder IRC (Impulse Response Compression)
Fixed-budget adaptive IRC
Use tdse_builder_irc_select_samples when the desired artifact is a smaller
set of original H matrices for linear reconstruction. Use
tdse_builder_irc_compress_adaptive when producing a Runtime Pack. Both take
one budget, target_taps, for example 1024 or 2048. No preset, excitation,
input history, or tolerance tuning is required.
The owned tdse_builder_irc_adaptive_result_t deliberately contains separate
arrays:
| Field | Meaning |
|---|---|
indices, tau | Selected original indices and times; shared by all matrix elements |
samples | Exact original H[indices] in [nh][nq][np] order |
runtime_weights | Separate convolution coefficients; NULL for sample-only selection |
diagnostics | Reconstruction error of samples against the complete input H |
The sample selector retains the first and last nodes. Runtime compression also retains index 1, so the instantaneous H[0] and the earliest delayed time remain separate. These necessary nodes count toward the budget. Therefore sample selection requires K>=2 for Nh>=2; Runtime compression requires K>=3 for Nh>=3. Both accept K>=1 when Nh=1, and Runtime accepts K>=2 when Nh=2. If K>=Nh, both return all Nh samples, and Runtime coefficients are an exact copy of input H. No duplicate nodes are inserted. The last original time is always covered.
For retained indices a<b and each original interior sample k, define
alpha=(tau[k]-tau[a])/(tau[b]-tau[a]) and
Hhat[k]=(1-alpha)*H[a]+alpha*H[b]. The objective and diagnostics are:
J(a,b) = sum(a<k<b) ||H[k]-Hhat[k]||_F^2
J = sum_intervals J(a,b)
E = sqrt(J / sum_k ||H[k]||_F^2)
The sums use all matrix elements and original sampling positions. They are
not time-integral weights, per-element relative errors, or scalar H norms.
Zero H reports zero error without an epsilon floor. compression_ratio is
input_taps/output_taps; max_interval_sse is the largest J(a,b).
Refinement first selects the interval with the largest current J(a,b). Within it, the earliest full-matrix residual peak and the index midpoint are the only split candidates. Their actual child SSE determines the better candidate. Candidate ties prefer the index midpoint, then the earlier index. Interval ties prefer larger candidate gain, longer index span, then earlier left end. Zero and negative gains continue until exactly K nodes are retained. This avoids an early stopping rule on constant data and permits renewed refinement around late echoes. The method is deterministic on one numerical platform; it does not claim global optimality, monotone improvement with K, or universal superiority over uniform grids.
Runtime conversion. Input H is a discrete kernel, including any explicit tau weighting already present in the source descriptor. For selected delayed nodes t_j, let phi_j be their linear hat basis. The adapter forms:
W[0] = H[0]
W[j] = sum(k>=1) H[k] * phi_j(tau[k]), j>=1
Each original delayed tap contributes to its two neighboring selected delayed nodes. It never contributes to W[0]. In exact arithmetic, partition of unity and linear reproduction imply, for every matrix element:
sum(j>=1) W[j] = sum(k>=1) H[k]
sum(j>=1) t_j*W[j] = sum(k>=1) tau[k]*H[k]
Consequently the delayed convolution is exact when the particular queried
history function x(t-tau) is linear on each selected delayed interval.
Floating-point projection and Runtime precision introduce their own rounding.
A step edge, startup zero extension, pulse, or arbitrary history need not meet
this condition. This adapter does not turn J into a simulation-error bound or
establish stability, passivity, or arbitrary-input accuracy.
For example, original H=[1,1,1,1,1] at times [0,1,2,3,4] is perfectly
reconstructed by its endpoints, so sample J=0. Directly treating those two
samples as Runtime coefficients changes a fully populated constant-history
output from 5 to 2. Runtime compression with K=3 retains [0,1,4] and projects
the coefficients to [1,2,2], recovering the same constant-history sum while
keeping H[0] separate. This algebraic example does not promise equality for
an arbitrary waveform.
C integration. Install the application's authorization provider through the usual Runtime setup before calling either authoring function. The source descriptor and its buffers remain caller-owned. An initialized result can be reused: failure leaves every old field and allocation unchanged; success replaces it. Release results through TDSE, including after authorization is revoked; never free their arrays with the application's allocator.
tdse_builder_irc_adaptive_result_t compressed;
tdse_builder_irc_adaptive_result_init(&compressed);
tdse_status_t status = tdse_builder_irc_compress_adaptive(
&source_h, 1024, &compressed);
if (status == TDSE_STATUS_OK) {
tdse_h_desc_t runtime_h = {0};
runtime_h.struct_size = sizeof(runtime_h);
runtime_h.port_count = compressed.port_count;
runtime_h.output_count = compressed.output_count;
runtime_h.nh = compressed.nh;
runtime_h.dt = compressed.dt;
runtime_h.data = compressed.runtime_weights;
runtime_h.tau = compressed.tau;
runtime_h.layout = TDSE_KERNEL_LAYOUT_TAP_MAJOR_ROW_MAJOR;
/* Configure Builder for compressed.nh and matching dt/np/nq before apply. */
status = tdse_builder_apply_h(builder, &runtime_h);
}
tdse_builder_irc_adaptive_result_release(&compressed);
The C++17 owner tdse::builder::AdaptiveIrc provides the same boundary:
auto compressed = tdse::builder::AdaptiveIrc::compress(source_h, 1024);
const auto& samples = compressed.get(); // original H samples and diagnostics
auto runtime_h = compressed.runtimeH(); // borrowed descriptor of weights
// Builder copies runtime_h during apply; compressed must live through that call.
AdaptiveIrc::selectSamples creates a sample-only result. Calling runtimeH()
on it throws std::logic_error. The owner is movable, never copyable.
Numeric and resource boundaries. C input and both output arrays are FP64. An upstream FP32 H must first be promoted to FP64, which preserves each finite FP32 value exactly; output error then refers to that discrete FP32-origin H. Runtime FP32 model storage and execution are separate downstream choices and must be validated separately. The selector permits arbitrary positive Nq/Np; Runtime compression additionally requires Nq>=Np. output_count=0 means Nq=Np. The existing H descriptor requires finite dt>0, and explicit tau starting at zero (the existing 1e-15 origin tolerance applies) and strictly increasing. The implicit k*dt axis is also validated before any scan.
The implementation uses stable FP64 interpolation, scaled compensated norms,
and norm accumulation across intervals. It preserves small representable
residuals beside large constant channels without first scaling H into a common
residual tensor. Dimension and allocation products are checked before input
scans. Allocation failures return OUT_OF_MEMORY and preserve the old result;
test builds exercise the existing builder.irc.adaptive allocation injection
point. There is no allocation or adaptive selection in the Runtime step path.
Runtime weights use Neumaier compensated summation to retain representable small contributions when larger terms cancel. The compensation follows only the two active delayed nodes and is folded into each completed node once. This improves strong-cancellation behavior without promising correctly rounded weights for every input. Retaining all original taps still validates every input value and returns bit-preserving copies with zero reconstruction error; it skips unnecessary energy and interval-error accumulation.
Non-finite inputs return INVALID_ARG, checked size overflow returns OUT_OF_RANGE, and unrepresentable derived residuals, evaluated candidate SSE, final SSE diagnostics, normalized error, or interior time fractions return NUMERIC. A candidate may exceed the FP64 range even if a different or fuller grid would avoid that error; the call then fails explicitly. Subnormal local SSE can round during candidate ranking; interval norms remain available for the final total. Cross-platform last-bit differences can change tied choices.
For M=NqNp, scanned interval lengths n_i, and at most two candidates, work is O(Msum_i n_i), plus O(K log K) heap work and sorting. Balanced refinement is typically O(MNhlog K); the worst unbalanced case is O(MNhK). Final reconstruction diagnostics and Runtime projection each cost O(NhM). Extra storage is O(KM+K), including owned outputs; projection compensation scratch is only 2*M FP64 values when K<Nh, and is absent for identity output. No full copy of the original H or residual tensor is required for compression. The last delay and history support remain unchanged, so smaller kernel storage does not imply the same reduction in all Runtime history memory or a measured speedup.
Legacy dyadic IRC preview
The existing preview API replaces the delayed tail with a dyadic explicit-time representation. Aggregate mode combines source taps into dyadic bins; moment-preserving mode represents each non-trivial bin with endpoint taps that preserve both its sum and first time moment. Both retain the configured exact prefix. These modes do not fit an exponential envelope, and their tolerance fields remain reserved. Use them only when maintaining or comparing an existing preset workflow; fixed-budget integrations use the APIs above.
tdse_builder_irc_profile_t profile;
tdse_builder_irc_profile_init(&profile);
tdse_builder_irc_profile_select(
(int32_t)TDSE_BUILDER_IRC_PRESET_BALANCED, &profile);
tdse_builder_irc_buffer_t compressed;
tdse_builder_irc_buffer_init(&compressed);
tdse_builder_irc_compress(&h_desc, &profile, &compressed);
tdse_builder_apply_h(b, &(tdse_h_desc_t){
.struct_size = sizeof(tdse_h_desc_t),
.port_count = compressed.port_count,
.output_count = compressed.output_count,
.nh = compressed.nh,
.dt = compressed.dt,
.data = compressed.data,
.tau = compressed.tau,
.layout = compressed.layout,
});
tdse_builder_irc_buffer_release(&compressed);
The fixed-width tdse_builder_irc_profile_t API is currently experimental.
Use it only when the release compatibility policy permits experimental Builder
APIs, and expect its ABI or behavior to evolve. Choose a semantic preset (BALANCED, COMPACT, AGGRESSIVE,
CONSERVATIVE, HIGH_FIDELITY, ULTRA_FIDELITY, or MAX_FIDELITY); its
shape is bound by manifests/policy/builder_irc_presets.json.
Keep the IRC parameters next to the pack so later comparisons are reproducible. Compare compressed output with the full kernel over representative workloads before production use.
Optional IR
IR is optional, but once attached it becomes part of the pack Runtime will load. Keep Builder dt and IR dt aligned, keep IR dimensions aligned with np and nq, make sure the IR support length covers the simulation horizon, and set pack form metadata intentionally (FLOW_FROM_EFFORT = Y+ISC, EFFORT_FROM_FLOW = Z+VOC, UNKNOWN only for packs without IR).
Builder Inspection
tdse_builder_info(...) is the fastest way to confirm what Builder currently has attached. Use it after configure and after every attach or clear. Fields worth logging are configured, dt/nh/np/nq, has_h/has_h_tau/h_layout, has_ir/ir_nsteps/ir_dt/ir_layout, and pack_form/pack_domain. The domain is product-neutral provenance metadata; it never changes the Runtime entitlement required to execute the resulting Runtime Pack. tdse_builder_last_diagnostics(...) is the sticky failure-time snapshot for mutating Builder APIs; capture it whenever configure, attach, clear, metadata, or pack-write calls return non-OK. Once the pack is written, tdse_model_info(...) becomes the matching Runtime-side confirmation point.
tdse_builder_diagnostics_t diag = tdse_builder_diagnostics_init();
tdse_status_t rc = tdse_builder_last_diagnostics(builder, &diag);
if (rc == TDSE_STATUS_OK && diag.last_code != TDSE_STATUS_OK) {
tdse_runtime_error_category_t category =
tdse_status_error_category(diag.last_code);
printf("builder failure api=%s code=%d category=%s reason=%s details=%s\n",
diag.api_name,
diag.last_code,
tdse_status_error_category_name(category),
tdse_status_stable_reason(diag.last_code),
diag.details);
}
Write Gate Checklist
Before tdse_builder_write_pack(...), confirm that Builder is configured, H is attached, dimensions match, optional IR is either absent or correctly attached, pack metadata is explicit, and the output path is stable. After the write, validate the pack, inspect it, then hand off to Runtime for create and loop verification.
Pack writes use a same-directory temporary file, flush it, and atomically replace a regular target. Symbolic-link and multiply-linked targets are rejected so the writer does not follow an ambiguous artifact path.
Pack write completion has three distinct outcomes:
TDSE_STATUS_OK: the target was replaced and durability confirmation completed- an ordinary pre-commit error such as
TDSE_STATUS_IO: the target was not replaced TDSE_STATUS_COMMITTED_DURABILITY_UNCERTAIN: the target was already replaced, but the platform could not confirm parent-directory/crash durability
For the last outcome, do not assume that the old artifact is still present.
Inspect and validate the current target, preserve the stable reason
committed_durability_uncertain in incident evidence, and regenerate on known
reliable storage if crash durability is required.
Worked Paths
Path A: Direct-H Pack
Highest-confidence path: configure once ->attach H ->inspect ->write ->validate ->create Runtime model. Fewest transformations; easiest to isolate failures.
Path B: Frequency-Domain Source To Pack
Archive the frequency grid and matrix family -> run tdse_builder_h_from_spectrum(...) -> attach -> inspect -> write -> validate. Needs more release discipline because a successful write does not prove the conversion policy was correct.
Path C: H + IR With Explicit Pack Meaning
Configure -> attach H -> attach IR -> tdse_builder_set_pack_meta(...) intentionally -> inspect -> write -> validate -> create. Most likely to confuse downstream users if metadata is omitted.
Triage
Failure Modes
| Failure | Symptom | Root Cause | Fix |
|---|---|---|---|
TDSE_STATUS_OUT_OF_RANGE | tdse_step_ir fails mid-run | Simulation time exceeds IR support window | Extend IR sequence or clamp simulation horizon |
TDSE_STATUS_CONCURRENT_API_USE | sporadic non-OK in multi-thread run | same handle stepped by multiple threads | Serialize per handle; one handle per thread |
TDSE_STATUS_INVALID_ARG | immediate failure on create/step | null/shape mismatch in inputs | Validate pointers, dimensions, struct sizes |
TDSE_STATUS_NUMERIC | conversion/compression fails without changing outputs | finite inputs overflowed while deriving a result | Rescale source data or choose a representable grid; do not consume canary outputs |
TDSE_STATUS_COMMITTED_DURABILITY_UNCERTAIN | pack write is non-OK but target contains the new artifact | replacement committed, then directory durability confirmation failed | Validate the current target; archive the stable reason; regenerate on reliable storage if required |
| pack validation failure | create rejects model bytes | corrupted or incompatible pack | regenerate pack, inspect tdse_model_create_diagnostics_t |
Builder Failure Classes Worth Catching Early
| Failure Class | Typical Root Cause | Best Stage | Support Clue |
|---|---|---|---|
| dimension mismatch | np/nq/nh disagreement | configure/apply | Builder snapshot vs. source planning sheet |
| malformed descriptor | null pointer, bad struct size, invalid layout | apply | attach call fails immediately |
| tau-axis issue | invalid explicit nonuniform timing | apply H | has_h_tau vs. source timing mismatch |
| wrong matrix family | host supplied wrong physical meaning | preflight | pack validates but numerical behavior is wrong |
| IR horizon issue | sequence shorter than intended run | preflight/attach | Runtime fails at tdse_step_ir(...) |
| metadata ambiguity | form/domain not set intentionally | pre-write review | cannot tell Y+ISC from Z+VOC |
| write-path issue | unstable output path or file handling | pack write | configure/attach succeeded but no artifact exists |
Builder-To-Runtime Failure Isolation
When a runtime test fails after a fresh pack write, split the investigation: did Builder produce the intended artifact, or did Runtime execute it incorrectly? This separation keeps teams from debugging the wrong layer.
Troubleshooting Decision Flow
Rapid checks: prefer tdse_model_create(...) over process-global create diagnostics; log tdse_model_info once at model create; log t, dt, and step index on each failed call; keep one deterministic repro input for local and automated runs.
Anti-Patterns
- re-configuring a handle and assuming previous
H/IRis still attached - treating
tdse_builder_write_pack(...)as proof of semantic correctness - attaching
IRwithout deciding pack representation metadata - debugging Runtime first when Builder snapshots were never captured
- letting Runtime compensate for Builder-side source interpretation mistakes
- using ad hoc output paths that break pack provenance
Pack Incident Triage
When a pack validation or create issue is reported, collect:
- Builder configure inputs (
dt,nh,np,nq) - Builder snapshot from
tdse_builder_info(...) - whether the pack came from direct
Hor spectrum conversion - any conversion or tail-processing settings
- whether
IRwas attached and what pack-form metadata was used - result of the
tdse_pack_validatecommand - result and diagnostics from
tdse_model_create(...)
If the first five items are missing, do not start by blaming Runtime.
