Commit a727289d authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: DP rung D3 - full per-scene parent (K x measure+prepare+step+commit, one entry)

pose_scene_dp: ONE device entry per scene. Self-chaining launchers (D1
pattern): pose_measure_dp (D2, payload unchanged; its tail launcher now
chains onward via a new PoseMeasureChain.scene back-pointer) ->
pose_scene_dp_lma (per-cycle prepare from LIVE corr-row count; full mode
= production per-cycle conditioning re-derivation with clears/assemble/
norm/fx/finish, light mode = the D1 frozen body) -> pose_scene_dp_step
(full-mode pure_weight read LIVE from prep_result - it changes every
cycle; the six validated spike stages) -> pose_scene_dp_commit (anchor
update on accept, 25-float trace + 5-int measure-stats trace rows,
self-chains the next cycle's measure). Fixed-K, no device early exit;
guard/invalid-prepare failures stop the chain fail-safe with
cycles-completed visible to the host.

Wrapper tp_proc_exec_pose_scene_dp: C2 nullable uploads + per-scene
policy/pull/reg/anchor, full provisioning (D2 measure provision + the
prepare/step scratch blocks), B4 bayer fence, ONE launch, ONE
end-of-scene readback (scene status + measure status + traces + final
packed + final anchor + compact prep); peaks/indices stay resident for
the unchanged fetch calls. D2 entry refactored onto shared
pose_measure_provision/pose_mchain_fill (same bytes).

GATE (cases.list scene_dp): test_pose_scene_dp_jna rides the captured
pose_corr case at the production 5120/150 LMA shape (full-grid centers
scattered by task txy - corr indices carry full-grid tile numbers).
Four chains vs the host-driven B3 loop (measure -> prepare_resident ->
resident step, host anchor bookkeeping): full accept K=3, full reject
K=2, full post-reject K=2, light K=2 riding the conditioning frozen by
the preceding full chain. Per-cycle trace rows, measure stats, final
packed/anchor/prep, and last-cycle corr rows keyed by packed index ALL
bit-identical; the measure<->LMA interleave is real (accepts move the
pose the next cycle measures at). run_cases.sh ALL PASS incl. pose_corr
@tol 0 + dp_cycles + measure_dp; DP spike PASS; sanitizer-12.8 memcheck
0 errors. Native-test timing is A==B by construction (no JNA in either
arm); the production win - killing the per-cycle Java/JNA round trips -
lands at rung D4.
Co-authored-by: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent 39534100
......@@ -67,6 +67,7 @@ case avg_td_oob test_avg_td_oob ${REPO}/testdata/synth4/avg_td_oob
case bayer_staged test_bayer_staged_jna ${REPO} --tol 0 # rung B4: synthetic, --data unused
case dp_cycles test_pose_lma_dp_cycles_jna ${REPO} --tol 0 # rung D1: synthetic, --data unused; DP K-cycle chain bit-exact vs host cycles # By Claude on 07/17/2026
case measure_dp test_pose_measure_dp_jna ${MODEL}/testdata/pose_corr --tol 0 # rung D2: device measure chain bit-exact vs B3 host-batched entry, keyed by packed index # By Claude on 07/17/2026
case scene_dp test_pose_scene_dp_jna ${MODEL}/testdata/pose_corr --tol 0 # rung D3: full per-scene DP parent bit-exact vs host-driven B3 loop (full+light chains) # By Claude on 07/17/2026
#case convert_direct test_convert_direct ${REPO}/testdata/convert_direct_legacy # local blessed goldens (~200MB, make_convert_direct_case.py)
# gen <name(s)> <generator command, run from ${REPO}>
......
This diff is collapsed.
This diff is collapsed.
......@@ -1291,6 +1291,9 @@ extern "C" __global__ void pose_dp_fill_bits(
}
}
// fwd (rung D3): the measure tail chains the per-scene LMA continuation.
extern "C" __global__ void pose_scene_dp_lma(PoseSceneDp * s);
// Tail launcher: runs AFTER correlate2D_inter, reads the LIVE corr-row count,
// guards it, then enqueues normalize + peak with the exact host arguments.
extern "C" __global__ void pose_measure_dp_tail(const PoseMeasureChain * c)
......@@ -1317,6 +1320,13 @@ extern "C" __global__ void pose_measure_dp_tail(const PoseMeasureChain * c)
c->peak_debias, c->peaks);
}
c->status[0] = nct; // npk == nct (exec_corr2d_peak return convention)
// Rung D3: per-scene parent continuation. Enqueued AFTER peak, so the
// LMA launcher sees LIVE peaks/counts. Guard failures above returned
// without chaining - the scene chain stops there (fail-safe; the host
// reads cycles-completed < num_cycles). By Claude on 07/17/2026.
if (c->scene != NULL) {
pose_scene_dp_lma<<<1, 1, 0, cudaStreamTailLaunch>>>((PoseSceneDp *) c->scene);
}
}
// Average launcher: reads the LIVE surviving-tile count, averages, copies the
......@@ -1424,6 +1434,193 @@ extern "C" __global__ void pose_measure_dp(const PoseMeasureChain * c)
pose_measure_dp_consolidate<<<1, 1, 0, cudaStreamTailLaunch>>>(c);
}
// ── DP rung D3: full per-scene parent (ladder ratified 2026-07-16) ───────────
// See tp_lma.h (PoseSceneDp). Per cycle the launchers replicate the host
// per-cycle sequence EXACTLY: [the D2 measure chain, unchanged - its tail
// chains pose_scene_dp_lma] -> [exec_pose_lma_prepare_resident: clears ->
// assemble -> (norm) -> fx_jacobian(current) -> finish] -> [the six resident
// step stages; the full-mode pure_weight is read LIVE from prep_result - it
// changes every cycle under per-cycle conditioning re-derivation (C3/C3b
// falsified freezing)] -> [commit: anchor update on accept + QC trace + the
// NEXT cycle's measure chain]. Fixed-K, no device early exit (ruling a);
// minimal per-cycle trace (ruling b). By Claude on 07/17/2026.
// Byte fill (the cudaMemset of the selection bytes; pose_dp_fill_bits on
// ceil(n/4) words would overrun a num_tiles%4 buffer).
extern "C" __global__ void pose_dp_fill_bytes(
int n,
unsigned char value,
unsigned char * data)
{
const int i = (int) (blockIdx.x * blockDim.x + threadIdx.x);
if (i < n) data[i] = value;
}
extern "C" __global__ void pose_scene_dp_step(PoseSceneDp * s);
extern "C" __global__ void pose_scene_dp_commit(PoseSceneDp * s);
// Per-cycle LMA continuation: tail-enqueued by pose_measure_dp_tail AFTER the
// peak kernel, so the corr-row count and peak rows are LIVE. Snapshots the
// measure status trace, enqueues the prepare stages (full or light) and
// chains the step launcher.
extern "C" __global__ void pose_scene_dp_lma(PoseSceneDp * s)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
const int cycle = s->cycle;
if (s->mstats != NULL) {
for (int i = 0; i < POSE_MEASURE_STATUS_INTS; i++) {
s->mstats[cycle * POSE_MEASURE_STATUS_INTS + i] = s->mc->status[i];
}
}
const int nct = *s->num_corr_tiles; // LIVE (>0: the tail chains only on success)
const int set = *s->current_set;
float * cur_vectors = s->set_vectors[set];
float * cur_fx = s->set_fx[set];
float * cur_jt = s->set_jt[set];
unsigned char * cur_valid = s->set_valid[set];
const int pose_threads = 256;
const int pose_blocks = (s->num_tiles + pose_threads - 1) / pose_threads;
const int a_threads = 256; // host assemble launch shape
const int a_blocks = (nct + a_threads - 1) / a_threads;
const int light = s->prepare_light;
const int nc = s->num_components;
const int np = POSE_NUM_PARAMS;
if (light) {
// the D1 light body: frozen conditioning, fresh measured y offsets only
const int clear_n = nc * s->num_tiles;
pose_lma_dp_clear<<<(clear_n + 255) / 256, 256, 0, cudaStreamTailLaunch>>>(
clear_n, s->y);
} else {
// exec_pose_lma_prepare_resident(light=0) pre-zero: y/weights over the
// full value range, eigen, selection, and the compact prep result (the
// resident pull/reg rows behind it survive - the host memset range).
const int num_values = nc * s->num_tiles + np;
pose_lma_dp_clear<<<(num_values + 255) / 256, 256, 0, cudaStreamTailLaunch>>>(
num_values, s->y);
pose_lma_dp_clear<<<(num_values + 255) / 256, 256, 0, cudaStreamTailLaunch>>>(
num_values, s->weights);
pose_lma_dp_clear<<<(4 * s->num_tiles + 255) / 256, 256, 0, cudaStreamTailLaunch>>>(
4 * s->num_tiles, s->eigen);
pose_dp_fill_bytes<<<(s->num_tiles + 255) / 256, 256, 0, cudaStreamTailLaunch>>>(
s->num_tiles, 0, s->selection);
pose_lma_dp_clear<<<1, 256, 0, cudaStreamTailLaunch>>>(
POSE_LMA_PREP_RESULT, s->prep_result);
}
pose_lma_assemble<<<a_blocks, a_threads, 0, cudaStreamTailLaunch>>>(
nct, s->num_tiles, s->corr_slot, s->ntile_shift,
nc, s->use_eigen, s->eig_min_sqrt, s->eig_max_sqrt,
s->min_confidence, s->same_weights, light,
s->corr_indices, s->peaks,
s->y, s->weights, s->eigen, s->selection);
if (!light) {
pose_lma_prepare_norm<<<1, 1, 0, cudaStreamTailLaunch>>>(
s->num_tiles, nc, np, s->pull,
s->y, s->weights, s->prep_result);
}
pose_fx_jacobian<<<pose_blocks, pose_threads, 0, cudaStreamTailLaunch>>>(
s->num_tiles, 1, s->cameras,
s->ref_radial, s->ref_rbr, s->ref_ers,
s->scene_radial, s->scene_rbr, s->scene_ers,
cur_vectors, s->centers, s->selection,
cur_fx, cur_jt, cur_valid);
pose_lma_prepare_finish<<<1, 1, 0, cudaStreamTailLaunch>>>(
s->num_tiles, nc, np, light,
cur_fx, cur_jt, cur_valid, s->eigen, s->regweights,
s->y, s->weights, s->prep_result);
pose_scene_dp_step<<<1, 1, 0, cudaStreamTailLaunch>>>(s);
}
// Step launcher: runs AFTER pose_lma_prepare_finish, so the full-mode
// pure_weight and prepare validity are LIVE. Enqueues the six validated
// resident step stages (the spike/D1 sequence) and chains the commit.
extern "C" __global__ void pose_scene_dp_step(PoseSceneDp * s)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
float pure_weight = s->pure_weight;
if (!s->prepare_light) {
if (s->prep_result[POSE_LMA_PREP_VALID] == 0.0f) {
s->status[1] = -13; // = the host prepare's invalid code; chain stops (fail-safe)
return;
}
pure_weight = s->prep_result[POSE_LMA_PREP_PURE_WEIGHT];
}
const int set = *s->current_set;
float * cur_vectors = s->set_vectors[set];
float * cur_fx = s->set_fx[set];
float * cur_jt = s->set_jt[set];
unsigned char * cur_valid = s->set_valid[set];
float * cand_vectors= s->set_vectors[set ^ 1];
float * cand_fx = s->set_fx[set ^ 1];
float * cand_jt = s->set_jt[set ^ 1];
unsigned char * cand_valid = s->set_valid[set ^ 1];
const float * candidate_vector = cand_vectors + 3 * POSE_NUM_PARAMS;
const int pose_threads = 256;
const int pose_blocks = (s->num_tiles + pose_threads - 1) / pose_threads;
pose_lma_prepare_products<<<s->num_partials, POSE_LMA_REDUCE_THREADS, 0, cudaStreamTailLaunch>>>(
s->num_tiles, cur_fx, cur_jt, cur_valid,
s->y, s->weights, s->eigen, s->vector,
s->jt_out, s->ymfx_out, s->partials);
pose_lma_reduce_step<<<1, 1, 0, cudaStreamTailLaunch>>>(
s->num_partials, s->lambda, pure_weight,
s->partials, s->vector, s->result);
pose_lma_dp_candidate_vector<<<1, 1, 0, cudaStreamTailLaunch>>>(
cur_vectors, s->result, cand_vectors);
pose_fx_jacobian<<<pose_blocks, pose_threads, 0, cudaStreamTailLaunch>>>(
s->num_tiles, 1, s->cameras,
s->ref_radial, s->ref_rbr, s->ref_ers,
s->scene_radial, s->scene_rbr, s->scene_ers,
cand_vectors, s->centers, s->selection,
cand_fx, cand_jt, cand_valid);
pose_lma_prepare_products<<<s->num_partials, POSE_LMA_REDUCE_THREADS, 0, cudaStreamTailLaunch>>>(
s->num_tiles, cand_fx, cand_jt, cand_valid,
s->y, s->weights, s->eigen, candidate_vector,
s->cand_jt_out, s->cand_ymfx_out, s->cand_partials);
pose_lma_reduce_rms<<<1, 1, 0, cudaStreamTailLaunch>>>(
s->num_partials, pure_weight, s->rms_diff,
s->cand_partials, s->result, s->current_set);
pose_scene_dp_commit<<<1, 1, 0, cudaStreamTailLaunch>>>(s);
}
// Commit: the anchor tracks the ACCEPTED pose (the D1 rule - a stale anchor
// makes every following candidate overshoot), per-cycle 25-float trace
// snapshot, then the NEXT cycle's measure chain (self-chaining: this launcher
// ran tail-enqueued behind reduce_rms, so the accept flip is visible to the
// next pose_measure_dp).
extern "C" __global__ void pose_scene_dp_commit(PoseSceneDp * s)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
if (s->result[POSE_LMA_ACCEPTED] != 0.0f) {
#pragma unroll
for (int par = 0; par < POSE_NUM_PARAMS; par++) {
s->vector[par] = s->result[16 + par];
}
}
const int cycle = s->cycle;
if (s->trace != NULL) {
for (int i = 0; i < POSE_LMA_RESIDENT_RESULT; i++) {
s->trace[cycle * POSE_LMA_RESIDENT_RESULT + i] = s->result[i];
}
}
s->status[0] = cycle + 1;
if (cycle + 1 < s->num_cycles) {
s->cycle = cycle + 1;
pose_measure_dp<<<1, 1, 0, cudaStreamTailLaunch>>>(s->mc);
}
}
// The D3 parent: ONE device entry for the whole per-scene solve. The measure
// pose needs no upload beyond scene entry - after an accept flip the current
// set already holds the candidate built by pose_lma_dp_candidate_vector
// (bit-identical to the host re-upload, the D1-proven fact).
extern "C" __global__ void pose_scene_dp(PoseSceneDp * s)
{
if ((blockIdx.x != 0) || (threadIdx.x != 0)) return;
s->cycle = 0;
s->status[0] = 0;
s->status[1] = 0;
pose_measure_dp<<<1, 1, 0, cudaStreamTailLaunch>>>(s->mc);
}
#endif // __CUDACC_RTC__
extern "C" __global__ void lma_normal_products(
......
......@@ -371,9 +371,75 @@ typedef struct {
const float * peak_debias; float * peaks;
int * status; // [0] rc (corr rows or stage-tagged negative),
// [1] pairs, [2] surviving, [3] misaligned, [4] corr rows
// DP rung D3: non-NULL = per-scene parent continuation (PoseSceneDp*). The
// peak tail launcher chains pose_scene_dp_lma after a successful measure;
// guard failures stop the whole scene chain (fail-safe). NULL = standalone
// rung-D2 behavior, unchanged. By Claude on 07/17/2026.
void * scene;
} PoseMeasureChain;
#define POSE_MEASURE_STATUS_INTS 5
// ── DP rung D3 (ladder ratified 2026-07-16): full per-scene parent ───────────
// ONE device entry per scene: num_cycles x [measure chain (the D2 stage list,
// via the resident PoseMeasureChain) -> prepare -> step -> commit], per-cycle
// QC trace to device buffers, ONE end-of-scene readback. prepare_light selects
// the per-cycle conditioning policy: 0 = production FULL prepare (per-cycle
// re-derivation - C3/C3b falsified freezing, pose_freeze_cycle defaults to 0;
// pure_weight is read LIVE from prep_result each cycle), 1 = the D1 light body
// (frozen conditioning, static pure_weight; kept for the recorded DP-era
// experiments, e.g. previous-scene conditioning). The launchers self-chain
// (D1 pattern): each runs tail-enqueued BEHIND the work it must observe, so
// LIVE reads of current_set / corr-row count / prep_result are ordered.
// cycle/status are device-mutated; both descriptors live in device memory.
// By Claude on 07/17/2026.
typedef struct {
int num_cycles;
int cycle; // device-mutable loop index (commit advances it)
int prepare_light; // 0 = per-cycle full prepare (production), 1 = D1 light body
int num_tiles;
int num_partials;
float lambda; // production policy v1: fixed lambda0, inner=1
float pure_weight; // light mode only; full mode reads prep_result LIVE
float rms_diff;
// pose_lma_assemble policy (= the host prepare's scalar args)
int corr_slot;
int ntile_shift;
int num_components;
int use_eigen;
float eig_min_sqrt;
float eig_max_sqrt;
float min_confidence;
int same_weights;
// the measure chain this parent drives (device copy; its .scene points back)
const PoseMeasureChain * mc;
// resident correlation / camera / tile state (= PoseDpCycles, live counts)
const int * corr_indices;
const float * peaks;
const int * num_corr_tiles; // LIVE corr-row count (assemble grid per cycle)
const PoseCameraMeta * cameras;
const float * ref_radial; const float * ref_rbr; const float * ref_ers;
const float * scene_radial;const float * scene_rbr;const float * scene_ers;
const float * centers;
unsigned char * selection; // full prepare clears+rebuilds it every cycle
float * set_vectors[2];
float * set_fx[2];
float * set_jt[2];
unsigned char * set_valid[2];
float * y; float * weights; float * eigen;
float * vector; // linearization anchor; device-updated on accept
const float * pull; // pull-target rows (full prepare, resident upload)
const float * regweights;
float * jt_out; float * ymfx_out; float * partials;
float * cand_jt_out; float * cand_ymfx_out; float * cand_partials;
float * result; // [POSE_LMA_RESIDENT_RESULT]
float * prep_result; // [POSE_LMA_PREP_RESULT] (+pull/reg rows behind it)
int * current_set;
float * trace; // [num_cycles][POSE_LMA_RESIDENT_RESULT]
int * mstats; // [num_cycles][POSE_MEASURE_STATUS_INTS]
int * status; // [0] cycles completed, [1] LMA rc (0 / -13 invalid prepare)
} PoseSceneDp;
#define POSE_SCENE_STATUS_INTS 2
extern "C" __global__ void lma_normal_products(
int num_params,
int num_values,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment