Skip to content

Stats idiomatic groovy 4.x#286

Merged
perNyfelt merged 13 commits intomainfrom
stats-idiomatic-groovy-4.x
Apr 9, 2026
Merged

Stats idiomatic groovy 4.x#286
perNyfelt merged 13 commits intomainfrom
stats-idiomatic-groovy-4.x

Conversation

@perNyfelt
Copy link
Copy Markdown
Member

@perNyfelt perNyfelt commented Apr 6, 2026

4.1 Clean up the higher-risk public API surfaces together so the migration pattern stays consistent across the numerically dense packages.
4.2 Replace public primitive parameters with idiomatic Groovy-facing inputs.
4.3 Replace public scalar return values with BigDecimal.
4.4 Replace public primitive vector/matrix outputs with idiomatic Groovy-facing containers.
4.5 For each hotspot identified in PR 1, benchmark the idiomatic Groovy-facing path against a Java primitive utility.
4.6 For each area benchmarked, decide explicitly whether to:
- use only the idiomatic Groovy implementation
- keep an idiomatic Groovy public wrapper over a Java primitive utility
- defer deeper internal cleanup if the algorithmic risk is too high for the current PR
4.7 Add output-equivalence tests for every retained Java utility path.
4.7.1 Remove or simplify any remaining adapter classes whose primitive bridge methods have become pure delegation only.
4.8 Verification

per and others added 13 commits April 6, 2026 21:06
  I added a module-specific ruleset in matrix-stats/config/codenarc/ruleset.groovy and set ignoreFailures = false in matrix-stats/build.gradle:42. I also fixed the remaining
  actionable findings in the module, including the FitOptions marker interface, nullable collection-return false positives in matrix-stats/src/main/groovy/se/alipsa/matrix/
  stats/formula/ModelFrame.groovy, the runtime catches in matrix-stats/src/main/groovy/se/alipsa/matrix/stats/timeseries/AdfGls.groovy and matrix-stats/src/main/groovy/se/
  alipsa/matrix/stats/timeseries/Granger.groovy, the oddness/binomial cleanup in matrix-stats/src/main/groovy/se/alipsa/matrix/stats/normality/KolmogorovSmirnov.groovy, and
  the remaining test-side naming/closure issues such as matrix-stats/src/test/groovy/se/alipsa/matrix/stats/timeseries/TimeSeriesUtilsTest.groovy and matrix-stats/src/test/
  groovy/QuantileRegressionTest.groovy.
…v2.4.0-idiomaticGroovy.md:398 with the verification entry at matrix-stats/req/v2.4.0-

  idiomaticGroovy.md:465. The main API changes are in matrix-stats/src/main/groovy/se/alipsa/matrix/stats/Anova.groovy:54, matrix-stats/src/main/groovy/se/alipsa/matrix/stats/
  StatUtils.groovy:42, matrix-stats/src/main/groovy/se/alipsa/matrix/stats/formula/SplineBasisExpander.groovy:95, matrix-stats/src/main/groovy/se/alipsa/matrix/stats/
  regression/LoessOptions.groovy:15, matrix-stats/src/main/groovy/se/alipsa/matrix/stats/regression/GamOptions.groovy:14, matrix-stats/src/main/groovy/se/alipsa/matrix/stats/
  cluster/KMeansPlusPlus.groovy:401, matrix-stats/src/main/groovy/se/alipsa/matrix/stats/solver/UnivariateObjective.groovy:27, and matrix-stats/src/main/groovy/se/alipsa/
  matrix/stats/solver/MultivariateObjective.groovy:27. The contingency/ANOVA result APIs now accept Number alpha inputs, utility/formula helpers accept Groovy collections,
  option surfaces accept Number, and the low-level solver interfaces now have Groovy-facing bridges while retaining primitive kernels.
…ts/src/main/groovy/se/alipsa/matrix/stats/timeseries/Ccm.groovy:163. I also added exact integer

  validation in matrix-stats/src/main/groovy/se/alipsa/matrix/stats/util/NumericConversion.groovy:224 and switched matrix-stats/src/main/groovy/se/alipsa/matrix/stats/
  distribution/HypergeometricDistribution.groovy:45 and the CCM library-size normalization to use it, so 10.5 now fails explicitly instead of truncating silently. Coverage was
  added in matrix-stats/src/test/groovy/distribution/HypergeometricDistributionTest.groovy:79 and matrix-stats/src/test/groovy/timeseries/CcmTest.groovy:155.

  For 3, I checked the actual source tree under matrix-stats/src and there are currently no source files that both import CompileStatic and fail to use @CompileStatic; the
  examples you cited only showed up in generated build/tmp/groovydoc copies, not in source. For 4 and 5, I left the scalar result-field migration alone and made the intent
  explicit in matrix-stats/req/v2.4.0-idiomaticGroovy.md:419: AnovaResult and timeseries result scalars are still intentionally deferred to section 4.3.
