Skip to content

Refine lookup tables by bisecting boxes along one dimension - #44

Open
mirams wants to merge 6 commits into
mainfrom
bisection-refinement
Open

mirams wants to merge 6 commits into
mainfrom
bisection-refinement

Conversation

@mirams

@mirams mirams commented Sep 28, 2026

Copy link
Copy Markdown
Member

Closes #43.

Summary

LookupTableGenerator now refines by bisecting the box with the largest error estimate along one dimension. Each step adds 2^(D−1) new points instead of up to 3^D − 2^D.

  • ParameterBox::SubDivide(unsigned dimension) splits a box into two. The existing SubDivide() (split into 2^D) is kept.
  • Choosing the dimension: ParameterBox::ChooseDimensionToSplit(q) picks the dimension with the largest per-dimension error estimate. That estimate is the midpoint-prediction error measured the last time this part of the tree was split along that dimension. Dimensions never split here count as unknown (∞), so every region is split once in each dimension (about 3^D points, the same as today's first level) before it can be declared converged. Ties are broken by how much the QoI varies across the box.
  • Neighbour points: a bisection's new points may already have been evaluated by neighbouring boxes. Their prediction errors are used immediately. Otherwise, when every new point already exists, the daughter boxes would have no error estimate (undefined behaviour in Release).
  • Refinement level now comes from box geometry, so SetMaxVariationInRefinement(n) still means n full levels. Comparisons use >=, and UNSIGNED_UNSET means no limit.
  • Null guard on predictions in AssignQoIValues, and the error-finalisation code is factored out into a helper.

Compatibility

  • No archive format change. No new serialized members and no version bumps. The per-dimension errors, split directions and refinement levels are all derived from box geometry and the existing mMaxErrorsInEachQoI.
  • Old tables in new code: existing published tables and checkpoints load and interpolate exactly as before. Refining them further uses bisection, and trees that mix both kinds of split are supported.
  • New tables in old code: older ApPredict / AP-Nimbus builds can read new tables.

Tests

  • TestParameterBox: 8 new tests covering:
    • 1D bisection matching the old algorithm exactly;
    • 2D bisection geometry and per-dimension error estimates;
    • the case where every new point already exists;
    • a 3D example that must end on a 3×3×3 grid;
    • only refining the dimensions that need it;
    • exact bilinear interpolation on a tree mixing both kinds of split;
    • bisecting a tree made by the old algorithm;
    • archiving a bisected tree.
  • TestLookupTableBackwardsCompatibility (new, continuous pack): loads a ParameterBox<2> archive made by the previous code with Boost 1.74 (generator source in test/data/legacy_lookup_tables/). It checks interpolation at 441 recorded points to 1e-12, then keeps refining by bisection.
  • TestLookupTableGenerator::TestLookupTableMaker2dBisection: each step adds 1–2 points, interpolation is exact at the corners and stays within the data range, and archiving and resuming work.
  • TestConvertLookupTableArchiveToBinary::TestInterpolationOfPublishedTable: the published 4D Shannon table interpolates exactly at the corners of parameter space and stays within the range of its data.

Verification so far

  • TestParameterBox and TestLookupTableBackwardsCompatibility pass in Debug-style (asserts on) and Release-style builds, under Boost 1.90 and 1.74. They were built against Chaste global only, not a full Chaste+ApPredict build.
  • Old code reading a new table: a ParameterBox archive of a bisected tree written by this branch, loaded by main's ParameterBox (Boost 1.74), gives bit-identical interpolation at 1681 points.
  • Not yet run: the new generator and published-table tests, which need AP simulations and a full build. This PR's CI run will cover them.

Points needed to reach a tolerance of 0.01 on cheap test functions:

Function Bisection Split into 2^D
exp(2x) 111 1605
exp(x)(1+y²) 329 761

Follow-ups

Tracked in #43:

  • Batching several boxes per step, to keep threads busy (bisection gives far fewer points per step).
  • Re-measuring stale inherited error estimates.
  • More forgiving error handling in LookupTableLoader when an archive fails to load.

The second commit only updates copyright years to 2026 (and strips trailing whitespace in CMake comments).

🤖 Generated with Claude Code

mirams and others added 2 commits September 28, 2026 14:33
LookupTableGenerator now bisects the box with the largest error estimate
along the dimension with the largest estimated interpolation error,
adding 2^(D-1) points per step instead of up to 3^D - 2^D.

- ParameterBox::SubDivide(unsigned) bisects along one dimension, and uses
  already-evaluated points on the new plane (from neighbouring boxes)
  to form error estimates immediately.
- Per-dimension error estimates and refinement levels are derived from box
  geometry and the existing mMaxErrorsInEachQoI, so the archive format is
  unchanged: old tables load and interpolate identically (and are refined
  further by bisection), and older ApPredict builds can read new tables.
- SetMaxVariationInRefinement(n) still means n full levels of refinement.
- Add a legacy ParameterBox archive fixture (made with the previous code and
  Boost 1.74) and TestLookupTableBackwardsCompatibility, plus new
  ParameterBox, 2D generator and published-table interpolation tests.

Closes #43

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Also strips trailing whitespace from CMake comment lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 28, 2026 13:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The legacy compatibility test lacks its required archive fixture, and the broad lookup-table refinement changes require final human review.

Review effort: Lite
Findings: None

What changed in this PR

This PR changes adaptive lookup-table refinement from full multidimensional subdivision to dimension-selective bisection while preserving archive compatibility.

Changes:

  • Adds bisection, per-dimension error tracking, and geometry-based refinement levels.
  • Updates lookup-table generation and adds compatibility, interpolation, and archiving tests.
  • Updates copyright years and CMake comment whitespace.
File Description
test/​TestTroublesomeApEvaluations.hpp Copyright update.
test/​TestTorsadePredictLong.hpp Copyright update.
test/​TestTorsadePredict.hpp Copyright update.
test/​TestPkpdReader.hpp Copyright update.
test/​TestPkpdInterpolations.hpp Copyright update.
test/​TestParameterBox.hpp Adds bisection and mixed-tree tests.
test/​TestModelFactory.hpp Copyright update.
test/​TestMetadataCellmlModels.hpp Copyright update.
test/​TestMakeALookupTable.hpp Copyright update.
test/​TestLookupTableGenerator.hpp Adds generator bisection tests.
test/​TestLookupTableBackwardsCompatibility.hpp Adds legacy archive compatibility testing.
test/​TestLogisticDistribution.hpp Copyright update.
test/​TestLinearDiscriminantAnalysis.hpp Copyright update.
test/​TestDoseResponseFitting.hpp Copyright update.
test/​TestDoseCalculator.hpp Copyright update.
test/​TestDavies2012Paper.hpp Copyright update.
test/​TestDataReaders.hpp Copyright update.
test/​TestConvertLookupTableArchiveToBinary.hpp Adds published-table interpolation coverage.
test/​TestCipaQNetCalculator.hpp Copyright update.
test/​TestBayesianInferer.hpp Copyright update.
test/​TestApPredictLong.hpp Copyright update.
test/​TestApPredict.hpp Copyright update.
test/​TestActionPotentialDownsampler.hpp Copyright update.
test/​data/​legacy_lookup_tables/​ParameterBox2d_legacy_interpolated.dat Stores legacy interpolation reference data.
test/​data/​legacy_lookup_tables/​GenerateLegacyParameterBoxFixture.cpp Documents legacy fixture generation.
test/​ContinuousTestPack.txt Registers the compatibility test.
test/​CMakeLists.txt Copyright and whitespace updates.
src/​stats/​LogLogisticDistribution.hpp Copyright update.
src/​stats/​LogisticDistribution.hpp Copyright update.
src/​stats/​LinearDiscriminantAnalysis.hpp Copyright update.
src/​stats/​LinearDiscriminantAnalysis.cpp Copyright update.
src/​stats/​DoseResponseParameterTypes.hpp Copyright update.
src/​stats/​BayesianInferer.hpp Copyright update.
src/​stats/​BayesianInferer.cpp Copyright update.
src/​stats/​AbstractDistribution.hpp Copyright update.
src/​single_cell/​TorsadePredictMethods.hpp Copyright update.
src/​single_cell/​TorsadePredictMethods.cpp Copyright update.
src/​single_cell/​SingleActionPotentialPrediction.hpp Copyright update.
src/​single_cell/​CipaQNetCalculator.hpp Copyright update.
src/​single_cell/​CipaQNetCalculator.cpp Copyright update.
src/​single_cell/​ApPredictMethods.hpp Copyright update.
src/​single_cell/​ApPredictMethods.cpp Copyright update.
src/​single_cell/​AbstractActionPotentialMethod.hpp Copyright update.
src/​single_cell/​AbstractActionPotentialMethod.cpp Copyright update.
src/​lookup/​QuantityOfInterest.hpp Copyright update.
src/​lookup/​ParameterPointData.hpp Copyright update.
src/​lookup/​ParameterBox.hpp Declares bisection and refinement APIs.
src/​lookup/​ParameterBox.cpp Implements bisection, error tracking, and geometry-based levels.
src/​lookup/​LookupTableLoader.hpp Copyright update.
src/​lookup/​LookupTableLoader.cpp Copyright update.
src/​lookup/​LookupTableGenerator.hpp Documents the new refinement behavior.
src/​lookup/​LookupTableGenerator.cpp Uses dimension-selective bisection.
src/​lookup/​AbstractUntemplatedLookupTableGenerator.hpp Copyright update.
src/​fortests/​SetupModel.hpp Copyright update.
src/​fortests/​SetupModel.cpp Copyright update.
src/​fortests/​ModelFactory.hpp Copyright update.
src/​fortests/​ModelFactory.cpp Copyright update.
src/​fortests/​DoseCalculator.hpp Copyright update.
src/​fortests/​DoseCalculator.cpp Copyright update.
src/​fortests/​ActionPotentialDownsampler.hpp Copyright update.
src/​fortests/​ActionPotentialDownsampler.cpp Copyright update.
src/​dose_response_fitter/​RunHillFunctionMinimization.hpp Copyright update.
src/​dose_response_fitter/​RunHillFunctionMinimization.cpp Copyright update.
src/​dose_response_fitter/​NelderMeadMinimizer.hpp Copyright update.
src/​dose_response_fitter/​NelderMeadMinimizer.cpp Copyright update.
src/​dose_response_fitter/​HillFunction.hpp Copyright update.
src/​dose_response_fitter/​HillFunction.cpp Copyright update.
src/​data_reading/​PkpdDataStructure.hpp Copyright update.
src/​data_reading/​DaviesDogDataStructure.hpp Copyright update.
src/​data_reading/​DaviesDogDataStructure.cpp Copyright update.
src/​data_reading/​CardiovascRes2011DataStructure.hpp Copyright update.
src/​data_reading/​AbstractDrugDataStructure.hpp Copyright update.
src/​data_reading/​AbstractDrugDataStructure.cpp Copyright update.
src/​data_reading/​AbstractDataStructure.hpp Copyright update.
src/​data_reading/​AbstractDataStructure.cpp Copyright update.
CMakeLists.txt Copyright and whitespace updates.
apps/​src/​TorsadePredict.cpp Copyright update.
apps/​src/​DoseResponseFitter.cpp Copyright update.
apps/​src/​ApPredict.cpp Copyright update.
apps/​CMakeLists.txt Copyright and whitespace updates.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

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.

LookupTableGenerator: refine by bisecting along one dimension instead of splitting into 2^D boxes

2 participants