BayesianEstimationTool 1.0.1__tar.gz → 1.0.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/BayesianEstimationTool.egg-info/PKG-INFO +25 -13
  2. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/BayesianEstimationTool.egg-info/SOURCES.txt +18 -0
  3. bayesianestimationtool-1.0.2/ExampleInstaller.py +41 -0
  4. bayesianestimationtool-1.0.2/Examples/1_SimpleExampleNuts.py +149 -0
  5. bayesianestimationtool-1.0.2/Examples/2_SimpleExampleSvi.py +149 -0
  6. bayesianestimationtool-1.0.2/Examples/3_SaveExampleNuts.py +151 -0
  7. bayesianestimationtool-1.0.2/Examples/4_LoadExampleNuts.py +174 -0
  8. bayesianestimationtool-1.0.2/Examples/5_SaveExampleSvi.py +151 -0
  9. bayesianestimationtool-1.0.2/Examples/6_LoadExampleSvi.py +174 -0
  10. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/PKG-INFO +25 -13
  11. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/README.md +24 -12
  12. bayesianestimationtool-1.0.2/docs/Cheatsheet.pdf +86 -0
  13. bayesianestimationtool-1.0.2/docs/Manual.pdf +0 -0
  14. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/pyproject.toml +4 -1
  15. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/BayesianEstimationTool.egg-info/dependency_links.txt +0 -0
  16. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/BayesianEstimationTool.egg-info/requires.txt +0 -0
  17. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/BayesianEstimationTool.egg-info/top_level.txt +0 -0
  18. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/BayesianEstimationTool.py +0 -0
  19. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/BayesianEstimationTool_CheatSheet.py +0 -0
  20. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/LICENSE +0 -0
  21. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/Method.py +0 -0
  22. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/MethodValidation.py +0 -0
  23. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/NutsMethod.py +0 -0
  24. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/SviMethod.py +0 -0
  25. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/__init__.py +0 -0
  26. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/setup.cfg +0 -0
  27. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/tests/test_custom_method.py +0 -0
  28. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/tests/test_estimator_integration.py +0 -0
  29. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/tests/test_estimator_validation.py +0 -0
  30. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/tests/test_longitudinal_transfer.py +0 -0
  31. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/tests/test_method_contract.py +0 -0
  32. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/tests/test_method_validation.py +0 -0
  33. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/tests/test_nuts_numerics.py +0 -0
  34. {bayesianestimationtool-1.0.1 → bayesianestimationtool-1.0.2}/tests/test_scientific_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BayesianEstimationTool
3
- Version: 1.0.1
3
+ Version: 1.0.2
4
4
  Summary: Hierarchical Bayesian estimation and prediction for mechanistic models
5
5
  License-Expression: Apache-2.0
6
6
  Project-URL: Repository, https://github.com/rafaelrbravo/BayesianEstimationTool
@@ -15,6 +15,8 @@ Dynamic: license-file
15
15
 
16
16
  # BayesianEstimationTool (BET)
17
17
 
18
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.23431103.svg)](https://doi.org/10.5281/zenodo.23431103)
19
+
18
20
  BayesianEstimationTool fits population-level parameters and individual-specific latent variables with NumPyro inference methods.
19
21
 
20
22
  ## Documentation
@@ -37,25 +39,19 @@ The GitHub Actions test workflow regenerates the PDFs (and the Python-authored c
37
39
 
38
40
  ## Installation
39
41
 
40
- BET requires Python 3.10 or newer. Install the latest version from [PyPI](https://pypi.org/project/BayesianEstimationTool/):
42
+ BET requires Python 3.10 or newer. Install from [PyPI](https://pypi.org/project/BayesianEstimationTool/):
41
43
 
42
44
  ```bash
43
45
  python -m pip install BayesianEstimationTool
44
46
  ```
45
47
 
46
- To install the specific first release:
47
-
48
- ```bash
49
- python -m pip install BayesianEstimationTool==1.0.1
50
- ```
51
-
52
- Alternatively, install directly from the tagged GitHub source:
48
+ Alternatively, install the latest source directly from GitHub:
53
49
 
54
50
  ```bash
55
- python -m pip install "git+https://github.com/rafaelrbravo/BayesianEstimationTool.git@v1.0.1"
51
+ python -m pip install "git+https://github.com/rafaelrbravo/BayesianEstimationTool.git"
56
52
  ```
57
53
 
58
- You can also download a wheel (`.whl`) or source archive (`.tar.gz`) from [GitHub Releases](https://github.com/rafaelrbravo/BayesianEstimationTool/releases) and install it with `python -m pip install PATH_TO_DOWNLOADED_FILE`. These release assets become available after publishing.
54
+ You can also download a wheel (`.whl`) or source archive (`.tar.gz`) from [GitHub Releases](https://github.com/rafaelrbravo/BayesianEstimationTool/releases) and install it with `python -m pip install PATH_TO_DOWNLOADED_FILE`.
59
55
 
60
56
  Verify installation:
61
57
 
@@ -67,10 +63,26 @@ For editable development from a source checkout, run `python -m pip install -e .
67
63
 
68
64
  See the manual for the minimal model and training examples.
69
65
 
66
+ ## Bundled examples and PDF documentation
67
+
68
+ After installing BET, copy its bundled runnable examples and PDF manual/cheatsheet to an editable folder in the current directory (no GitHub download needed):
69
+
70
+ ```bash
71
+ python -m BayesianEstimationTool.ExampleInstaller
72
+ ```
73
+
74
+ To select a different parent directory:
75
+
76
+ ```bash
77
+ python -m BayesianEstimationTool.ExampleInstaller ~/Documents
78
+ ```
79
+
80
+ This creates `BET-Examples/` with the six Python examples and a `Documentation/` subfolder containing `Manual.pdf` and `Cheatsheet.pdf`. Run a script with `python BET-Examples/1_SimpleExampleNuts.py`. The installer refuses to overwrite an existing `BET-Examples/` directory. The installer is available in package builds that include bundled examples and documentation.
81
+
70
82
  ## How to cite
71
83
 
72
84
  If you use BET in your research, please cite:
73
85
 
74
- Bravo, R., Tercan, B., Lima, E., Aguilar, B., & Yankeelov, T. (2026). *BayesianEstimationTool* (Version 1.0.1) [Computer software]. https://github.com/rafaelrbravo/BayesianEstimationTool
86
+ Bravo, R., Tercan, B., Lima, E., Aguilar, B., & Yankeelov, T. (2026). *BayesianEstimationTool* (Version 1.0.2) [Computer software]. https://doi.org/10.5281/zenodo.23431103
75
87
 
76
- GitHub also provides a **Cite this repository** option using [CITATION.cff](CITATION.cff).
88
+ This DOI identifies all versions of BET. GitHub also provides a **Cite this repository** option using [CITATION.cff](CITATION.cff).
@@ -1,5 +1,6 @@
1
1
  BayesianEstimationTool.py
2
2
  BayesianEstimationTool_CheatSheet.py
3
+ ExampleInstaller.py
3
4
  LICENSE
4
5
  Method.py
5
6
  MethodValidation.py
@@ -10,16 +11,33 @@ __init__.py
10
11
  pyproject.toml
11
12
  ./BayesianEstimationTool.py
12
13
  ./BayesianEstimationTool_CheatSheet.py
14
+ ./ExampleInstaller.py
13
15
  ./Method.py
14
16
  ./MethodValidation.py
15
17
  ./NutsMethod.py
16
18
  ./SviMethod.py
17
19
  ./__init__.py
20
+ ./Examples/1_SimpleExampleNuts.py
21
+ ./Examples/2_SimpleExampleSvi.py
22
+ ./Examples/3_SaveExampleNuts.py
23
+ ./Examples/4_LoadExampleNuts.py
24
+ ./Examples/5_SaveExampleSvi.py
25
+ ./Examples/6_LoadExampleSvi.py
26
+ ./docs/Cheatsheet.pdf
27
+ ./docs/Manual.pdf
18
28
  BayesianEstimationTool.egg-info/PKG-INFO
19
29
  BayesianEstimationTool.egg-info/SOURCES.txt
20
30
  BayesianEstimationTool.egg-info/dependency_links.txt
21
31
  BayesianEstimationTool.egg-info/requires.txt
22
32
  BayesianEstimationTool.egg-info/top_level.txt
33
+ Examples/1_SimpleExampleNuts.py
34
+ Examples/2_SimpleExampleSvi.py
35
+ Examples/3_SaveExampleNuts.py
36
+ Examples/4_LoadExampleNuts.py
37
+ Examples/5_SaveExampleSvi.py
38
+ Examples/6_LoadExampleSvi.py
39
+ docs/Cheatsheet.pdf
40
+ docs/Manual.pdf
23
41
  tests/test_custom_method.py
24
42
  tests/test_estimator_integration.py
25
43
  tests/test_estimator_validation.py
@@ -0,0 +1,41 @@
1
+ """Copy bundled BET examples and PDF documentation into an editable folder."""
2
+ import argparse
3
+ from pathlib import Path
4
+ import shutil
5
+ import sys
6
+
7
+
8
+ def main():
9
+ parser = argparse.ArgumentParser(description="Copy bundled BET examples and documentation.")
10
+ parser.add_argument("path", nargs="?", default=".", help="Parent directory for BET-Examples (default: current directory)")
11
+ args = parser.parse_args()
12
+ package = Path(__file__).resolve().parent
13
+ source = package / "Examples"
14
+ docs = package / "docs"
15
+ parent = Path(args.path).expanduser().resolve()
16
+ target = parent / "BET-Examples"
17
+ if not parent.is_dir():
18
+ print(f"Parent directory does not exist: {parent}", file=sys.stderr)
19
+ return 1
20
+ if not source.is_dir() or not list(source.glob("*.py")):
21
+ print("BET examples are missing from this installation.", file=sys.stderr)
22
+ return 1
23
+ pdfs = sorted(docs.glob("*.pdf")) if docs.is_dir() else []
24
+ if not pdfs:
25
+ print("BET documentation PDFs are missing from this installation.", file=sys.stderr)
26
+ return 1
27
+ if target.exists():
28
+ print(f"Destination already exists: {target}\\nNothing was overwritten.", file=sys.stderr)
29
+ return 1
30
+ shutil.copytree(source, target)
31
+ docs_target = target / "Documentation"
32
+ docs_target.mkdir()
33
+ for pdf in pdfs:
34
+ shutil.copy2(pdf, docs_target / pdf.name)
35
+ print(f"Copied BET examples and documentation PDFs to {target}")
36
+ print("Run an example with: python BET-Examples/1_SimpleExampleNuts.py")
37
+ return 0
38
+
39
+
40
+ if __name__ == "__main__":
41
+ sys.exit(main())
@@ -0,0 +1,149 @@
1
+ import numpyro as npo
2
+ npo.set_host_device_count(4)
3
+
4
+ import jax.numpy as jnp
5
+ import numpyro.distributions as dist
6
+ from jax import random
7
+
8
+ import BayesianEstimationTool as bet
9
+
10
+
11
+ # ---------------------------------------------------------------------
12
+ # Generate 10 synthetic patient trajectories
13
+ # ---------------------------------------------------------------------
14
+
15
+ keyG,keyNoise=random.split(random.PRNGKey(2),2)
16
+
17
+ nPatients=10
18
+ nTimes=8
19
+ times=jnp.linspace(0,10,nTimes)
20
+
21
+ # True population:
22
+ # mean growth rate = 0.15
23
+ # std growth rate = 0.03
24
+
25
+ trueG=0.15+0.03*random.normal(keyG,(nPatients,))
26
+
27
+ # Give patients slightly different initial tumor sizes.
28
+
29
+ v0=jnp.linspace(0.8,1.2,nPatients)
30
+ trueTumorSize=v0[:,None]*jnp.exp(trueG[:,None]*times)
31
+ tumorSize=trueTumorSize+0.05*random.normal(keyNoise,trueTumorSize.shape)
32
+
33
+ data={
34
+ "times":jnp.tile(times,(nPatients,1)),
35
+ "v0":v0,
36
+ "tumorSize":tumorSize,
37
+ }
38
+
39
+
40
+ # ---------------------------------------------------------------------
41
+ # Model components
42
+ # ---------------------------------------------------------------------
43
+
44
+ # Analytical solution of dV/dt = gV.
45
+
46
+ # localZs["g"] is the standardized patient-specific deviation.
47
+ # Convert it to the physical growth rate using the population mean/std.
48
+
49
+ def ModelFn(globalParams,localZs,data):
50
+ g=globalParams["gMean"]+globalParams["gStd"]*localZs["g"]
51
+ return {"tumorSize":data["v0"]*jnp.exp(g*data["times"])}
52
+
53
+
54
+ # Population parameters and observation noise.
55
+
56
+ def GlobalPriorFn(dataShapes):
57
+ return {
58
+ "gMean":dist.Normal(0.15,0.1),
59
+ "gStd":dist.HalfNormal(0.1),
60
+ "sigma":dist.HalfNormal(0.1),
61
+ }
62
+
63
+
64
+ # Training likelihood for one patient; BET vectorizes it across patients.
65
+
66
+ def TrainLikelihoodFn(globalParams,localZs,data,modelOut):
67
+ return {"tumorSize":(data["tumorSize"],dist.Normal(modelOut["tumorSize"],globalParams["sigma"]))}
68
+
69
+
70
+ # Test likelihood for one patient; only the first nFit observations are used.
71
+
72
+ def TestLikelihoodFn(globalParams,localZs,data,modelOut):
73
+ nFit=3
74
+ tumorSize=modelOut["tumorSize"]
75
+ mask=jnp.arange(tumorSize.shape[-1])<nFit
76
+ return {"tumorSize":(data["tumorSize"],dist.Normal(tumorSize,globalParams["sigma"]).mask(mask))}
77
+
78
+
79
+ # Score only the last five observations; the first three may be used for fitting.
80
+
81
+ def ScoreFn(modelOut,data):
82
+ return jnp.mean(jnp.abs(modelOut["tumorSize"][3:]-data["tumorSize"][3:]))
83
+
84
+
85
+ # ---------------------------------------------------------------------
86
+ # Construct Estimator
87
+ # ---------------------------------------------------------------------
88
+
89
+ est=bet.Estimator(data=data,ModelFn=ModelFn,localZNames=["g"],GlobalPriorFn=GlobalPriorFn,rngSeed=0)
90
+
91
+ # Patients 0-4 train the population model.
92
+ # Patients 5-9 are completely held out.
93
+
94
+
95
+ # ---------------------------------------------------------------------
96
+ # 1. TRAINING
97
+ #
98
+ # Infer:
99
+ # - population mean of g
100
+ # - population std of g
101
+ # - g for each training patient
102
+ # - observation noise sigma
103
+ #
104
+ # using all observations from the five training patients.
105
+ # ---------------------------------------------------------------------
106
+
107
+ est.Train( trainDataIdxs=jnp.arange(5), TrainLikelihoodFn=TrainLikelihoodFn, Method=bet.Nuts(numWarmup=500,numSamples=500,numChains=4), printSummary=True)
108
+ trainScores=est.ScoreTrainSamples(ScoreFn=ScoreFn)
109
+
110
+
111
+ # ---------------------------------------------------------------------
112
+ # 2. TESTING WITHOUT INFERENCE
113
+ #
114
+ # Do not use any test-patient observations for inference.
115
+ #
116
+ # Use the median global posterior state to keep this introductory run fast.
117
+ # For each test patient, sample local Zs from the learned
118
+ # population distribution and generate trajectories.
119
+ #
120
+ # numSamples controls the number of local Z samples generated.
121
+ # ---------------------------------------------------------------------
122
+
123
+ est.Test( testDataIdxs=jnp.arange(5,10), TestLikelihoodFn=None, nGlobalSamples=None, nLocalZSamples=2000)
124
+ noInferenceScores=est.ScoreTestSamples(ScoreFn=ScoreFn)
125
+
126
+
127
+ # ---------------------------------------------------------------------
128
+ # 3. TESTING WITH NUTS
129
+ #
130
+ # Use the median global parameter values from the training posterior
131
+ # and infer each test patient's local Z using early observations.
132
+ #
133
+ # TestLikelihoodFn exposes only the first nFit=3 tumor measurements,
134
+ # so each test patient's g is updated using its early trajectory.
135
+ #
136
+ # ScoreFn evaluates only the final five measurements, held out from inference.
137
+ # ---------------------------------------------------------------------
138
+
139
+ est.Test( testDataIdxs=jnp.arange(5,10), TestLikelihoodFn=TestLikelihoodFn, Method=bet.Nuts, nGlobalSamples=None, printSummary=True)
140
+ testNutsScores=est.ScoreTestSamples(ScoreFn=ScoreFn)
141
+
142
+
143
+ # ---------------------------------------------------------------------
144
+ # Results
145
+ # ---------------------------------------------------------------------
146
+
147
+ print("Training score: ",jnp.mean(trainScores))
148
+ print("Test score, no inference: ",jnp.mean(noInferenceScores))
149
+ print("Test score, NUTS: ",jnp.mean(testNutsScores))
@@ -0,0 +1,149 @@
1
+ import numpyro as npo
2
+ npo.set_host_device_count(4)
3
+
4
+ import jax.numpy as jnp
5
+ import numpyro.distributions as dist
6
+ from jax import random
7
+
8
+ import BayesianEstimationTool as bet
9
+
10
+
11
+ # ---------------------------------------------------------------------
12
+ # Generate 10 synthetic patient trajectories
13
+ # ---------------------------------------------------------------------
14
+
15
+ keyG,keyNoise=random.split(random.PRNGKey(2),2)
16
+
17
+ nPatients=10
18
+ nTimes=8
19
+ times=jnp.linspace(0,10,nTimes)
20
+
21
+ # True population:
22
+ # mean growth rate = 0.15
23
+ # std growth rate = 0.03
24
+
25
+ trueG=0.15+0.03*random.normal(keyG,(nPatients,))
26
+
27
+ # Give patients slightly different initial tumor sizes.
28
+
29
+ v0=jnp.linspace(0.8,1.2,nPatients)
30
+ trueTumorSize=v0[:,None]*jnp.exp(trueG[:,None]*times)
31
+ tumorSize=trueTumorSize+0.05*random.normal(keyNoise,trueTumorSize.shape)
32
+
33
+ data={
34
+ "times":jnp.tile(times,(nPatients,1)),
35
+ "v0":v0,
36
+ "tumorSize":tumorSize,
37
+ }
38
+
39
+
40
+ # ---------------------------------------------------------------------
41
+ # Model components
42
+ # ---------------------------------------------------------------------
43
+
44
+ # Analytical solution of dV/dt = gV.
45
+
46
+ # localZs["g"] is the standardized patient-specific deviation.
47
+ # Convert it to the physical growth rate using the population mean/std.
48
+
49
+ def ModelFn(globalParams,localZs,data):
50
+ g=globalParams["gMean"]+globalParams["gStd"]*localZs["g"]
51
+ return {"tumorSize":data["v0"]*jnp.exp(g*data["times"])}
52
+
53
+
54
+ # Population parameters and observation noise.
55
+
56
+ def GlobalPriorFn(dataShapes):
57
+ return {
58
+ "gMean":dist.Normal(0.15,0.1),
59
+ "gStd":dist.HalfNormal(0.1),
60
+ "sigma":dist.HalfNormal(0.1),
61
+ }
62
+
63
+
64
+ # Training likelihood for one patient; BET vectorizes it across patients.
65
+
66
+ def TrainLikelihoodFn(globalParams,localZs,data,modelOut):
67
+ return {"tumorSize":(data["tumorSize"],dist.Normal(modelOut["tumorSize"],globalParams["sigma"]))}
68
+
69
+
70
+ # Test likelihood for one patient; only the first nFit observations are used.
71
+
72
+ def TestLikelihoodFn(globalParams,localZs,data,modelOut):
73
+ nFit=3
74
+ tumorSize=modelOut["tumorSize"]
75
+ mask=jnp.arange(tumorSize.shape[-1])<nFit
76
+ return {"tumorSize":(data["tumorSize"],dist.Normal(tumorSize,globalParams["sigma"]).mask(mask))}
77
+
78
+
79
+ # Score only the last five observations; the first three may be used for fitting.
80
+
81
+ def ScoreFn(modelOut,data):
82
+ return jnp.mean(jnp.abs(modelOut["tumorSize"][3:]-data["tumorSize"][3:]))
83
+
84
+
85
+ # ---------------------------------------------------------------------
86
+ # Construct Estimator
87
+ # ---------------------------------------------------------------------
88
+
89
+ est=bet.Estimator(data=data,ModelFn=ModelFn,localZNames=["g"],GlobalPriorFn=GlobalPriorFn,rngSeed=0)
90
+
91
+ # Patients 0-4 train the population model.
92
+ # Patients 5-9 are completely held out.
93
+
94
+
95
+ # ---------------------------------------------------------------------
96
+ # 1. TRAINING
97
+ #
98
+ # Infer:
99
+ # - population mean of g
100
+ # - population std of g
101
+ # - g for each training patient
102
+ # - observation noise sigma
103
+ #
104
+ # using all observations from the five training patients.
105
+ # ---------------------------------------------------------------------
106
+
107
+ est.Train( trainDataIdxs=jnp.arange(5), TrainLikelihoodFn=TrainLikelihoodFn, Method=bet.Svi(numSteps=10000,numSamples=2000,stepSize=0.01), printSummary=True)
108
+ trainScores=est.ScoreTrainSamples(ScoreFn=ScoreFn)
109
+
110
+
111
+ # ---------------------------------------------------------------------
112
+ # 2. TESTING WITHOUT INFERENCE
113
+ #
114
+ # Do not use any test-patient observations for inference.
115
+ #
116
+ # Use the median global posterior state to keep this introductory run fast.
117
+ # For each test patient, sample local Zs from the learned
118
+ # population distribution and generate trajectories.
119
+ #
120
+ # numSamples controls the number of local Z samples generated.
121
+ # ---------------------------------------------------------------------
122
+
123
+ est.Test( testDataIdxs=jnp.arange(5,10), TestLikelihoodFn=None, nGlobalSamples=None, nLocalZSamples=2000)
124
+ noInferenceScores=est.ScoreTestSamples(ScoreFn=ScoreFn)
125
+
126
+
127
+ # ---------------------------------------------------------------------
128
+ # 3. TESTING WITH SVI
129
+ #
130
+ # Use the median global parameter values from the training posterior
131
+ # and infer each test patient's local Z using early observations.
132
+ #
133
+ # TestLikelihoodFn exposes only the first nFit=3 tumor measurements,
134
+ # so each test patient's g is updated using its early trajectory.
135
+ #
136
+ # ScoreFn evaluates only the final five measurements, held out from inference.
137
+ # ---------------------------------------------------------------------
138
+
139
+ est.Test( testDataIdxs=jnp.arange(5,10), TestLikelihoodFn=TestLikelihoodFn, Method=bet.Svi, nGlobalSamples=None, printSummary=True)
140
+ testSviScores=est.ScoreTestSamples(ScoreFn=ScoreFn)
141
+
142
+
143
+ # ---------------------------------------------------------------------
144
+ # Results
145
+ # ---------------------------------------------------------------------
146
+
147
+ print("Training score: ",jnp.mean(trainScores))
148
+ print("Test score, no inference: ",jnp.mean(noInferenceScores))
149
+ print("Test score, SVI: ",jnp.mean(testSviScores))
@@ -0,0 +1,151 @@
1
+ import numpyro as npo
2
+ npo.set_host_device_count(4)
3
+
4
+ import jax.numpy as jnp
5
+ import numpyro.distributions as dist
6
+ from jax import random
7
+
8
+ import BayesianEstimationTool as bet
9
+
10
+
11
+ # ---------------------------------------------------------------------
12
+ # Generate 10 synthetic patient trajectories
13
+ # ---------------------------------------------------------------------
14
+
15
+ keyG,keyD,keySize,keyNoise=random.split(random.PRNGKey(2),4)
16
+
17
+ nPatients=10
18
+ nTimes=11
19
+ times=jnp.linspace(0,10,nTimes)
20
+ drugStart=4.0
21
+
22
+ # Generate correlated patient-specific growth and drug-response effects.
23
+
24
+ rho=0.8
25
+ zG=random.normal(keyG,(nPatients,))
26
+ zDIndependent=random.normal(keyD,(nPatients,))
27
+ zD=rho*zG+jnp.sqrt(1.0-rho**2)*zDIndependent
28
+
29
+ # True population:
30
+ # mean growth rate = 0.15
31
+ # std growth rate = 0.03
32
+ # mean drug effect = 0.25
33
+ # std drug effect = 0.05
34
+
35
+ trueG=0.15+0.03*zG
36
+ trueD=0.25+0.05*zD
37
+
38
+ # Give patients different initial tumor sizes.
39
+
40
+ v0=random.uniform(keySize,(nPatients,),minval=0.8,maxval=1.2)
41
+
42
+ timeOnDrug=jnp.maximum(times-drugStart,0.0)
43
+ trueTumorSize=v0[:,None]*jnp.exp(trueG[:,None]*times-trueD[:,None]*timeOnDrug)
44
+ tumorSize=trueTumorSize+0.05*random.normal(keyNoise,trueTumorSize.shape)
45
+
46
+ data={
47
+ "times":jnp.tile(times,(nPatients,1)),
48
+ "drugStart":jnp.full(nPatients,drugStart),
49
+ "v0":v0,
50
+ "tumorSize":tumorSize,
51
+ }
52
+
53
+
54
+ # ---------------------------------------------------------------------
55
+ # Model components
56
+ # ---------------------------------------------------------------------
57
+
58
+ # Analytical solution: dV/dt = gV before treatment and (g-d)V after.
59
+
60
+ # localZs["g"] and localZs["d"] are standardized
61
+ # patient-specific deviations.
62
+ #
63
+ # Convert them to physical parameter values using the inferred
64
+ # population means and standard deviations.
65
+
66
+ def ModelFn(globalParams,localZs,data):
67
+ g=globalParams["gMean"]+globalParams["gStd"]*localZs["g"]
68
+ d=globalParams["dMean"]+globalParams["dStd"]*localZs["d"]
69
+ tumorSize=data["v0"]*jnp.exp(g*data["times"]-d*jnp.maximum(data["times"]-data["drugStart"],0.0))
70
+ return {"tumorSize":tumorSize}
71
+
72
+
73
+ # Population parameters and observation noise.
74
+
75
+ def GlobalPriorFn(dataShapes):
76
+ return {
77
+ "gMean":dist.Normal(0.15,0.1),
78
+ "gStd":dist.HalfNormal(0.1),
79
+ "dMean":dist.Normal(0.25,0.1),
80
+ "dStd":dist.HalfNormal(0.1),
81
+ "sigma":dist.HalfNormal(0.1),
82
+ }
83
+
84
+
85
+ # Training likelihood for one patient; BET vectorizes it across patients.
86
+
87
+ def TrainLikelihoodFn(globalParams,localZs,data,modelOut):
88
+ return {"tumorSize":(data["tumorSize"],dist.Normal(modelOut["tumorSize"],globalParams["sigma"]))}
89
+
90
+
91
+ # Score one trajectory against all available observations.
92
+
93
+ def ScoreFn(modelOut,data):
94
+ return jnp.mean(jnp.abs(modelOut["tumorSize"]-data["tumorSize"]))
95
+
96
+
97
+ # ---------------------------------------------------------------------
98
+ # Construct Estimator
99
+ # ---------------------------------------------------------------------
100
+
101
+ est=bet.Estimator(data=data,ModelFn=ModelFn,localZNames=["g","d"],GlobalPriorFn=GlobalPriorFn,rngSeed=0)
102
+
103
+
104
+ # ---------------------------------------------------------------------
105
+ # 1. TRAINING
106
+ #
107
+ # Patients 0-4 train the population model.
108
+ #
109
+ # Infer:
110
+ # - population mean growth rate
111
+ # - population std growth rate
112
+ # - population mean drug effect
113
+ # - population std drug effect
114
+ # - correlation between patient-specific g and d
115
+ # - patient-specific g and d values
116
+ # - observation noise sigma
117
+ #
118
+ # using all observations from the five training patients.
119
+ # ---------------------------------------------------------------------
120
+
121
+ est.Train(trainDataIdxs=jnp.arange(5),TrainLikelihoodFn=TrainLikelihoodFn,Method=bet.Nuts(numWarmup=500,numSamples=500,numChains=4),printSummary=True)
122
+
123
+
124
+ # ---------------------------------------------------------------------
125
+ # 2. SCORE TRAINING POSTERIOR
126
+ # ---------------------------------------------------------------------
127
+
128
+ trainScores=est.ScoreTrainSamples(ScoreFn=ScoreFn)
129
+
130
+ print("Training score before save:",jnp.mean(trainScores))
131
+
132
+
133
+ # ---------------------------------------------------------------------
134
+ # 3. SAVE
135
+ #
136
+ # The Estimator is saved as a NumPy .npz archive.
137
+ #
138
+ # By default, Save stores:
139
+ # - Estimator metadata
140
+ # - training/test run settings, including method name and constructor arguments
141
+ # - training/test indices
142
+ # - RNG state
143
+ # - training global posterior samples
144
+ # - training Cholesky samples
145
+ # - training local posterior samples
146
+ # - test local Zs/globals/cholesky, if testing has been run
147
+ #
148
+ # Model outputs and the full input dataset are not saved by default.
149
+ # ---------------------------------------------------------------------
150
+
151
+ est.Save("SimpleTumorGrowthNuts.npz")