Skip to content

Commit 0909ab6

Browse files
committed
#558 WWZ: replace Gauss–Jordan matinv with closed-form 3×3 symmetric inverse
- Faster, no per-call double[][] allocation; add useLegacyMatinv for Gauss–Jordan benchmark - Add WWZMatinvBenchmarkTest to compare implementations Made-with: Cursor
1 parent e0e13bd commit 0909ab6

2 files changed

Lines changed: 134 additions & 11 deletions

File tree

src/org/aavso/tools/vstar/util/period/wwz/WeightedWaveletZTransform.java

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@
5555
*/
5656
public class WeightedWaveletZTransform implements IAlgorithm {
5757

58+
/** For benchmark only: use legacy Gauss-Jordan matinv instead of closed-form. Package visibility for tests. */
59+
static boolean useLegacyMatinv = false;
60+
5861
// Observations to be analysed.
5962
private List<ValidObservation> obs;
6063

@@ -399,25 +402,63 @@ public void make_freqs_from_period_range(double minPer, double maxPer,
399402
}
400403

401404
/**
402-
* Invert the matrix of the wwz equations...
405+
* Invert the 3×3 symmetric matrix in dmat (in-place).
406+
* Uses a closed-form cofactor formula. Faster than Gauss-Jordan for fixed 3×3,
407+
* no pivoting logic, and avoids the per-(tau,freq) loop overhead.
403408
*/
404409
private void matinv() throws InterruptedException {
405-
double dsol[][] = new double[3][3];// (0:2,0:2);
406-
double dfac;
410+
if (useLegacyMatinv) {
411+
matinvGaussJordan();
412+
return;
413+
}
414+
// Symmetric 3×3: a=dmat[0][0], b=dmat[0][1], c=dmat[0][2], d=dmat[1][1], e=dmat[1][2], f=dmat[2][2]
415+
double a = dmat[0][0], b = dmat[0][1], c = dmat[0][2];
416+
double d = dmat[1][1], e = dmat[1][2], f = dmat[2][2];
417+
418+
// det = a(df - e²) - b(bf - ce) + c(be - cd)
419+
double det = a * (d * f - e * e) - b * (b * f - c * e) + c * (b * e - c * d);
420+
if (Math.abs(det) <= 1.0e-14) {
421+
return; // singular, leave dmat unchanged (matches previous behaviour)
422+
}
423+
double invDet = 1.0 / det;
407424

408-
int ndim = 2;
425+
if (interrupted) {
426+
throw new InterruptedException();
427+
}
409428

429+
// Adjugate (symmetric for symmetric input), then inv = adj/det
430+
double a00 = (d * f - e * e) * invDet;
431+
double a01 = (c * e - b * f) * invDet;
432+
double a02 = (b * e - c * d) * invDet;
433+
double a11 = (a * f - c * c) * invDet;
434+
double a12 = (c * b - a * e) * invDet;
435+
double a22 = (a * d - b * b) * invDet;
436+
437+
dmat[0][0] = a00;
438+
dmat[0][1] = a01;
439+
dmat[0][2] = a02;
440+
dmat[1][0] = a01;
441+
dmat[1][1] = a11;
442+
dmat[1][2] = a12;
443+
dmat[2][0] = a02;
444+
dmat[2][1] = a12;
445+
dmat[2][2] = a22;
446+
}
447+
448+
/** Legacy Gauss-Jordan 3×3 in-place inverse; used only when useLegacyMatinv is true (benchmark). */
449+
private void matinvGaussJordan() throws InterruptedException {
450+
double dsol[][] = new double[3][3];
451+
double dfac;
452+
int ndim = 2;
410453
for (int i = 0; i <= 2; i++) {
411454
for (int j = 0; j <= 2; j++) {
412455
dsol[i][j] = 0.0;
413456
}
414457
dsol[i][i] = 1.0;
415-
416458
if (interrupted) {
417459
throw new InterruptedException();
418460
}
419461
}
420-
421462
for (int i = 0; i <= ndim; i++) {
422463
if (dmat[i][i] == 0.0) {
423464
if (i == ndim)
@@ -426,16 +467,14 @@ private void matinv() throws InterruptedException {
426467
if (dmat[j][i] != 0.0) {
427468
for (int k = 0; k <= ndim; k++) {
428469
dmat[i][k] = dmat[i][k] + dmat[j][k];
429-
dsol[i][j] = dsol[i][j] + dsol[j][k];
470+
dsol[i][k] = dsol[i][k] + dsol[j][k];
430471
}
431472
}
432473
}
433-
434474
if (interrupted) {
435475
throw new InterruptedException();
436476
}
437477
}
438-
439478
dfac = dmat[i][i];
440479
for (int j = 0; j <= ndim; j++) {
441480
dmat[i][j] = dmat[i][j] / dfac;
@@ -448,7 +487,6 @@ private void matinv() throws InterruptedException {
448487
dmat[j][k] = dmat[j][k] - (dmat[i][k] * dfac);
449488
dsol[j][k] = dsol[j][k] - (dsol[i][k] * dfac);
450489
}
451-
452490
if (interrupted) {
453491
throw new InterruptedException();
454492
}
@@ -459,7 +497,6 @@ private void matinv() throws InterruptedException {
459497
for (int j = 0; j <= ndim; j++) {
460498
dmat[i][j] = dsol[i][j];
461499
}
462-
463500
if (interrupted) {
464501
throw new InterruptedException();
465502
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/**
2+
* VStar: a statistical analysis tool for variable star data.
3+
* Copyright (C) 2010 AAVSO (http://www.aavso.org/)
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as
7+
* published by the Free Software Foundation, either version 3 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
package org.aavso.tools.vstar.util.period.wwz;
19+
20+
import org.aavso.tools.vstar.util.period.dcdft.DataTestBase;
21+
22+
/**
23+
* Benchmark to quantify speedup of closed-form 3×3 matinv over legacy Gauss-Jordan.
24+
* Same scenario as WWZTUmi2420000To2425000Test; runs multiple iterations and prints times.
25+
*/
26+
public class WWZMatinvBenchmarkTest extends DataTestBase {
27+
28+
private static final int WARMUP = 1;
29+
private static final int ITERATIONS = 5;
30+
31+
public WWZMatinvBenchmarkTest() {
32+
super("WWZ matinv benchmark", TUmi2420000To2425000Data.data);
33+
}
34+
35+
/**
36+
* Run WWZ with new (closed-form) matinv then with legacy (Gauss-Jordan), report total times and speedup.
37+
*/
38+
public void testMatinvBenchmark() throws Exception {
39+
double minFreq = 0.01;
40+
double maxFreq = 0.02;
41+
double deltaFreq = 0.001;
42+
double decay = 0.01;
43+
double timeDivisions = 50.0;
44+
45+
// Warmup and time: closed-form (new) matinv
46+
WeightedWaveletZTransform.useLegacyMatinv = false;
47+
runWarmup(obs, decay, timeDivisions, minFreq, maxFreq, deltaFreq);
48+
long newNs = runIterations(obs, decay, timeDivisions, minFreq, maxFreq, deltaFreq, ITERATIONS);
49+
50+
// Warmup and time: legacy Gauss-Jordan matinv
51+
WeightedWaveletZTransform.useLegacyMatinv = true;
52+
runWarmup(obs, decay, timeDivisions, minFreq, maxFreq, deltaFreq);
53+
long legacyNs = runIterations(obs, decay, timeDivisions, minFreq, maxFreq, deltaFreq, ITERATIONS);
54+
55+
WeightedWaveletZTransform.useLegacyMatinv = false;
56+
57+
double newMs = newNs / 1_000_000.0;
58+
double legacyMs = legacyNs / 1_000_000.0;
59+
double speedup = (double) legacyNs / (double) newNs;
60+
61+
System.out.println("WWZ matinv benchmark (TUMi 2420000–2425000, " + ITERATIONS + " runs each):");
62+
System.out.println(" Closed-form (new) matinv: " + String.format("%.2f", newMs) + " ms total");
63+
System.out.println(" Legacy Gauss-Jordan : " + String.format("%.2f", legacyMs) + " ms total");
64+
System.out.println(" Speedup (legacy/new): " + String.format("%.2fx", speedup));
65+
}
66+
67+
private void runWarmup(java.util.List<org.aavso.tools.vstar.data.ValidObservation> obs,
68+
double decay, double timeDivisions, double minFreq, double maxFreq, double deltaFreq) throws Exception {
69+
for (int i = 0; i < WARMUP; i++) {
70+
WeightedWaveletZTransform wwt = new WeightedWaveletZTransform(obs, decay, timeDivisions);
71+
wwt.make_freqs_from_freq_range(minFreq, maxFreq, deltaFreq);
72+
wwt.execute();
73+
}
74+
}
75+
76+
private long runIterations(java.util.List<org.aavso.tools.vstar.data.ValidObservation> obs,
77+
double decay, double timeDivisions, double minFreq, double maxFreq, double deltaFreq, int n) throws Exception {
78+
long start = System.nanoTime();
79+
for (int i = 0; i < n; i++) {
80+
WeightedWaveletZTransform wwt = new WeightedWaveletZTransform(obs, decay, timeDivisions);
81+
wwt.make_freqs_from_freq_range(minFreq, maxFreq, deltaFreq);
82+
wwt.execute();
83+
}
84+
return System.nanoTime() - start;
85+
}
86+
}

0 commit comments

Comments
 (0)