Commit f8df8dce authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: rung-2 closure - GPU peak kernel in production (JNA, float): PD never leaves the GPU

leanMeasure now measures peaks ON the GPU (corr2D_peak_eig_f via
GpuQuad.execCorr2DPeaks, float = the ratified production precision; the
double kernel stays the validation tier): per LMA iteration only the small
task streams go up and [n_corr][8] peak floats come down - the PD download
happens ONLY for debug rendering (-POSE-RT-CORR2D), the pose_corr export
capture, or the JCuda fallback (the original Java-oracle loop, kept intact).

- GpuQuad: execCorr2DPeaks/setPeakDebias base = null/false (JCuda -> Java
  fallback); PEAK_ROW_FLOATS contract constant.
- GpuQuadJna + TpJna: tp_proc_exec_corr2d_peak(use_float)/get_peaks/
  set_peak_debias bindings.
- TDCorrTile: convertTDtoPDInPlace split - fetchNormalizedPD is now the
  optional D2H half.
- CuasPoseRT.leanMeasure: normalize in place -> float peak kernel -> map
  rows by packed corr index (valid==0 = kernel reject == oracle null);
  de-bias uploaded as the float array when pose_debias > 0; once-per-program
  'GPU peak kernel active' console line.
- PoseCorrExport.iterGpuPeaks: the production float rows land in the case
  (expected_gpu_peaks_it<k>, corr_indices order) = the TOL-0 peak tier;
  test_pose_corr_jna compares its float replay bit-exactly when present
  (tile_processor_gpu companion commit).