…stats/req/v2.4.0-idiomaticGroovy.md:419. Public result scalars now use BigDecimal across the ANOVA,

  contingency, and timeseries result types, and the public vector/matrix outputs that were still primitive are now Groovy-facing lists in places like matrix-stats/src/main/
  groovy/se/alipsa/matrix/stats/contingency/Fisher.groovy, matrix-stats/src/main/groovy/se/alipsa/matrix/stats/timeseries/Ccm.groovy, matrix-stats/src/main/groovy/se/alipsa/
  matrix/stats/timeseries/Johansen.groovy, and matrix-stats/src/main/groovy/se/alipsa/matrix/stats/timeseries/Df.groovy. I also removed the leftover @CompileStatic usage/
  import from matrix-stats/src/main/groovy/se/alipsa/matrix/stats/formula/FormulaSupport.groovy per your correction, and fixed the redundant cast you pointed out in matrix-
  stats/src/main/groovy/se/alipsa/matrix/stats/cluster/ClusteredPoint.groovy:132.

  The affected tests were updated to use list semantics instead of array assumptions in matrix-stats/src/test/groovy/contingency/FisherTest.groovy, matrix-stats/src/test/
  groovy/timeseries/CcmTest.groovy, and matrix-stats/src/test/groovy/timeseries/JohansenTest.groovy. Verification passed with ./gradlew :matrix-stats:test --tests 'AnovaTest'
  --tests 'contingency.*' --tests 'timeseries.*' :matrix-stats:codenarcMain :matrix-stats:codenarcTest (342 tests passed). One non-blocking warning remains from the custom
  switch-style check about 4 old case X: switches, but CodeNarc itself passed.
…(>5 variables), critVal is null and the loop breaks with a clear message instead of throwing

  NumberFormatException.
  2. Identity accessors deprecated: getEigenvalueValues(), getTraceStatisticValues(), and getCriticalValueRows5pct() are marked @deprecated pointing callers to the
  properties directly. They still work for existing consumers.
…v2.4.0-idiomaticGroovy.md:459. I added a focused benchmark harness in matrix-stats/src/test/groovy/se/

  alipsa/matrix/stats/timeseries/Section45BenchmarkTest.groovy:20 that covers the initial PR-1 hotspot areas: linalg, regression, solver, and timeseries. Each benchmark warms
  up, compares the Groovy-facing path to the retained primitive path, asserts output equivalence, and logs avg/min/max timings without using flaky speed assertions.

  The local benchmark snapshot is now recorded in the roadmap. On this machine, every measured primitive path was faster, most notably Linalg.svd(Matrix),
  MultipleLinearRegression(List, List), and list-normalized fitOLS, which sets up section 4.6 cleanly without mixing the benchmark work with the policy decision.
  matrix-stats/src/main/groovy/se/alipsa/matrix/stats/regression/MultipleLinearRegression.groovy and matrix-stats/src/main/groovy/se/alipsa/matrix/stats/timeseries/
  TimeSeriesUtils.groovy now route the retained dense least-squares hotspot through a shared Java utility, matrix-stats/src/main/java/se/alipsa/matrix/stats/util/
  LeastSquaresKernel.java. That is the explicit 4.6 decision for the regression and timeseries benchmarked paths: keep idiomatic Groovy-facing APIs, move the hot primitive
  loops into Java. I also added focused coverage in matrix-stats/src/test/groovy/util/LeastSquaresKernelTest.groovy.

  The remaining benchmark decisions are now recorded explicitly in matrix-stats/req/v2.4.0-idiomaticGroovy.md: linalg deferred, BrentSolver and NelderMeadOptimizer kept as-is,
  LinearProgramSolver deferred, and section 4.6 plus 4.8.7 are marked complete with the verification command logged.
  I added explicit wrapper-vs-kernel equivalence tests in matrix-stats/src/test/groovy/regression/MultipleLinearRegressionTest.groovy and matrix-stats/src/test/groovy/se/
  alipsa/matrix/stats/timeseries/TimeSeriesUtilsTest.groovy. These now verify that the Groovy-facing regression and timeseries paths produce the same outputs as the retained
  Java utility (LeastSquaresKernel).

  I also cleaned up the remaining pure delegation in the linalg adapter layer. matrix-stats/src/main/groovy/se/alipsa/matrix/stats/linalg/LinalgAdapters.groovy no longer
  forwards Matrix/Grid toDoubleArray(...) calls, and matrix-stats/src/main/groovy/se/alipsa/matrix/stats/linalg/Linalg.groovy plus matrix-stats/src/main/groovy/se/alipsa/
  matrix/stats/linalg/SvdResult.groovy now call NumericConversion directly for those conversions. The plan doc is updated in matrix-stats/req/v2.4.0-idiomaticGroovy.md.

  Verified with:
  ./gradlew :matrix-stats:spotlessApply :matrix-stats:test --tests 'regression.MultipleLinearRegressionTest' --tests 'util.LeastSquaresKernelTest' --tests
  'se.alipsa.matrix.stats.timeseries.TimeSeriesUtilsTest' --tests 'linalg.*' :matrix-stats:codenarcMain :matrix-stats:codenarcTest
