docs(sparse-sfm): freeze Gate F parameter fingerprint

This commit is contained in:
fy59 2026-08-25 08:18:30 +02:00
parent fa58c8a225
commit a67d903a8c

View file

@ -597,7 +597,182 @@ The candidate reconstruction identity is:
Runtime task IDs, worker count, Governor state, pause timing and resource
observations are excluded. Any output-changing threshold, camera model,
initialization policy, triangulation policy, PnP policy, BA policy, precision or
loss parameter belongs in the future fingerprint.
loss parameter belongs in the parameter fingerprint materialized by the Gate F
persistent-identity seam. Gate D and Gate E neither receive, compute, serialize
nor transport it. Project DB stores it but does not own its meaning; Task Runtime
and the Resource System are also excluded. This seam is a Gate F orchestration
responsibility, not a new subsystem or scientific solver gate.
### Sparse SfM parameter fingerprint v1
The parameter fingerprint is `SHA-256(record_v1)`, with one hash operation over
the exact fixed record below. Its output is the complete 32-byte digest. The
record begins with the eight ASCII bytes `L3DSFMFP` (`4c 33 44 53 46 4d 46 50`),
without a NUL byte, followed by `fingerprint_encoding_version=1`. Every
multi-byte scalar is little-endian. `f64` means the exact finite IEEE-754
binary64 bit pattern, with both signed zeros encoded as positive zero; NaN and
infinity are invalid. Categorical values are the explicit `u32` policy IDs
defined here, never native enum ordinals. No native struct, padding, pointer,
`size_t`, host-endian value, JSON or locale-dependent text is hashed. A boolean,
if a future encoding uses one, is `u8`, with false `0` and true `1`; v1 has no
boolean field and no padding or reserved bytes.
| Offset | Width | Field | Canonical type | Source/value |
|---:|---:|---|---|---|
| 0 | 8 | `domain` | ASCII bytes | `L3DSFMFP` |
| 8 | 4 | `fingerprint_encoding_version` | `u32` | `1` |
| 12 | 4 | `minimum_seed_tracks` | `u32` | effective Gate D parameter |
| 16 | 4 | `minimum_seed_landmarks` | `u32` | effective Gate D parameter |
| 20 | 4 | `minimum_pnp_correspondences` | `u32` | effective Gate D parameter |
| 24 | 4 | `maximum_seed_candidates` | `u32` | effective Gate D parameter |
| 28 | 4 | `maximum_registration_rounds` | `u32` | effective Gate D parameter |
| 32 | 4 | `maximum_landmarks_per_round` | `u32` | effective Gate D parameter |
| 36 | 4 | `maximum_images` | `u32` | effective Gate D parameter |
| 40 | 8 | `maximum_observations` | `u64` | effective Gate D parameter |
| 48 | 8 | `maximum_tracks` | `u64` | effective Gate D parameter |
| 56 | 8 | `reprojection_threshold_px` | `f64` | effective Gate D parameter |
| 64 | 8 | `minimum_track_parallax_rad` | `f64` | effective Gate D parameter |
| 72 | 8 | `relative_pose.robust_threshold_px` | `f64` | effective Gate D parameter |
| 80 | 8 | `relative_pose.confidence` | `f64` | effective Gate D parameter |
| 88 | 4 | `relative_pose.max_iterations` | `u32` | effective Gate D parameter |
| 92 | 4 | `relative_pose.minimum_inliers` | `u32` | effective Gate D parameter |
| 96 | 8 | `relative_pose.minimum_inlier_ratio` | `f64` | effective Gate D parameter |
| 104 | 8 | `relative_pose.minimum_parallax_rad` | `f64` | effective Gate D parameter |
| 112 | 8 | `relative_pose.minimum_cheirality_ratio` | `f64` | effective Gate D parameter |
| 120 | 8 | `relative_pose.deterministic_seed` | `u64` | effective Gate D parameter |
| 128 | 8 | `pnp.reprojection_threshold_px` | `f64` | effective Gate D parameter |
| 136 | 8 | `pnp.confidence` | `f64` | effective Gate D parameter |
| 144 | 4 | `pnp.max_iterations` | `u32` | effective Gate D parameter |
| 148 | 4 | `pnp.minimum_inliers` | `u32` | effective Gate D parameter |
| 152 | 8 | `pnp.minimum_inlier_ratio` | `f64` | effective Gate D parameter |
| 160 | 8 | `pnp.deterministic_seed` | `u64` | effective Gate D parameter |
| 168 | 4 | `refinement.max_iterations` | `u32` | effective Gate D parameter |
| 172 | 8 | `refinement.convergence_tolerance` | `f64` | effective Gate D parameter |
| 180 | 4 | `camera_model_policy` | `u32` | `PINHOLE_K1_K2_P1_P2_V1=1` |
| 184 | 4 | `calibration_policy` | `u32` | `KNOWN_FIXED_CALIBRATION_V1=1` |
| 188 | 4 | `source_pixel_policy` | `u32` | `SOURCE_PIXEL_TOP_LEFT_X_RIGHT_Y_DOWN_V1=1` |
| 192 | 4 | `pose_policy` | `u32` | `WORLD_TO_CAMERA_R_CW_CW_V1=1` |
| 196 | 4 | `seed_ranking_policy` | `u32` | `SHARED_PARALLAX_IMAGE_ID_V1=1` |
| 200 | 4 | `robust_seed_policy` | `u32` | `LOCAL_DETERMINISTIC_SEED_V1=1` |
| 204 | 4 | `next_image_policy` | `u32` | `VISIBLE_SUPPORT_THEN_IMAGE_ID_V1=1` |
| 208 | 4 | `track_order_policy` | `u32` | `CANONICAL_TRACK_OBSERVATION_V1=1` |
| 212 | 4 | `component_policy` | `u32` | `DISCONNECTED_INDEPENDENT_COMPONENTS_V1=1` |
| 216 | 4 | `triangulation_policy` | `u32` | `NORMALIZED_DLT_POINT_REFINE_V1=1` |
| 220 | 4 | `landmark_rejection_policy` | `u32` | `WHOLE_LANDMARK_ACCEPT_OR_REJECT_V1=1` |
| 224 | 4 | `cheirality_policy` | `u32` | `POSITIVE_DEPTH_THRESHOLD_V1=1` |
| 228 | 4 | `gate_d_numeric_policy` | `u32` | `BINARY64_DETERMINISTIC_V1=1` |
| 232 | 4 | `ba_mode` | `u32` | `FINAL_PER_COMPONENT_POSTPROCESS_V1=1` |
| 236 | 4 | `local_ba_policy` | `u32` | `LOCAL_BA_DISABLED_V1=1` |
| 240 | 4 | `ba_optimized_variables` | `u32` | `ROTATION_CENTER_LANDMARK_XYZ_V1=1` |
| 244 | 4 | `ba_fixed_inputs` | `u32` | `INTRINSICS_DISTORTION_OBSERVATIONS_IDENTITIES_V1=1` |
| 248 | 4 | `ba_pose_representation` | `u32` | `UNIT_QUATERNION_R_CW_PLUS_CW_V1=1` |
| 252 | 4 | `ba_gauge_policy` | `u32` | `MIN_IMAGE_FARTHEST_CENTER_ONE_AXIS_V1=1` |
| 256 | 8 | `ba_degenerate_scale_threshold` | `f64` | `1e-9` |
| 264 | 4 | `ba_residual_policy` | `u32` | `ONE_FULL_2D_BLOCK_PER_OBSERVATION_V1=1` |
| 268 | 4 | `ba_projection_policy` | `u32` | `SOURCE_PIXEL_PINHOLE_K1_K2_P1_P2_V1=1` |
| 272 | 8 | `ba_minimum_camera_depth` | `f64` | `1e-9` |
| 280 | 4 | `ba_robust_loss` | `u32` | `HUBER_FULL_2D_NORM_V1=1` |
| 284 | 8 | `ba_huber_delta_px` | `f64` | `2.0` |
| 292 | 4 | `ba_robust_cost_policy` | `u32` | `HALF_SUM_RHO_SQUARED_NORM_V1=1` |
| 296 | 4 | `ba_solver_contract` | `u32` | `CERES_2_2_CONTRACT_V1=1` |
| 300 | 4 | `ba_minimizer` | `u32` | `TRUST_REGION_V1=1` |
| 304 | 4 | `ba_trust_region_strategy` | `u32` | `LEVENBERG_MARQUARDT_V1=1` |
| 308 | 4 | `ba_linear_solver` | `u32` | `ITERATIVE_SCHUR_V1=1` |
| 312 | 4 | `ba_preconditioner` | `u32` | `SCHUR_JACOBI_V1=1` |
| 316 | 4 | `ba_num_threads` | `u32` | `1` |
| 320 | 4 | `ba_max_num_iterations` | `u32` | `50` |
| 324 | 8 | `ba_function_tolerance` | `f64` | `1e-6` |
| 332 | 8 | `ba_gradient_tolerance` | `f64` | `1e-10` |
| 340 | 8 | `ba_parameter_tolerance` | `f64` | `1e-8` |
| 348 | 4 | `ba_retry_count` | `u32` | `0` |
| 352 | 4 | `ba_convergence_acceptance` | `u32` | `CONVERGENCE_ONLY_V1=1` |
| 356 | 8 | `ba_cost_non_regression_factor` | `f64` | `1e-12` |
| 364 | 4 | `ba_underconstraint_policy` | `u32` | `MANIFEST_UC1_UC2_UC3_UC4_V1=1` |
| 368 | 4 | `ba_numeric_policy` | `u32` | `BINARY64_SINGLE_THREAD_V1=1` |
The exact v1 record length is 372 bytes. Gate D field semantics and source
translations are exhaustive:
| Field | Source type | Encoding/normalization | Scientific meaning |
|---|---|---|---|
| `minimum_seed_tracks` | `uint32_t` | `u32` | minimum shared Tracks for a seed |
| `minimum_seed_landmarks` | `uint32_t` | `u32` | minimum accepted seed landmarks |
| `minimum_pnp_correspondences` | `uint32_t` | `u32` | minimum correspondences for registration |
| `maximum_seed_candidates` | `uint32_t` | `u32` | bound on seed attempts |
| `maximum_registration_rounds` | `uint32_t` | `u32` | bound on growth rounds |
| `maximum_landmarks_per_round` | `uint32_t` | `u32` | bound on new landmarks per round |
| `maximum_images` | `uint32_t` | `u32` | accepted input image bound |
| `maximum_observations` | `uint64_t` | `u64` | accepted observation bound |
| `maximum_tracks` | `uint64_t` | `u64` | accepted Track bound |
| `reprojection_threshold_px` | `double` | finite canonical `f64` | landmark reprojection acceptance |
| `minimum_track_parallax_rad` | `double` | finite canonical `f64` | landmark parallax acceptance |
| `relative_pose.robust_threshold_px` | `double` | finite canonical `f64` | essential robust residual threshold |
| `relative_pose.confidence` | `double` | finite canonical `f64` | essential robust confidence |
| `relative_pose.max_iterations` | `uint32_t` | `u32` | essential robust iteration bound |
| `relative_pose.minimum_inliers` | `uint32_t` | `u32` | essential minimum inlier count |
| `relative_pose.minimum_inlier_ratio` | `double` | finite canonical `f64` | essential minimum inlier fraction |
| `relative_pose.minimum_parallax_rad` | `double` | finite canonical `f64` | relative-pose minimum parallax |
| `relative_pose.minimum_cheirality_ratio` | `double` | finite canonical `f64` | relative-pose positive-depth fraction |
| `relative_pose.deterministic_seed` | `uint64_t` | `u64` | local essential robust-estimator seed |
| `pnp.reprojection_threshold_px` | `double` | finite canonical `f64` | PnP robust residual threshold |
| `pnp.confidence` | `double` | finite canonical `f64` | PnP robust confidence |
| `pnp.max_iterations` | `uint32_t` | `u32` | PnP robust iteration bound |
| `pnp.minimum_inliers` | `uint32_t` | `u32` | PnP minimum inlier count |
| `pnp.minimum_inlier_ratio` | `double` | finite canonical `f64` | PnP minimum inlier fraction |
| `pnp.deterministic_seed` | `uint64_t` | `u64` | local PnP robust-estimator seed |
| `refinement.max_iterations` | `uint32_t` | `u32` | point-refinement iteration bound |
| `refinement.convergence_tolerance` | `double` | finite canonical `f64` | point-refinement stopping tolerance |
Every row is fingerprinted. Integer fields require no normalization beyond
their fixed-width little-endian translation; every floating field uses the
canonical-zero rule above.
The policy IDs above freeze the full named Gate D and Gate E v1 semantics,
including canonical order and tie breaks,
whole-landmark rejection, positive-depth cheirality, the pose anchor chosen by
smallest image ID, the farthest-center scale anchor with exact image-ID tie,
X/Y/Z axis tie order and exactly one fixed center coordinate. The Gate E
projection is `R_cw + Cw`; intrinsics and `k1/k2/p1/p2` distortion are fixed.
The robust cost policy is exactly `0.5 * sum rho(dx^2+dy^2)` with one Huber loss
on each full two-dimensional residual. `CERES_2_2_CONTRACT_V1` denotes the
accepted Lardon3D Ceres contract `>=2.2.0,<2.3.0`, not package, build, linker or
transitive SuiteSparse metadata.
For Gate D, `WORLD_TO_CAMERA_R_CW_CW_V1` includes the frozen SO(3) residual
limit `1e-6`;
`POSITIVE_DEPTH_THRESHOLD_V1` means strict camera depth greater than `1e-9`;
and `NORMALIZED_DLT_POINT_REFINE_V1` includes the frozen homogeneous-scale
epsilon `1e-12` and collinearity covariance-determinant limit `1e-10`. These
constants are not runtime members, so their stable policy IDs, rather than
duplicate floating fields or implementation enum ordinals, own their exact
v1 semantics.
All 27 effective scalar members, including nested members, of
`Lardon3DSparseIncrementalParameters` occur exactly once. Serialization uses
the validated effective values actually passed to Gate D, so an omitted default
and the same explicitly supplied value produce identical bytes. Actual Track
Set identity, Track/Feature IDs, calibration-scope identity and individual
calibration IDs or numeric values, `sfm_kind`, `sfm_version`, project/task/
transaction/reconstruction IDs, timestamps, resource state, result values and
metrics are excluded. The separate calibration scope hash binds sorted image
IDs to calibration hashes, and each calibration hash binds dimensions,
`fx/fy/cx/cy/k1/k2/p1/p2`, model/version and provenance; the parameter record
therefore records calibration semantics without duplicating calibration
instances.
Changing only a parameter value retains encoding version 1 and naturally
changes the digest. Adding a fingerprint-owned field or changing byte layout
requires a new encoding version; changing the Sparse SfM algorithmic contract
may separately require a new `sfm_version`. Neither version substitutes for the
other. Equal complete candidate tuples therefore identify the same scientific
candidate regardless of runtime metadata.
Gate F implementation must prefer an internal, solver-independent helper unless
a separate public C17 API decision is made. It must reuse the project's SHA-256
implementation, use bounded constant-size storage without cache, scheduling,
Governor interaction or reservation, and add a golden 372-byte default record,
its expected SHA-256 digest, and mutations proving every fingerprint-owned
category changes the digest. This contract authorizes no public symbol.
Exact byte identity is not promised for a future multi-threaded floating-point
solver until measured. The v1 target is deterministic ordering and numerical
@ -812,7 +987,8 @@ Gate D consumes exactly one immutable Track Set, one immutable calibration
scope, finite calibration values for participating images, bounded keypoint
coordinates addressed by `(feature_set_id, feature_index)`, and explicit
parameters immutable during execution. Gate D neither computes nor carries the
future parameter fingerprint. The Track Set is never mutated.
parameter fingerprint materialized later at the Gate F persistent-identity
seam. The Track Set is never mutated.
### Algorithm
@ -1025,9 +1201,11 @@ The immutable reconstruction identity is:
`track_set_id` and `calibration_scope_id` are project-local immutable database
references, consistent with the existing Track Model identity convention. The
parameter fingerprint is required to be a 32-byte SHA-256 value but its final
byte encoding remains a later geometry-gate decision. Runtime IDs, timestamps,
worker count, resource state and metrics are excluded.
parameter fingerprint is the raw 32-byte SHA-256 digest of the 372-byte Sparse
SfM parameter record v1 defined above. Gate F materializes it at its
persistent-identity seam; Project DB stores it without owning or recomputing
it. Runtime IDs, timestamps, worker count, resource state and metrics are
excluded.
Component identity is the minimum registered `image_id` in that component. It
is deterministic, project-local, unique because an image belongs to at most one