mvn package OK; CuasTD headless self-test ALL PASSED; run_cases.sh ALL PASS
(pre-hookup case: gpu_peaks buffer absent -> skipped gracefully).
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent ded8dd7c
......@@ -2781,6 +2781,37 @@ public class GpuQuad{ // quad camera description
cuCtxSynchronize();
}
// ---- per-tile correlation peak measurement (tp_peak.cu, roadmap 3-C rung 2) ----
// By Claude on 07/14/2026, Andrey's rulings (15-arg frozen contract, float production).
/** peak output row: {dx, dy, strength, e0x, e0y, lambda0, lambda1, valid(1/0)} */
public static final int PEAK_ROW_FLOATS = 8;
/**
* Run the frozen 15-arg getMaxXYCmEig contract ON THE GPU over the FZ-normalized
* pixel-domain correlation buffer (call AFTER execCorr2D_normalize(false,...)) -
* replaces the PD D2H + Java Correlation2d.getMaxXYCmEig loop on backends that
* carry the kernel. Rows align 1:1 with getCorrIndices() order; non-selected /
* rejected slots = 7x NaN + valid 0. Base (JCuda) implementation returns null =
* kernel not available, caller keeps the Java oracle path.
* @param pair_select packed-index pair code to process (lean sum = 0xff, -1 = all)
* @param abs_min/rel_min/min_peak/eig_sub_frac imp.eig_min_abs/eig_min_rel/min_str_sum/eig_sub_frac
* @param refine recentering passes (curt.pose_recenter)
* @param eig_sub_frac1/scale_axes/inc_axes recentering params (imp.*)
* @param use_float true = float kernel (production, ratified 07/14/2026), false = double (validation tier)
* @return [num_corr_tiles * PEAK_ROW_FLOATS] rows, or null when unsupported
*/
public float [] execCorr2DPeaks(
int pair_select,
double abs_min, double rel_min, double min_peak, double eig_sub_frac,
int refine, double eig_sub_frac1, double scale_axes, double inc_axes,
boolean use_float) {
return null; // JCuda module does not carry tp_peak.cu - Java oracle fallback
}
/** Upload (or clear, null) the optional envelope de-bias array for execCorr2DPeaks.
* Base: unsupported. @return true if the backend accepted it */
public boolean setPeakDebias(float [] debias) {
return false;
}
public void execRBGA(
double [] color_weights,
boolean is_lwir,
......
......@@ -364,6 +364,24 @@ public class GpuQuadJna extends GpuQuad {
// fcorr_weights (per-tile fat-zero divisor) not yet plumbed -> native uses null (matches the common null path)
lib.tp_proc_exec_corr2d_normalize(proc, combo ? 1 : 0, fat_zero, corr_radius);
}
// ---- peak measurement (tp_peak.cu, roadmap 3-C rung 2) ---- // By Claude on 07/14/2026
@Override public float[] execCorr2DPeaks(
int pair_select,
double abs_min, double rel_min, double min_peak, double eig_sub_frac,
int refine, double eig_sub_frac1, double scale_axes, double inc_axes,
boolean use_float) {
int n = lib.tp_proc_exec_corr2d_peak(proc, pair_select,
(float) abs_min, (float) rel_min, (float) min_peak, (float) eig_sub_frac,
refine, (float) eig_sub_frac1, (float) scale_axes, (float) inc_axes,
use_float ? 1 : 0);
if (n <= 0) return null; // caller falls back to the Java oracle path
float[] peaks = new float[n * PEAK_ROW_FLOATS];
return (lib.tp_proc_get_peaks(proc, peaks) == n) ? peaks : null;
}
@Override public boolean setPeakDebias(float[] debias) {
return lib.tp_proc_set_peak_debias(proc, debias, (debias == null) ? 0 : debias.length) == 0;
}
@Override public float[][] getCorr2D(int corr_rad) {
int corr_size = (2 * corr_rad + 1) * (2 * corr_rad + 1);
int n = lib.tp_proc_num_corr_tiles(proc);
......
......@@ -106,6 +106,20 @@ public interface TpJna extends Library {
int tp_proc_exec_corr2d_combine(Pointer proc, int initCorrCombo, int numPairs, int pairsMask);
int tp_proc_exec_corr2d_normalize(Pointer proc, int combo, double fatZero, int corrRadius);
int tp_proc_get_corr2d(Pointer proc, float[] out, int corrRad);
// ---- peak measurement (tp_peak.cu corr2D_peak_eig{,_f}, roadmap 3-C rung 2) ---- // By Claude on 07/14/2026
/** Optional envelope de-bias array ((2*corrRad+1)^2 floats); len 0/null clears -> kernel gets NULL. */
int tp_proc_set_peak_debias(Pointer proc, float[] data, int len);
/** Frozen 15-arg getMaxXYCmEig contract on the normalized PD buffer (run AFTER
* tp_proc_exec_corr2d_normalize combo=0): one {dx,dy,str,e0x,e0y,l0,l1,valid} row per
* resident corr tile. pairSelect 0xff = lean sum slots (-1 = all); useFloat: 0 = double
* (oracle-matched validation tier), 1 = float (production - ratified 07/14/2026, real-case
* float-vs-double dx/dy <= 7.2e-7 px). Returns the row count or <0. */
int tp_proc_exec_corr2d_peak(Pointer proc, int pairSelect,
float absMin, float relMin, float minPeak, float eigSubFrac,
int refine, float eigSubFrac1, float scaleAxes, float incAxes,
int useFloat);
/** D2H the peak rows (count x 8 floats). Returns the count. */
int tp_proc_get_peaks(Pointer proc, float[] out);
int tp_proc_num_corr_tiles(Pointer proc);
int tp_proc_num_corr_combo(Pointer proc);
/** Upload to a named __constant__ symbol (lpf_data / lpf_corr / lpf_rb_corr). 0 on success. */
......
......@@ -105,6 +105,7 @@ public class PoseCorrExport {
private static final ArrayList<int []> pd_indices = new ArrayList<int []>(); // dense tile index per PD tile
private static final ArrayList<float []> pd_data = new ArrayList<float []>(); // normalized PD, [n_pd*225]
private static final ArrayList<float []> peaks = new ArrayList<float []>(); // oracle {dx,dy,str,e0x,e0y,l0,l1}, NaN row = rejected
private static final ArrayList<float []> gpu_peaks = new ArrayList<float []>(); // PRODUCTION float-kernel rows [n_corr*8], corr_indices order - the tol-0 peak tier // By Claude on 07/14/2026
private static double fz_inter, corr_weight; // execCorr2D_normalize inputs
private static double eig_min_abs, eig_min_rel, min_str_sum, eig_sub_frac; // frozen peak-contract params
private static int pose_recenter; // recentering passes (runtime contract param)
......@@ -141,6 +142,7 @@ public class PoseCorrExport {
tasks0_post.clear(); tasks1_post.clear();
corr_indices.clear(); corr_td.clear();
pd_indices.clear(); pd_data.clear(); peaks.clear(); // By Claude on 07/13/2026
gpu_peaks.clear(); // By Claude on 07/14/2026
debias_arr = null;
debias_oracle = null;
pd_statics_ok = false;
......@@ -312,6 +314,19 @@ public class PoseCorrExport {
peaks.add(pk);
}
/**
* Hook (CuasPoseRT.leanMeasure, after execCorr2DPeaks): the PRODUCTION peak rows
* exactly as the float GPU kernel produced them ([n_corr][8], aligned with the
* resident corr_indices order = expected_corr_indices_it<k>). This is the TOL-0
* tier for the peak kernel: the NVRTC replay (test_pose_corr_jna, use_float=1)
* must reproduce these bit-exactly. null (JCuda fallback ran) = not captured.
* By Claude on 07/14/2026.
*/
public static synchronized void iterGpuPeaks(QuadCLT scene, float [] peaks_rows) {
if (!armed || done || (bound_scene == null) || !bound_scene.equals(scene.getImageName())) return;
gpu_peaks.add((peaks_rows == null) ? null : peaks_rows.clone());
}
/**
* Hook (CuasPoseRT.leanFitScene, after the measure<->solve cycles succeeded):
* all iterations of the bound scene are captured - write the case.
......@@ -522,6 +537,11 @@ public class PoseCorrExport {
tdw.buf("expected_pd_indices_it"+k, pd_indices.get(k), pd_indices.get(k).length);
tdw.buf("expected_corr_pd_it"+k, pd_data.get(k), pd_data.get(k).length / pd_size, pd_size);
tdw.buf("expected_peaks_it"+k, peaks.get(k), peaks.get(k).length / 7, 7);
// production float-kernel rows, corr_indices order (the tol-0 peak tier;
// absent when the JCuda fallback measured the scene). By Claude on 07/14/2026.
if ((k < gpu_peaks.size()) && (gpu_peaks.get(k) != null)) {
tdw.buf("expected_gpu_peaks_it"+k, gpu_peaks.get(k), gpu_peaks.get(k).length / 8, 8);
}
}
if (debug > -3) {
System.out.println("PoseCorrExport: wrote "+tdw.getDir()+" (scene "+bound_scene+", "+
......
......@@ -490,15 +490,32 @@ public class TDCorrTile {
final double gpu_fat_zero,
final int debug_level
){
final int gpu_corr_rad = GPUTileProcessor.DTT_SIZE - 1;
final int tilesX = gpuQuad.getImageWidth() / GpuQuad.getDttSize();
final int tilesY = gpuQuad.getImageHeight() / GpuQuad.getDttSize();
final int [] indices = gpuQuad.getCorrIndices(); // small D2H; also refreshes num_corr_tiles for normalize/getCorr2D
gpuQuad.execCorr2D_normalize(
false, // per-pair buffer (the inter output)
gpu_fat_zero,
null, // weights: null == the repacked path's uniform 1.0 (see header)
gpu_corr_rad);
GPUTileProcessor.DTT_SIZE - 1);
return fetchNormalizedPD(gpuQuad, indices);
}
/**
* The D2H + mapping half of convertTDtoPDInPlace, split out (rung-2 second half,
* 07/14/2026): with the GPU peak kernel active the normalize runs WITHOUT this
* download - the PD crosses only for debug rendering (-POSE-RT-CORR2D), the
* pose_corr export capture, or the JCuda Java-oracle fallback.
* Call AFTER execCorr2D_normalize(false, ...). By Claude on 07/14/2026.
* @param gpuQuad GPU quad instance
* @param indices resident packed corr indices (gpuQuad.getCorrIndices())
* @return sparse array in line-scan order, each element null or double[225]
*/
public static double [][] fetchNormalizedPD(
final GpuQuad gpuQuad,
final int [] indices
){
final int gpu_corr_rad = GPUTileProcessor.DTT_SIZE - 1;
final int tilesX = gpuQuad.getImageWidth() / GpuQuad.getDttSize();
final int tilesY = gpuQuad.getImageHeight() / GpuQuad.getDttSize();
final float [][] fcorr2D = gpuQuad.getCorr2D(gpu_corr_rad);
final double [][] mapped_corrs = new double [tilesX * tilesY][];
final Thread[] threads = ImageDtt.newThreadArray(ImageDtt.THREADS_MAX);
......
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