…en.groovy now keeps the legacy dynamic test(List<?>) bridge for compatibility and adds typed entry

  points testArrays(...) and testSeries(...). matrix-stats/src/main/groovy/se/alipsa/matrix/stats/distribution/FDistribution.groovy does the same with
  oneWayAnovaFValueArrays(...) / oneWayAnovaFValueFromLists(...) and matching p-value methods, and matrix-stats/src/main/groovy/se/alipsa/matrix/stats/Anova.groovy now uses
  the typed list-based ANOVA methods. I used named typed entry points instead of same-name generic overloads because Groovy/JVM erasure plus dynamic list dispatch makes the
  suggested overload shape unsafe.

  matrix-stats/src/main/java/se/alipsa/matrix/stats/util/LeastSquaresKernel.java no longer repeats the unreachable degrees-of-freedom check. matrix-stats/src/main/groovy/se/
  alipsa/matrix/stats/distribution/FDistribution.groovy now caches double df values once at construction instead of coercing BigDecimal on every CDF call. matrix-stats/src/
  main/groovy/se/alipsa/matrix/stats/timeseries/Ccm.groovy keeps the alias accessors but documents that they exist to preserve the *Values naming pattern.

  I also added compile-static API coverage in matrix-stats/src/test/groovy/timeseries/JohansenApiTypingTest.groovy and matrix-stats/src/test/groovy/distribution/
  FDistributionApiTypingTest.groovy. Issue 5 was already fixed before this pass: matrix-stats/src/main/groovy/se/alipsa/matrix/stats/distribution/ContinuousDistribution.groovy
  already declares cumulativeProbability(Number) with a deprecated primitive bridge.
…, fix FDistribution double-conversion bug, use BigDecimal extension methods in ExpressionEvaluator

- Remove List<Number>/BigDecimal mean/variance overloads from StatUtils to avoid
  duplication with core Stat class; retain only double[] methods with Kahan summation
- Fix FDistribution.oneWayAnovaPValueFromLists calling oneWayAnovaFValueFromLists
  instead of oneWayAnovaFValueInternal, which caused redundant re-normalization
- Extract shared dispatchPrimitiveOrList() into NumericConversion, used by
  FDistribution and Johansen legacy bridge methods
- Replace Math.log/sqrt/exp/pow with BigDecimal extension methods in
  ExpressionEvaluator for consistent precision
- Add null-element rejection in copyPrimitiveGroups/copyPrimitiveInputs
- Make BrentSolver.SolverResult fields private with deprecated primitive getters
- Add numerical-limitations Javadoc to LeastSquaresKernel
- Remove unnecessary @SuppressWarnings('MethodSize') from Df.test()
- Use null instead of Double.NaN in Ccm result lists for idiomatic Groovy
- Update FormulaDesignMatrixTest to use NumberExtension.E for precision

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@perNyfelt perNyfelt merged commit 9e0291b into main Apr 9, 2026
1 check passed
@perNyfelt perNyfelt deleted the stats-idiomatic-groovy-4.x branch April 9, 2026 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant