Commit 284f6ae1 authored by Andrey Filippov's avatar Andrey Filippov

CLAUDE: test 7 - windowed-LoG TD product (correlation machinery): broadband...

CLAUDE: test 7 - windowed-LoG TD product (correlation machinery): broadband exact, crease bands UNCHANGED

Andrey's separate-LoG proposal prototyped on the CPU oracle: LoG as a windowed
16x16 DATA tile (taps pre-divided by the sine window AND cosAtten so the
effective filter is the exact flux-normalized LoG), full 16-mult quad product
per tile (GPU correlateAccumulateTiles table == convolve_tile(conj(data),k) -
verified term-by-term), dttt_iie back (mode-TRANSPOSED = GPU dttii_2d),
corr_unfold_tile, windowed OLA with sum(w^2) normalization.

RESULT: impulse fidelity 2.0e-3 (no aberration-kernel cross-term - a separate
pass), but the ramp crease bands are IDENTICAL to the per-bin fold (+/-0.546):
the DTT symmetric-extension reflection is only attenuated by the window, not
removed - the crease is BASIS-INHERENT to any single-tile TD product. The
per-bin fold is the degenerate (symmetric-kernel) case of this same product.
Band-free LoG requires pixel-domain convolution on the assembled image.

Calibrated conventions along the way (documented in code): correlation product
table, dttii mode transpose, corr_unfold patch mapping (+1 offset, no flip),
global gain 4.000, window and cos tap precompensation.
Co-Authored-By: 's avatarClaude Fable 5 <noreply@anthropic.com>
parent da5c157f
...@@ -594,10 +594,180 @@ public class CuasLogFold { ...@@ -594,10 +594,180 @@ public class CuasLogFold {
System.out.println(String.format(" (max|band| = %.3f)", max_abs)); System.out.println(String.format(" (max|band| = %.3f)", max_abs));
} }
} }
// --- test 7: SEPARATE windowed-LoG TD product (Andrey's proposal 07/07/2026,
// "while we are in MCLT alchemy"): the LoG folded like DATA (16x16 pixel
// tile, windowed MCLT fold + DTT-IV, pixel-centered at (8,8) -> all 4
// quadrants), full 16-mult quad product per tile (the correlation
// machinery; LoG symmetric => correlation == convolution), DTT-II back
// (dttt_iie per quadrant = GPU dttii_2d) + corr_unfold_tile -> 15x15
// patch, windowed OLA with per-pixel sum(w^2) normalization.
// RESULT (07/07/2026): the crease bands are IDENTICAL to the per-bin
// fold (+/-0.546 per 5/row ramp) - the window only makes the DTT
// symmetric-extension REFLECTION small (w~0.1-0.3 at edge pixels), it
// does not remove it; the per-bin fold is the degenerate case of this
// same quad product. The crease is BASIS-INHERENT to any single-tile
// TD product. Correlation tolerates it (smooth additive bias does not
// move peaks); filtering exposes it. Broadband fidelity is excellent
// (impulse 2e-3, better than the kernel fold - no aberration-kernel
// cross-term since this is a separate pass). Band-free LoG requires
// pixel-domain convolution on the ASSEMBLED image (no tile boundaries).
{
System.out.println("--- test 7: windowed-LoG TD product (correlation-style, separate pass)");
final int n2 = 2 * TRANSFORM_SIZE;
double [] k16 = new double [n2 * n2];
int lr = log_w / 2;
// pre-divide taps by the sine window: the fold WINDOWS the tile, and at
// tap offset 3 the window is already ~0.78 - without this the LoG's
// negative surround is under-weighted vs its center, breaking the flux
// balance (measured ~8% DC leakage). w*(log/w) = exact LoG after fold.
double [] w16 = new double [n2];
for (int i = 0; i < n2; i++) w16[i] = Math.sin(Math.PI * (i + 0.5) / n2);
// ... and by cosAtten (the OLA window non-commutation, same as getLogTd):
// the effective applied filter is then the EXACT flux-normalized LoG
// (DC response = 0 - without this the attenuated LoG has DC gain ~0.06,
// which showed as exactly 0.0607*ramp in the first run of this test)
for (int dy = -lr; dy <= lr; dy++) {
for (int dx = -lr; dx <= lr; dx++) {
k16[(TRANSFORM_SIZE + dy) * n2 + TRANSFORM_SIZE + dx] =
log_pix[(dy + lr) * log_w + dx + lr] /
(w16[TRANSFORM_SIZE + dy] * w16[TRANSFORM_SIZE + dx] * cosAtten(dx, dy));
}
}
DttRad2 dtt7 = new DttRad2(TRANSFORM_SIZE);
dtt7.set_window(1);
double [][] log_tdq = new double [4][];
for (int m = 0; m < 4; m++) {
log_tdq[m] = dtt7.dttt_iv(dtt7.fold_tile(k16, TRANSFORM_SIZE, m), m, TRANSFORM_SIZE);
}
// calibrate patch orientation + conjugation side + global gain on the
// impulse image against the cos-attenuated pixel LoG (the OLA window
// non-commutation is still expected - benign, precompensable)
double [] oracle_imp = pixConvolve(impulses, width, log_pix, log_w, false); // exact pixel LoG
double best_err = Double.MAX_VALUE; int best_var = -1; double best_gain = 1.0;
int height7 = impulses.length / width;
for (int variant = 0; variant < 8; variant++) {
double [] o7 = tdLogFilter(imageDtt, impulses, width, log_tdq,
(variant & 1) != 0, (variant & 2) != 0, (variant & 4) != 0);
double sxy = 0, sxx = 0;
for (int y = margin; y < height7 - margin; y++) {
for (int x = margin; x < width - margin; x++) {
sxy += o7[y*width+x] * oracle_imp[y*width+x];
sxx += o7[y*width+x] * o7[y*width+x];
}
}
double g = sxy / sxx;
double md7 = 0, mr7 = 0;
for (int y = margin; y < height7 - margin; y++) {
for (int x = margin; x < width - margin; x++) {
md7 = Math.max(md7, Math.abs(g * o7[y*width+x] - oracle_imp[y*width+x]));
mr7 = Math.max(mr7, Math.abs(oracle_imp[y*width+x]));
}
}
double e = md7 / mr7;
System.out.println(String.format(
" 7 variant %d (conj_log=%b, flip=%b, modeT=%b): gain=%9.5f rel_err=%9.3e",
variant, (variant & 1) != 0, (variant & 2) != 0, (variant & 4) != 0, g, e));
if (e < best_err) { best_err = e; best_var = variant; best_gain = g; }
}
all_pass &= report(String.format("7: windowed-LoG TD product == pixel LoG broadband (var %d)", best_var),
best_err, WRAP_TOL);
// THE BAND TEST: same ramp as test 6 - crease bands should VANISH
double [] ramp7 = new double [width * height];
for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) ramp7[y*width+x] = 5.0 * y;
double [] o7r = tdLogFilter(imageDtt, ramp7, width, log_tdq,
(best_var & 1) != 0, (best_var & 2) != 0, (best_var & 4) != 0);
double max_band7 = 0;
System.out.print(" ramp 5/row band profile rows 56..71 (test 6 fold gave +/-0.547):");
for (int y = 56; y < 72; y++) {
double s = 0;
for (int x = margin; x < width - margin; x++) s += best_gain * o7r[y*width+x];
s /= (width - 2*margin);
max_band7 = Math.max(max_band7, Math.abs(s));
System.out.print(String.format(" %+.3f", s));
}
System.out.println();
// CHARACTERIZATION, not a target: the crease is basis-inherent (see the
// block comment) - assert it stays at the known level, catching regressions
all_pass &= report("7b: ramp crease bands (EXPECTED ~0.547, basis-inherent)", max_band7, 0.6);
}
System.out.println(all_pass? "ALL PASSED" : "FAILURES PRESENT"); System.out.println(all_pass? "ALL PASSED" : "FAILURES PRESENT");
System.exit(all_pass? 0 : 1); System.exit(all_pass? 0 : 1);
} }
/**
* Filter an image with the LoG entirely in the transform domain using the
* CORRELATION machinery (test 7 / Andrey's separate-LoG proposal): per tile,
* the full 16-multiply quad product of the image MCLT with a windowed+folded
* 16x16 LoG tile (DATA-side fold - no symmetric extension, hence no crease),
* DTT-II back (dttt_iie per quadrant, as GPU dttii_2d), corr_unfold_tile to a
* 15x15 pixel patch, windowed overlap-add normalized by the accumulated w^2.
* Output is full image size (margins under-covered - compare interior only).
* Global scale is NOT normalized here (calibrated by the caller on an impulse).
* By Claude on 07/07/2026.
*/
static double [] tdLogFilter(
ImageDttCPU imageDtt,
double [] img,
int width,
double [][] log_tdq, // [4][64] windowed+folded LoG tile (DTT-IV)
boolean conj_log, // conjugate the LoG side (else the image side)
boolean flip, // patch index mapping direction
boolean mode_t) { // transpose the DTT-II mode bits (GPU works transposed)
final int ts = TRANSFORM_SIZE;
final int n2 = 2 * ts;
final int height = img.length / width;
double [][][][] clt = imageDtt.clt_2d(
img, width, 1, // window_type = sine
0, 0, -1, -1, 0, 8, 0);
final int tilesY = clt.length, tilesX = clt[0].length;
double [] out = new double [img.length];
double [] wsum = new double [img.length];
DttRad2 dtt = new DttRad2(ts);
dtt.set_window(1);
double [] w1 = new double [n2];
for (int i = 0; i < n2; i++) w1[i] = Math.sin(Math.PI * (i + 0.5) / n2);
for (int ty = 0; ty < tilesY; ty++) {
for (int tx = 0; tx < tilesX; tx++) {
// conjugation (correlation vs convolution orientation): negate the
// S components (quadrants 1,2; quadrant 3 flips twice) of ONE side
double [][] a = new double [4][];
double [][] b = new double [4][];
for (int q = 0; q < 4; q++) {
a[q] = clt[ty][tx][q].clone();
b[q] = log_tdq[q].clone();
if ((q == 1) || (q == 2)) {
double [] tgt = conj_log ? b[q] : a[q];
for (int i = 0; i < tgt.length; i++) tgt[i] = -tgt[i];
}
}
imageDtt.convolve_tile(a, b, false); // the same 16-mult quad product
double [][] pd = new double [4][];
for (int q = 0; q < 4; q++) {
int m2 = mode_t ? (((q & 1) << 1) | ((q >> 1) & 1)) : q;
pd[q] = dtt.dttt_iie(a[q], m2, ts); // GPU dttii_2d equivalent
}
double [] patch = dtt.corr_unfold_tile(pd, ts); // 15x15
for (int py = 0; py < n2 - 1; py++) {
int ly = flip ? (n2 - 1 - py) : (py + 1);
int gy = ty * ts + ly;
if ((gy < 0) || (gy >= height)) continue;
for (int px = 0; px < n2 - 1; px++) {
int lx = flip ? (n2 - 1 - px) : (px + 1);
int gx = tx * ts + lx;
if ((gx < 0) || (gx >= width)) continue;
double w = w1[ly] * w1[lx];
out [gy * width + gx] += w * patch[py * (n2 - 1) + px];
wsum[gy * width + gx] += w * w;
}
}
}
}
for (int i = 0; i < out.length; i++) {
out[i] = (wsum[i] > 0) ? (out[i] / wsum[i]) : 0.0;
}
return out;
}
/** max |a-b| over the interior (no normalization); a is the clt-cropped image. */ /** max |a-b| over the interior (no normalization); a is the clt-cropped image. */
static double absErrInterior( static double absErrInterior(
double [] a, double [] a,
......
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