BayesianEstimationTool 1.0.0__py3-none-any.whl
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.
- BayesianEstimationTool/BayesianEstimationTool.py +577 -0
- BayesianEstimationTool/BayesianEstimationTool_CheatSheet.py +245 -0
- BayesianEstimationTool/Method.py +10 -0
- BayesianEstimationTool/MethodValidation.py +61 -0
- BayesianEstimationTool/NutsMethod.py +57 -0
- BayesianEstimationTool/SviMethod.py +53 -0
- BayesianEstimationTool/__init__.py +6 -0
- bayesianestimationtool-1.0.0.dist-info/METADATA +68 -0
- bayesianestimationtool-1.0.0.dist-info/RECORD +12 -0
- bayesianestimationtool-1.0.0.dist-info/WHEEL +5 -0
- bayesianestimationtool-1.0.0.dist-info/licenses/LICENSE +201 -0
- bayesianestimationtool-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
import numpyro as npo
|
|
2
|
+
import numpyro.distributions as dist
|
|
3
|
+
from jax import random, vmap
|
|
4
|
+
import jax.numpy as jnp
|
|
5
|
+
from pprint import pprint
|
|
6
|
+
import jax
|
|
7
|
+
import gc
|
|
8
|
+
import functools
|
|
9
|
+
import numpy as np
|
|
10
|
+
import inspect
|
|
11
|
+
import os
|
|
12
|
+
import warnings
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
class Estimator():
|
|
16
|
+
_samplePrefixes=("__globalParam__","__localZ__","__cholesky__")
|
|
17
|
+
_modelOutputPrefix="__modelOutput__"
|
|
18
|
+
|
|
19
|
+
def __init__(self,data,ModelFn,localZNames=None,GlobalPriorFn=None,lkjConcentration=2.0,rngSeed=None):
|
|
20
|
+
self._ValidateInit(data,ModelFn,localZNames,GlobalPriorFn,lkjConcentration,rngSeed)
|
|
21
|
+
self._data=data
|
|
22
|
+
self._localZNames=[] if localZNames is None else localZNames
|
|
23
|
+
self._ModelFn=ModelFn
|
|
24
|
+
self._GlobalPriorFn=GlobalPriorFn
|
|
25
|
+
self._lkjConcentration=lkjConcentration
|
|
26
|
+
self._modelFnName=self._GetFnName(self._ModelFn)
|
|
27
|
+
self._globalPriorFnName=self._GetFnName(self._GlobalPriorFn)
|
|
28
|
+
self._globalNames=None
|
|
29
|
+
self._key=None if rngSeed is None else random.PRNGKey(rngSeed)
|
|
30
|
+
self._ClearTrainState()
|
|
31
|
+
self._ClearTestState()
|
|
32
|
+
|
|
33
|
+
def Train(self,trainDataIdxs,TrainLikelihoodFn,Method,printSummary=False,rngSeed=None,saveLocalZs=True,saveModelOut=False):
|
|
34
|
+
Method=self._ResolveMethod(Method,test=False)
|
|
35
|
+
self._ValidateTrain(trainDataIdxs,TrainLikelihoodFn,Method,printSummary,rngSeed,saveLocalZs,saveModelOut)
|
|
36
|
+
self._SetTrainState(trainDataIdxs,TrainLikelihoodFn,Method)
|
|
37
|
+
dataShapes={key:value.shape[1:] for key,value in self._data.items()}
|
|
38
|
+
fitModel=functools.partial(self._RunMethodTrain,TrainLikelihoodFn,dataShapes,saveModelOut,**self._GetTrainData())
|
|
39
|
+
samplePrefixes=self._samplePrefixes+(self._modelOutputPrefix,) if saveModelOut else self._samplePrefixes
|
|
40
|
+
result=Method._Fit(fitModel,self._NextKey(rngSeed),samplePrefixes)
|
|
41
|
+
post=result["samples"]
|
|
42
|
+
self._trainGlobals=self._GetPost(post,"__globalParam__")
|
|
43
|
+
self._trainCholesky=post.get("__cholesky__")
|
|
44
|
+
if saveLocalZs: self._trainLocalZs=self._GetPost(post,"__localZ__")
|
|
45
|
+
if saveModelOut: self._trainModelOut=self._GetPost(post,"__modelOutput__")
|
|
46
|
+
self._ClearTestState()
|
|
47
|
+
if printSummary: self._PrintMethodSummary(Method,result["output"],post,self._localZNames,self._globalNames,"--= Training posterior summary =--")
|
|
48
|
+
jax.block_until_ready(post)
|
|
49
|
+
self._ClearJAX()
|
|
50
|
+
return result["output"]
|
|
51
|
+
|
|
52
|
+
def Test(self,testDataIdxs,TestLikelihoodFn=None,Method=None,nGlobalSamples=None,nLocalZSamples=2000,printSummary=False,rngSeed=None,saveModelOut=False):
|
|
53
|
+
if TestLikelihoodFn is not None: Method=self._ResolveMethod(Method,test=True)
|
|
54
|
+
self._ValidateTest(testDataIdxs,TestLikelihoodFn,Method,nGlobalSamples,nLocalZSamples,printSummary,rngSeed,saveModelOut)
|
|
55
|
+
self._SetTestState(testDataIdxs,TestLikelihoodFn,Method,nGlobalSamples,nLocalZSamples)
|
|
56
|
+
if TestLikelihoodFn is None:
|
|
57
|
+
result=self._TestNoEstimator(nGlobalSamples,nLocalZSamples,rngSeed,saveModelOut)
|
|
58
|
+
else:
|
|
59
|
+
fitResults=self._TestMethod(TestLikelihoodFn,Method,nGlobalSamples,rngSeed,saveModelOut)
|
|
60
|
+
if printSummary:
|
|
61
|
+
for i,fitResult in enumerate(fitResults):
|
|
62
|
+
title="--= Testing posterior summary =--" if len(fitResults)==1 else f"--= Testing posterior summary {i+1} =--"
|
|
63
|
+
if self._testGlobals:
|
|
64
|
+
print(title)
|
|
65
|
+
print("Global parameter values:")
|
|
66
|
+
for name,value in self._testGlobals.items(): print(f" {name}: {np.asarray(value[i])}")
|
|
67
|
+
title=None
|
|
68
|
+
self._PrintMethodSummary(Method,fitResult["output"],fitResult["samples"],self._localZNames,[],title)
|
|
69
|
+
result=[fitResult["output"] for fitResult in fitResults]
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
def ScoreTrainSamples(self,ScoreFn,nSamples=None,rngSeed=None):
|
|
73
|
+
self._ValidateScoreTrainSamples(ScoreFn,nSamples,rngSeed)
|
|
74
|
+
nPost=self._GetNTrainSamples()
|
|
75
|
+
if nSamples is None: idxs=jnp.arange(nPost)
|
|
76
|
+
else: idxs=self._GetRandomIndices( nSamples, nPost, self._NextKey(rngSeed))
|
|
77
|
+
data=self._GetTrainData()
|
|
78
|
+
if self._trainModelOut is not None: modelOut={ name:value[idxs] for name,value in self._trainModelOut.items() }
|
|
79
|
+
else:
|
|
80
|
+
globalParams={ name:value[idxs] for name,value in self._trainGlobals.items() }
|
|
81
|
+
if self._localZNames:
|
|
82
|
+
localZs={ name:value[idxs] for name,value in self._trainLocalZs.items() }
|
|
83
|
+
modelOut=vmap( vmap(self._ModelFn,in_axes=(None,0,0)), in_axes=(0,0,None))(globalParams,localZs,data)
|
|
84
|
+
else: modelOut=vmap( vmap(self._ModelFn,in_axes=(None,None,0)), in_axes=(0,None,None))(globalParams,{},data)
|
|
85
|
+
return vmap( vmap(ScoreFn,in_axes=(0,0)), in_axes=(0,None))(modelOut,data)
|
|
86
|
+
|
|
87
|
+
def ScoreTestSamples(self,ScoreFn,nSamples=None,rngSeed=None):
|
|
88
|
+
self._ValidateScoreTestSamples(ScoreFn,nSamples,rngSeed)
|
|
89
|
+
if self._testModelOut is not None: nLocal=next(iter(self._testModelOut.values())).shape[1]
|
|
90
|
+
else: nLocal=next(iter(self._testLocalZs.values())).shape[1]
|
|
91
|
+
if nSamples is None: idxs=jnp.arange(nLocal)
|
|
92
|
+
else: idxs=self._GetRandomIndices( nSamples, nLocal, self._NextKey(rngSeed))
|
|
93
|
+
data=self._GetTestData()
|
|
94
|
+
if self._testModelOut is not None: modelOut={ name:value[:,idxs] for name,value in self._testModelOut.items() }
|
|
95
|
+
else:
|
|
96
|
+
localZs={ name:value[:,idxs] for name,value in self._testLocalZs.items() }
|
|
97
|
+
modelOut=self._RunModelSamples( self._testGlobals, localZs, data)
|
|
98
|
+
return vmap( vmap( vmap(ScoreFn,in_axes=(0,0)), in_axes=(0,None)), in_axes=(0,None))(modelOut,data)
|
|
99
|
+
|
|
100
|
+
def Save( self, fileName, saveTrainLocalZs=True, saveTestLocalZs=True, saveTrainModelOut=False, saveTestModelOut=False, saveAllData=False):
|
|
101
|
+
self._ValidateSave(fileName,saveTrainLocalZs,saveTestLocalZs,saveTrainModelOut,saveTestModelOut,saveAllData)
|
|
102
|
+
if fileName is None: return self._SaveRecord(saveTrainLocalZs,saveTestLocalZs,saveTrainModelOut,saveTestModelOut,saveAllData,flatten=False)
|
|
103
|
+
record=self._SaveRecord(saveTrainLocalZs,saveTestLocalZs,saveTrainModelOut,saveTestModelOut,saveAllData,flatten=True)
|
|
104
|
+
np.savez_compressed(fileName,**record)
|
|
105
|
+
|
|
106
|
+
@classmethod
|
|
107
|
+
def Load(cls, fileName, ModelFn, GlobalPriorFn=None, data=None):
|
|
108
|
+
cls._ValidateLoad(fileName,ModelFn,GlobalPriorFn,data)
|
|
109
|
+
fileName = Path(fileName)
|
|
110
|
+
if fileName.suffix.lower() != ".npz": fileName = fileName.with_name(fileName.name + ".npz")
|
|
111
|
+
if ModelFn is None: return cls._LoadToDict(fileName)
|
|
112
|
+
return cls._Load(fileName,ModelFn,GlobalPriorFn,data)
|
|
113
|
+
|
|
114
|
+
def _PrintMethodSummary(self,Method,output,samples,localZNames,globalNames,title=None):
|
|
115
|
+
mode=getattr(Method,"_printMode",None)
|
|
116
|
+
if mode in ("before","replace"): Method._Print(output,localZNames,globalNames,title)
|
|
117
|
+
if mode!="replace":
|
|
118
|
+
self._PrintSampleSummary(samples,localZNames,globalNames,title if mode!="before" else None)
|
|
119
|
+
if mode=="after": Method._Print(output,localZNames,globalNames,None)
|
|
120
|
+
|
|
121
|
+
def _PrintSampleSummary(self,samples,localZNames,globalNames,title=None):
|
|
122
|
+
if title is not None: print(title)
|
|
123
|
+
print(f"{'parameter':<20} {'mean':>10} {'std':>10} {'median':>10} {'2.5%':>10} {'97.5%':>10}")
|
|
124
|
+
for prefix,names in (("__globalParam__",[] if globalNames is None else list(globalNames)),("__localZ__",list(localZNames))):
|
|
125
|
+
for name in names:
|
|
126
|
+
sampleName=f"{prefix}{name}"
|
|
127
|
+
if sampleName not in samples: continue
|
|
128
|
+
vals=np.asarray(samples[sampleName])
|
|
129
|
+
shape=vals.shape[1:]
|
|
130
|
+
if not shape: self._PrintSampleRow(name,vals)
|
|
131
|
+
else:
|
|
132
|
+
for inds in np.ndindex(shape): self._PrintSampleRow(f"{name}[{','.join(map(str,inds))}]",vals[(slice(None),)+inds])
|
|
133
|
+
|
|
134
|
+
def _PrintSampleRow(self,label,vals):
|
|
135
|
+
print(f"{label:<20} {np.mean(vals):>10.3f} {np.std(vals):>10.3f} {np.median(vals):>10.3f} {np.percentile(vals,2.5):>10.3f} {np.percentile(vals,97.5):>10.3f}")
|
|
136
|
+
|
|
137
|
+
def _TestMethod(self,TestLikelihoodFn,Method,nGlobalSamples=None,rngSeed=None,saveModelOut=False):
|
|
138
|
+
postKey,runKey=random.split(self._NextKey(rngSeed),2)
|
|
139
|
+
self._SetTestGlobals(nGlobalSamples,postKey)
|
|
140
|
+
testData=self._GetTestData()
|
|
141
|
+
nGlobalSamples=1 if nGlobalSamples is None else nGlobalSamples
|
|
142
|
+
runKeys=random.split(runKey,nGlobalSamples)
|
|
143
|
+
results=[]
|
|
144
|
+
localZPosts=[]
|
|
145
|
+
modelOutPosts=[]
|
|
146
|
+
for i in range(nGlobalSamples):
|
|
147
|
+
globs={name:value[i] for name,value in self._testGlobals.items()}
|
|
148
|
+
cholesky=None if self._testCholesky is None else self._testCholesky[i]
|
|
149
|
+
fitModel=functools.partial(self._RunMethodTest,TestLikelihoodFn,globs,cholesky,saveModelOut,**testData)
|
|
150
|
+
samplePrefixes=("__localZ__",self._modelOutputPrefix) if saveModelOut else ("__localZ__",)
|
|
151
|
+
result=Method._Fit(fitModel,runKeys[i],samplePrefixes)
|
|
152
|
+
localZPosts.append(self._GetPost(result["samples"],"__localZ__"))
|
|
153
|
+
if saveModelOut: modelOutPosts.append(self._GetPost(result["samples"],"__modelOutput__"))
|
|
154
|
+
results.append(result)
|
|
155
|
+
self._testLocalZs={} if not localZPosts or not localZPosts[0] else {name:jnp.stack([p[name] for p in localZPosts]) for name in localZPosts[0]}
|
|
156
|
+
if saveModelOut: self._testModelOut={} if not modelOutPosts or not modelOutPosts[0] else {name:jnp.stack([p[name] for p in modelOutPosts]) for name in modelOutPosts[0]}
|
|
157
|
+
jax.block_until_ready((self._testLocalZs,self._testModelOut))
|
|
158
|
+
self._ClearJAX()
|
|
159
|
+
return results
|
|
160
|
+
|
|
161
|
+
def _TestNoEstimator(self,nGlobalSamples,nLocalZSamples,rngSeed=None,saveModelOut=False):
|
|
162
|
+
postKey,sampleKey=random.split(self._NextKey(rngSeed),2)
|
|
163
|
+
self._SetTestGlobals(nGlobalSamples,postKey)
|
|
164
|
+
nGlobalSamples=1 if nGlobalSamples is None else nGlobalSamples
|
|
165
|
+
testData=self._GetTestData()
|
|
166
|
+
nTest=len(testData[next(iter(testData))])
|
|
167
|
+
nLocal=len(self._localZNames)
|
|
168
|
+
if nLocal>0:
|
|
169
|
+
localZs=random.normal(sampleKey,(nGlobalSamples,nLocalZSamples,nTest,nLocal))
|
|
170
|
+
if nLocal>1:
|
|
171
|
+
localZs=jnp.einsum("gspi,gji->gspj",localZs,self._testCholesky)
|
|
172
|
+
localZs={name:localZs[:,:,:,i] for i,name in enumerate(self._localZNames)}
|
|
173
|
+
else: localZs={}
|
|
174
|
+
self._testLocalZs=localZs
|
|
175
|
+
if saveModelOut:
|
|
176
|
+
self._testModelOut=self._RunModelSamples(self._testGlobals,self._testLocalZs,testData)
|
|
177
|
+
|
|
178
|
+
def _RunModelSamples(self,globalParams,localZs,data):
|
|
179
|
+
return vmap( vmap( vmap(self._ModelFn,in_axes=(None,0,0)),in_axes=(None,0,None)),in_axes=(0,0,None))(globalParams,localZs,data)
|
|
180
|
+
|
|
181
|
+
def _GetRandomIndices(self,nSamples,length,RNGkey):
|
|
182
|
+
return random.choice(RNGkey,length,shape=(nSamples,),replace=nSamples>length)
|
|
183
|
+
|
|
184
|
+
def _GenLocalZsTraining(self,dataSize):
|
|
185
|
+
nLocal=len(self._localZNames)
|
|
186
|
+
localZsRaw=npo.sample("__localZRaw__",dist.Normal(0.0,1.0).expand((dataSize,nLocal)))
|
|
187
|
+
localZs=localZsRaw
|
|
188
|
+
if nLocal>1:
|
|
189
|
+
choleskySample=npo.sample("__choleskySample__",dist.LKJCholesky(nLocal,concentration=self._lkjConcentration))
|
|
190
|
+
cholesky=npo.deterministic("__cholesky__",choleskySample)
|
|
191
|
+
localZs=localZs@cholesky.T
|
|
192
|
+
return dict(zip(self._localZNames,localZs.T))
|
|
193
|
+
|
|
194
|
+
def _RunMethodTrain(self,TrainLikelihoodFn,dataShapes,saveOutput,**modelData):
|
|
195
|
+
dataLen=len(modelData[next(iter(modelData))])
|
|
196
|
+
localZs=self._GenLocalZsTraining(dataLen) if self._localZNames else {}
|
|
197
|
+
for name, value in localZs.items(): npo.deterministic(f"__localZ__{name}", value)
|
|
198
|
+
globalDists=self._GlobalPriorFn(dataShapes) if self._GlobalPriorFn is not None else {}
|
|
199
|
+
if not isinstance(globalDists,dict): raise TypeError("GlobalPriorFn must return a dictionary mapping global parameter names to NumPyro distributions.")
|
|
200
|
+
globalParams={}
|
|
201
|
+
for name,distribution in globalDists.items():
|
|
202
|
+
if not isinstance(name,str): raise TypeError(f"GlobalPriorFn keys must be strings; got {type(name).__name__}.")
|
|
203
|
+
if not name: raise ValueError("GlobalPriorFn keys cannot be empty strings.")
|
|
204
|
+
if name.startswith("__"): raise ValueError(f"GlobalPriorFn key '{name}' cannot start with '__', which is reserved for BET internal sample sites.")
|
|
205
|
+
globalParams[name]=npo.sample(name,distribution)
|
|
206
|
+
if self._globalNames is None: self._globalNames=list(globalParams)
|
|
207
|
+
for name,value in globalParams.items(): npo.deterministic(f"__globalParam__{name}",value)
|
|
208
|
+
modelOut=vmap(self._ModelFn,in_axes=(None,0,0))(globalParams,localZs,modelData)
|
|
209
|
+
if saveOutput:
|
|
210
|
+
for name,value in modelOut.items(): npo.deterministic(f"__modelOutput__{name}",value)
|
|
211
|
+
self._ApplyLikelihoodFn(TrainLikelihoodFn,globalParams,localZs,modelData,modelOut)
|
|
212
|
+
|
|
213
|
+
def _GenLocalZsTesting(self,cholesky,dataSize):
|
|
214
|
+
nLocal=len(self._localZNames)
|
|
215
|
+
localZsRaw=npo.sample("__localZRaw__",dist.Normal(0.0,1.0).expand((dataSize,nLocal)))
|
|
216
|
+
localZs=localZsRaw
|
|
217
|
+
if nLocal>1:
|
|
218
|
+
localZs=localZs @ cholesky.T
|
|
219
|
+
return dict(zip(self._localZNames,localZs.T))
|
|
220
|
+
|
|
221
|
+
def _RunMethodTest(self,TestLikelihoodFn,globalParams,cholesky,saveOutput,**modelData):
|
|
222
|
+
dataLen=len(modelData[next(iter(modelData))])
|
|
223
|
+
localZs=self._GenLocalZsTesting(cholesky,dataLen) if self._localZNames else {}
|
|
224
|
+
for name,value in localZs.items(): npo.deterministic(f"__localZ__{name}",value)
|
|
225
|
+
modelOut=vmap(self._ModelFn,in_axes=(None,0,0))(globalParams,localZs,modelData)
|
|
226
|
+
if saveOutput:
|
|
227
|
+
for name,value in modelOut.items(): npo.deterministic(f"__modelOutput__{name}",value)
|
|
228
|
+
self._ApplyLikelihoodFn(TestLikelihoodFn,globalParams,localZs,modelData,modelOut)
|
|
229
|
+
|
|
230
|
+
def _ApplyLikelihoodFn(self,LikelihoodFn,globalParams,localZs,modelData,modelOut):
|
|
231
|
+
likelihoods=vmap(LikelihoodFn,in_axes=(None,0,0,0))(globalParams,localZs,modelData,modelOut)
|
|
232
|
+
if not isinstance(likelihoods,dict): raise TypeError("LikelihoodFn must return a dictionary mapping sample-site names to (observation, distribution) tuples.")
|
|
233
|
+
if not likelihoods: raise ValueError("LikelihoodFn must return at least one likelihood.")
|
|
234
|
+
for name,value in likelihoods.items():
|
|
235
|
+
if not isinstance(name,str): raise TypeError(f"LikelihoodFn keys must be strings; got {type(name).__name__}.")
|
|
236
|
+
if not name: raise ValueError("LikelihoodFn keys cannot be empty strings.")
|
|
237
|
+
if name.startswith("__"): raise ValueError(f"LikelihoodFn key '{name}' cannot start with '__', which is reserved for BET internal sample sites.")
|
|
238
|
+
if not isinstance(value,tuple) or len(value)!=2: raise TypeError(f"LikelihoodFn['{name}'] must be an (observation, distribution) tuple.")
|
|
239
|
+
observation,distribution=value
|
|
240
|
+
if not isinstance(distribution,dist.Distribution): raise TypeError(f"LikelihoodFn['{name}'][1] must be a NumPyro Distribution; got {type(distribution).__name__}.")
|
|
241
|
+
npo.sample(name,distribution,obs=observation)
|
|
242
|
+
|
|
243
|
+
def _NextKey(self,rngSeed=None):
|
|
244
|
+
if rngSeed is not None:
|
|
245
|
+
self._key=random.PRNGKey(rngSeed)
|
|
246
|
+
self._key,key=random.split(self._key)
|
|
247
|
+
return key
|
|
248
|
+
|
|
249
|
+
def _GetTrainData(self):
|
|
250
|
+
return { k: v[jnp.asarray(self._trainDataIdxs)] for k, v in self._data.items() }
|
|
251
|
+
|
|
252
|
+
def _GetTestData(self):
|
|
253
|
+
return { k: v[jnp.asarray(self._testDataIdxs)] for k, v in self._data.items() }
|
|
254
|
+
|
|
255
|
+
def _GetNTrainSamples(self):
|
|
256
|
+
if self._trainGlobals: return len(next(iter(self._trainGlobals.values())))
|
|
257
|
+
if self._trainLocalZs: return len(next(iter(self._trainLocalZs.values())))
|
|
258
|
+
if self._trainCholesky is not None: return len(self._trainCholesky)
|
|
259
|
+
return 0
|
|
260
|
+
|
|
261
|
+
def _GetFnName(self,Fn):
|
|
262
|
+
if Fn is None: return None
|
|
263
|
+
if isinstance(Fn, functools.partial): return Fn.func.__name__
|
|
264
|
+
return getattr(Fn, "__name__", type(Fn).__name__)
|
|
265
|
+
|
|
266
|
+
def _ResolveMethod(self,Method,test=False):
|
|
267
|
+
if not inspect.isclass(Method): return Method
|
|
268
|
+
params=None
|
|
269
|
+
if test and self._testParams is not None and self._testParams.get("mode")=="Method" and self._testParams.get("methodName") is not None:
|
|
270
|
+
params=self._testParams
|
|
271
|
+
elif self._trainParams is not None:
|
|
272
|
+
params=self._trainParams
|
|
273
|
+
if params is None: raise RuntimeError(f"{Method.__name__} was passed as a Method class, but no saved Method configuration is available.")
|
|
274
|
+
savedName=params.get("methodName")
|
|
275
|
+
if Method.__name__!=savedName:
|
|
276
|
+
warnings.warn(f"Method class {Method.__name__} does not match the saved Method {savedName}. Using the saved {savedName} constructor arguments with {Method.__name__}.",stacklevel=2)
|
|
277
|
+
args=params.get("methodArgs")
|
|
278
|
+
if args is None: raise RuntimeError(f"No saved constructor arguments are available for Method {savedName}.")
|
|
279
|
+
try: return Method(**args)
|
|
280
|
+
except Exception as e: raise RuntimeError(f"Could not reconstruct saved Method {savedName} from its saved constructor arguments.") from e
|
|
281
|
+
|
|
282
|
+
def _GetPost(self,post, prefix):
|
|
283
|
+
ret = { name.removeprefix(prefix): value for name, value in post.items() if name.startswith(prefix) }
|
|
284
|
+
if not ret: return {}
|
|
285
|
+
return ret
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _SaveRecord(self,saveTrainLocalZs=True,saveTestLocalZs=True,saveTrainModelOut=False,saveTestModelOut=False,saveAllData=False,flatten=True):
|
|
289
|
+
record={}
|
|
290
|
+
self._AddInfo(record,"meta",flatten,{
|
|
291
|
+
"localZNames":np.asarray(self._localZNames,dtype=str),
|
|
292
|
+
"globalNames":None if self._globalNames is None else np.asarray(self._globalNames,dtype=str),
|
|
293
|
+
"lkjConcentration":self._lkjConcentration,
|
|
294
|
+
"modelFnName":self._modelFnName,
|
|
295
|
+
"globalPriorFnName":self._globalPriorFnName})
|
|
296
|
+
self._AddInfo(record,"state",flatten,{"trainDataIdxs":self._trainDataIdxs,"testDataIdxs":self._testDataIdxs,"rngKey":self._key})
|
|
297
|
+
self._AddInfo(record,"trainParams",flatten,self._trainParams)
|
|
298
|
+
self._AddInfo(record,"testParams",flatten,self._testParams)
|
|
299
|
+
self._AddInfo(record,"trainGlobals",flatten,self._trainGlobals)
|
|
300
|
+
if self._trainCholesky is not None: record["trainCholesky"]=np.asarray(self._trainCholesky)
|
|
301
|
+
if saveTrainLocalZs: self._AddInfo(record,"trainLocalZs",flatten,self._trainLocalZs)
|
|
302
|
+
if saveTestLocalZs and self._testLocalZs:
|
|
303
|
+
self._AddInfo(record,"testGlobals",flatten,self._testGlobals)
|
|
304
|
+
self._AddInfo(record,"testLocalZs",flatten,self._testLocalZs)
|
|
305
|
+
if self._testCholesky is not None: record["testCholesky"]=np.asarray(self._testCholesky)
|
|
306
|
+
if saveTrainModelOut and self._trainModelOut is not None: self._AddInfo(record,"trainModelOut",flatten,self._trainModelOut)
|
|
307
|
+
if saveTestModelOut and self._testModelOut is not None: self._AddInfo(record,"testModelOut",flatten,self._testModelOut)
|
|
308
|
+
if saveAllData: self._AddInfo(record,"data",flatten,self._data)
|
|
309
|
+
return record
|
|
310
|
+
|
|
311
|
+
@classmethod
|
|
312
|
+
def _Load(cls,fileName,ModelFn,GlobalPriorFn=None,data=None):
|
|
313
|
+
with np.load(fileName,allow_pickle=False) as record:
|
|
314
|
+
meta=cls._GetInfo(record,"meta")
|
|
315
|
+
state=cls._GetInfo(record,"state")
|
|
316
|
+
savedData=cls._GetInfo(record,"data")
|
|
317
|
+
if data is None:
|
|
318
|
+
if not savedData: raise ValueError("data must be provided because the saved framework does not contain the original model data.")
|
|
319
|
+
data=savedData
|
|
320
|
+
localZNames=meta.get("localZNames")
|
|
321
|
+
localZNames=[] if localZNames is None else list(localZNames)
|
|
322
|
+
framework=cls(data,ModelFn,localZNames=localZNames,GlobalPriorFn=GlobalPriorFn,lkjConcentration=meta["lkjConcentration"])
|
|
323
|
+
globalNames=meta.get("globalNames")
|
|
324
|
+
framework._globalNames=None if globalNames is None else list(globalNames)
|
|
325
|
+
framework._trainParams=cls._GetInfo(record,"trainParams") or None
|
|
326
|
+
framework._testParams=cls._GetInfo(record,"testParams") or None
|
|
327
|
+
framework._trainGlobals={name:jnp.asarray(value) for name,value in cls._GetInfo(record,"trainGlobals").items()}
|
|
328
|
+
framework._trainLocalZs={name:jnp.asarray(value) for name,value in cls._GetInfo(record,"trainLocalZs").items()}
|
|
329
|
+
framework._trainCholesky=jnp.asarray(record["trainCholesky"]) if "trainCholesky" in record.files else None
|
|
330
|
+
framework._testGlobals={name:jnp.asarray(value) for name,value in cls._GetInfo(record,"testGlobals").items()}
|
|
331
|
+
framework._testLocalZs={name:jnp.asarray(value) for name,value in cls._GetInfo(record,"testLocalZs").items()}
|
|
332
|
+
framework._testCholesky=jnp.asarray(record["testCholesky"]) if "testCholesky" in record.files else None
|
|
333
|
+
trainModelOut=cls._GetInfo(record,"trainModelOut")
|
|
334
|
+
testModelOut=cls._GetInfo(record,"testModelOut")
|
|
335
|
+
framework._trainModelOut={name:jnp.asarray(value) for name,value in trainModelOut.items()} if trainModelOut else None
|
|
336
|
+
framework._testModelOut={name:jnp.asarray(value) for name,value in testModelOut.items()} if testModelOut else None
|
|
337
|
+
framework._trainDataIdxs=None if state.get("trainDataIdxs") is None else np.asarray(state["trainDataIdxs"])
|
|
338
|
+
framework._testDataIdxs=None if state.get("testDataIdxs") is None else np.asarray(state["testDataIdxs"])
|
|
339
|
+
framework._key=None if state.get("rngKey") is None else jnp.asarray(state["rngKey"])
|
|
340
|
+
savedModelFnName=meta.get("modelFnName")
|
|
341
|
+
currentModelFnName=framework._GetFnName(ModelFn)
|
|
342
|
+
if savedModelFnName!=currentModelFnName: warnings.warn(f"Saved ModelFn was '{savedModelFnName}', but the supplied ModelFn is '{currentModelFnName}'.",RuntimeWarning)
|
|
343
|
+
savedGlobalPriorFnName=meta.get("globalPriorFnName")
|
|
344
|
+
currentGlobalPriorFnName=framework._GetFnName(GlobalPriorFn)
|
|
345
|
+
if savedGlobalPriorFnName!=currentGlobalPriorFnName: warnings.warn(f"Saved GlobalPriorFn was '{savedGlobalPriorFnName}', but the supplied GlobalPriorFn is '{currentGlobalPriorFnName}'.",RuntimeWarning)
|
|
346
|
+
return framework
|
|
347
|
+
|
|
348
|
+
@staticmethod
|
|
349
|
+
def _LoadToDict(path):
|
|
350
|
+
with np.load(path,allow_pickle=False) as flat:
|
|
351
|
+
record={}
|
|
352
|
+
for key,value in flat.items():
|
|
353
|
+
parts=key.split("/")
|
|
354
|
+
if len(parts)==1:
|
|
355
|
+
record[key]=value.item() if value.ndim==0 else value
|
|
356
|
+
continue
|
|
357
|
+
if len(parts) not in (2,3): raise ValueError(f"Invalid saved key '{key}'.")
|
|
358
|
+
prefix,name=parts[:2]
|
|
359
|
+
if not prefix or not name: raise ValueError(f"Invalid saved key '{key}'.")
|
|
360
|
+
if len(parts)==3 and parts[2]!="__none__": raise ValueError(f"Invalid saved key '{key}'.")
|
|
361
|
+
if prefix not in record: record[prefix]={}
|
|
362
|
+
record[prefix][name]=None if len(parts)==3 else (value.item() if value.ndim==0 else value)
|
|
363
|
+
return record
|
|
364
|
+
|
|
365
|
+
@staticmethod
|
|
366
|
+
def _AddInfo(record,prefix,flatten,info):
|
|
367
|
+
if info is None: return
|
|
368
|
+
if not flatten:
|
|
369
|
+
record[prefix]=info
|
|
370
|
+
return
|
|
371
|
+
for name,value in info.items():
|
|
372
|
+
if not isinstance(name,str): raise TypeError(f"Saved dictionary keys must be strings; got {type(name).__name__}.")
|
|
373
|
+
if not name: raise ValueError("Saved dictionary keys cannot be empty.")
|
|
374
|
+
if "/" in name: raise ValueError(f"Saved dictionary key '{name}' cannot contain '/'.")
|
|
375
|
+
if value is None: record[f"{prefix}/{name}/__none__"]=np.asarray(True)
|
|
376
|
+
elif isinstance(value,dict):
|
|
377
|
+
for subName,subValue in value.items():
|
|
378
|
+
if not isinstance(subName,str) or not subName or "/" in subName: raise ValueError(f"Invalid nested saved key '{subName}'.")
|
|
379
|
+
if subValue is None: record[f"{prefix}/{name}.{subName}/__none__"]=np.asarray(True)
|
|
380
|
+
else: record[f"{prefix}/{name}.{subName}"]=np.asarray(subValue)
|
|
381
|
+
else: record[f"{prefix}/{name}"]=np.asarray(value)
|
|
382
|
+
|
|
383
|
+
@staticmethod
|
|
384
|
+
def _GetInfo(record,prefix):
|
|
385
|
+
info={}
|
|
386
|
+
start=f"{prefix}/"
|
|
387
|
+
noneSuffix="/__none__"
|
|
388
|
+
keys=record.files if hasattr(record,"files") else record.keys()
|
|
389
|
+
for key in keys:
|
|
390
|
+
if not key.startswith(start): continue
|
|
391
|
+
name=key.removeprefix(start)
|
|
392
|
+
isNone=name.endswith(noneSuffix)
|
|
393
|
+
if isNone: name=name.removesuffix(noneSuffix)
|
|
394
|
+
if not name or "/" in name: raise ValueError(f"Invalid saved key '{key}'.")
|
|
395
|
+
value=None if isNone else record[key]
|
|
396
|
+
value=value if value is None or value.ndim else value.item()
|
|
397
|
+
if "." in name:
|
|
398
|
+
parent,child=name.split(".",1)
|
|
399
|
+
info.setdefault(parent,{})[child]=value
|
|
400
|
+
else: info[name]=value
|
|
401
|
+
return info
|
|
402
|
+
|
|
403
|
+
def _ValidateInit(self,data,ModelFn,localZNames,GlobalPriorFn,lkjConcentration,rngSeed):
|
|
404
|
+
self._CheckModelData(data)
|
|
405
|
+
self._CheckCallable(ModelFn,"ModelFn",3)
|
|
406
|
+
self._CheckCallable(GlobalPriorFn,"GlobalPriorFn",1,optional=True)
|
|
407
|
+
if localZNames is not None:
|
|
408
|
+
if not isinstance(localZNames,(list,tuple)): raise TypeError("localZNames must be a list or tuple of strings, or None.")
|
|
409
|
+
for i,name in enumerate(localZNames):
|
|
410
|
+
if not isinstance(name,str): raise TypeError(f"localZNames[{i}] must be a string; got {type(name).__name__}.")
|
|
411
|
+
if not name: raise ValueError(f"localZNames[{i}] cannot be empty.")
|
|
412
|
+
if len(set(localZNames))!=len(localZNames): raise ValueError("localZNames cannot contain duplicate names.")
|
|
413
|
+
try: concentration=np.asarray(lkjConcentration)
|
|
414
|
+
except Exception as e: raise TypeError("lkjConcentration must be a positive finite real scalar.") from e
|
|
415
|
+
if concentration.ndim!=0: raise TypeError("lkjConcentration must be a scalar.")
|
|
416
|
+
if not np.issubdtype(concentration.dtype,np.number) or np.issubdtype(concentration.dtype,np.complexfloating): raise TypeError("lkjConcentration must be a real numeric scalar.")
|
|
417
|
+
concentration=concentration.item()
|
|
418
|
+
if not np.isfinite(concentration): raise ValueError("lkjConcentration must be finite.")
|
|
419
|
+
if concentration<=0: raise ValueError("lkjConcentration must be > 0.")
|
|
420
|
+
if rngSeed is not None: self._CheckNonnegativeInt(rngSeed,"RNG seed")
|
|
421
|
+
|
|
422
|
+
def _ValidateTrain(self,trainDataIdxs,TrainLikelihoodFn,Method,printSummary,rngSeed,saveLocalZs,saveModelOut):
|
|
423
|
+
self._CheckIndices(trainDataIdxs,len(self._data[next(iter(self._data))]),"trainDataIdxs")
|
|
424
|
+
self._CheckCallable(TrainLikelihoodFn,"TrainLikelihoodFn",4)
|
|
425
|
+
self._CheckMethod(Method,"Method")
|
|
426
|
+
self._CheckBool(printSummary,"printSummary"); self._CheckBool(saveLocalZs,"saveLocalZs"); self._CheckBool(saveModelOut,"saveModelOut")
|
|
427
|
+
self._CheckRngAvailable(rngSeed)
|
|
428
|
+
|
|
429
|
+
def _ValidateTest(self,testDataIdxs,TestLikelihoodFn,Method,nGlobalSamples,nLocalZSamples,printSummary,rngSeed,saveModelOut):
|
|
430
|
+
if self._trainParams is None: raise RuntimeError("Train() must be run before Test().")
|
|
431
|
+
if not self._localZNames: raise RuntimeError("Test() requires at least one local Z.")
|
|
432
|
+
self._CheckIndices(testDataIdxs,len(self._data[next(iter(self._data))]),"testDataIdxs")
|
|
433
|
+
self._CheckCallable(TestLikelihoodFn,"TestLikelihoodFn",4,optional=True)
|
|
434
|
+
if nGlobalSamples is not None: self._CheckPositiveInt(nGlobalSamples,"nGlobalSamples")
|
|
435
|
+
if TestLikelihoodFn is None:
|
|
436
|
+
if Method is not None: raise ValueError("Method must be None when TestLikelihoodFn is None.")
|
|
437
|
+
self._CheckPositiveInt(nLocalZSamples,"nLocalZSamples")
|
|
438
|
+
else: self._CheckMethod(Method,"Method")
|
|
439
|
+
self._CheckBool(printSummary,"printSummary"); self._CheckBool(saveModelOut,"saveModelOut")
|
|
440
|
+
self._CheckRngAvailable(rngSeed)
|
|
441
|
+
|
|
442
|
+
def _ValidateScoreTrainSamples(self,ScoreFn,nSamples,rngSeed):
|
|
443
|
+
if self._trainParams is None: raise RuntimeError("Train() must be run before ScoreTrainSamples().")
|
|
444
|
+
self._CheckCallable(ScoreFn,"ScoreFn",2)
|
|
445
|
+
if nSamples is not None:
|
|
446
|
+
self._CheckPositiveInt(nSamples,"nSamples"); self._CheckRngAvailable(rngSeed)
|
|
447
|
+
if self._trainModelOut is None and self._localZNames and not self._trainLocalZs: raise RuntimeError("ScoreTrainSamples() requires saved training local Zs or saved training model outputs when the model has local Zs.")
|
|
448
|
+
|
|
449
|
+
def _ValidateScoreTestSamples(self,ScoreFn,nSamples,rngSeed):
|
|
450
|
+
if self._testParams is None: raise RuntimeError("Test() must be run before ScoreTestSamples().")
|
|
451
|
+
if self._testModelOut is None and not self._testLocalZs: raise RuntimeError("ScoreTestSamples() requires saved test local Zs or saved test model outputs.")
|
|
452
|
+
self._CheckCallable(ScoreFn,"ScoreFn",2)
|
|
453
|
+
if nSamples is not None:
|
|
454
|
+
self._CheckPositiveInt(nSamples,"nSamples"); self._CheckRngAvailable(rngSeed)
|
|
455
|
+
|
|
456
|
+
def _ValidateSave(self,fileName,saveTrainLocalZs,saveTestLocalZs,saveTrainModelOut,saveTestModelOut,saveAllData):
|
|
457
|
+
if fileName is not None and not isinstance(fileName,(str,os.PathLike)): raise TypeError("fileName must be a string, path-like object, or None.")
|
|
458
|
+
for value,name in ((saveTrainLocalZs,"saveTrainLocalZs"),(saveTestLocalZs,"saveTestLocalZs"),(saveTrainModelOut,"saveTrainModelOut"),(saveTestModelOut,"saveTestModelOut"),(saveAllData,"saveAllData")): self._CheckBool(value,name)
|
|
459
|
+
|
|
460
|
+
@classmethod
|
|
461
|
+
def _ValidateLoad(cls,fileName,ModelFn,GlobalPriorFn,data):
|
|
462
|
+
if not isinstance(fileName,(str,os.PathLike)): raise TypeError("fileName must be a string or path-like object.")
|
|
463
|
+
cls._CheckCallable(ModelFn,"ModelFn",3,optional=True)
|
|
464
|
+
cls._CheckCallable(GlobalPriorFn,"GlobalPriorFn",1,optional=True)
|
|
465
|
+
if data is not None: cls._CheckModelData(data)
|
|
466
|
+
|
|
467
|
+
def _SetTrainState(self,trainDataIdxs,TrainLikelihoodFn,Method):
|
|
468
|
+
self._trainParams={"trainLikelihoodName":self._GetFnName(TrainLikelihoodFn),"methodName":type(Method).__name__,"methodArgs":Method._SaveArgs()}
|
|
469
|
+
self._trainDataIdxs=trainDataIdxs
|
|
470
|
+
self._globalNames=None
|
|
471
|
+
self._trainGlobals={}
|
|
472
|
+
self._trainLocalZs={}
|
|
473
|
+
self._trainCholesky=None
|
|
474
|
+
self._trainModelOut=None
|
|
475
|
+
|
|
476
|
+
def _SetTestState(self,testDataIdxs,TestLikelihoodFn,Method,nGlobalSamples,nLocalZSamples):
|
|
477
|
+
self._testParams={"mode":"NoMethod" if TestLikelihoodFn is None else "Method","nGlobalSamples":nGlobalSamples,"nLocalZSamples":nLocalZSamples if TestLikelihoodFn is None else None,"testLikelihoodName":self._GetFnName(TestLikelihoodFn),"methodName":None if Method is None else type(Method).__name__,"methodArgs":None if Method is None else Method._SaveArgs()}
|
|
478
|
+
self._testDataIdxs=testDataIdxs
|
|
479
|
+
self._testGlobals={}
|
|
480
|
+
self._testLocalZs={}
|
|
481
|
+
self._testCholesky=None
|
|
482
|
+
self._testModelOut=None
|
|
483
|
+
|
|
484
|
+
def _SetTestGlobals(self,nGlobalSamples,RNGkey):
|
|
485
|
+
if nGlobalSamples is None:
|
|
486
|
+
self._testGlobals={name:jnp.expand_dims(jnp.median(value,axis=0),axis=0) for name,value in self._trainGlobals.items()}
|
|
487
|
+
self._testCholesky=None if self._trainCholesky is None else jnp.expand_dims(jnp.median(self._trainCholesky,axis=0), axis=0)
|
|
488
|
+
else:
|
|
489
|
+
nTrainSamples=self._GetNTrainSamples()
|
|
490
|
+
if nTrainSamples==0:
|
|
491
|
+
raise RuntimeError("No saved training posterior draws are available for nGlobalSamples; save global parameters, local Zs, or Cholesky draws, or use nGlobalSamples=None.")
|
|
492
|
+
idxs=self._GetRandomIndices(nGlobalSamples,nTrainSamples,RNGkey)
|
|
493
|
+
self._testGlobals={ name:value[idxs] for name,value in self._trainGlobals.items() }
|
|
494
|
+
self._testCholesky=None if self._trainCholesky is None else self._trainCholesky[idxs]
|
|
495
|
+
|
|
496
|
+
@staticmethod
|
|
497
|
+
def _CheckBool(value,name):
|
|
498
|
+
if not isinstance(value,(bool,np.bool_)): raise TypeError(f"{name} must be bool; got {type(value).__name__}.")
|
|
499
|
+
|
|
500
|
+
@staticmethod
|
|
501
|
+
def _CheckPositiveInt(value,name):
|
|
502
|
+
if isinstance(value,(bool,np.bool_)) or not isinstance(value,(int,np.integer)): raise TypeError(f"{name} must be an integer; got {type(value).__name__}.")
|
|
503
|
+
if value<=0: raise ValueError(f"{name} must be > 0.")
|
|
504
|
+
|
|
505
|
+
@staticmethod
|
|
506
|
+
def _CheckNonnegativeInt(value,name):
|
|
507
|
+
if isinstance(value,(bool,np.bool_)) or not isinstance(value,(int,np.integer)): raise TypeError(f"{name} must be an integer; got {type(value).__name__}.")
|
|
508
|
+
if value<0: raise ValueError(f"{name} must be >= 0.")
|
|
509
|
+
|
|
510
|
+
@staticmethod
|
|
511
|
+
def _CheckCallable(fn,name,nArgs,optional=False):
|
|
512
|
+
if fn is None:
|
|
513
|
+
if optional: return
|
|
514
|
+
raise TypeError(f"{name} cannot be None.")
|
|
515
|
+
if not callable(fn): raise TypeError(f"{name} must be callable; got {type(fn).__name__}.")
|
|
516
|
+
try: signature=inspect.signature(fn)
|
|
517
|
+
except (TypeError,ValueError): return
|
|
518
|
+
try: signature.bind(*([None]*nArgs))
|
|
519
|
+
except TypeError as e: raise TypeError(f"{name} must accept {nArgs} positional argument{'s' if nArgs!=1 else ''}. Framework call will be incompatible with signature {signature}.") from e
|
|
520
|
+
|
|
521
|
+
@staticmethod
|
|
522
|
+
def _CheckMethod(estimator,name,optional=False):
|
|
523
|
+
if estimator is None and optional: return
|
|
524
|
+
from .MethodValidation import ValidateMethod
|
|
525
|
+
ValidateMethod(estimator)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
@staticmethod
|
|
529
|
+
def _CheckModelData(data):
|
|
530
|
+
if not isinstance(data,(dict,np.lib.npyio.NpzFile)): raise TypeError("data must be a dictionary.")
|
|
531
|
+
if len(data)==0: raise ValueError("data cannot be empty.")
|
|
532
|
+
dataSize=None
|
|
533
|
+
for name,value in data.items():
|
|
534
|
+
if not isinstance(name,str): raise TypeError(f"data keys must be strings; got {type(name).__name__}.")
|
|
535
|
+
if not name: raise ValueError("data keys cannot be empty strings.")
|
|
536
|
+
if not isinstance(value,(jax.Array,np.ndarray)): raise TypeError(f"data['{name}'] must be a JAX or NumPy array; got {type(value).__name__}.")
|
|
537
|
+
if value.ndim==0: raise ValueError(f"data['{name}'] must have at least one dimension because its first dimension represents data entries.")
|
|
538
|
+
if dataSize is None: dataSize=len(value)
|
|
539
|
+
elif len(value)!=dataSize: raise ValueError(f"All arrays in data must have the same first dimension. Expected {dataSize}, but data['{name}'] has length {len(value)}.")
|
|
540
|
+
if dataSize==0: raise ValueError("data must contain at least one data entry.")
|
|
541
|
+
|
|
542
|
+
@staticmethod
|
|
543
|
+
def _CheckIndices(indices,dataSize,name):
|
|
544
|
+
if not isinstance(indices,(list,tuple,np.ndarray,jax.Array)): raise TypeError(f"{name} must be a list, tuple, NumPy array, or JAX array.")
|
|
545
|
+
arr=np.asarray(indices)
|
|
546
|
+
if arr.ndim!=1: raise ValueError(f"{name} must be one-dimensional.")
|
|
547
|
+
if len(arr)==0: raise ValueError(f"{name} cannot be empty.")
|
|
548
|
+
if not np.issubdtype(arr.dtype,np.integer): raise TypeError(f"{name} must contain integers.")
|
|
549
|
+
if np.any(arr<0): raise ValueError(f"{name} cannot contain negative indices.")
|
|
550
|
+
if np.any(arr>=dataSize): raise IndexError(f"{name} contains an index outside data, which contains {dataSize} entries.")
|
|
551
|
+
if len(np.unique(arr))!=len(arr): raise ValueError(f"{name} cannot contain duplicate indices.")
|
|
552
|
+
|
|
553
|
+
def _CheckRngAvailable(self,rngSeed):
|
|
554
|
+
if rngSeed is not None:
|
|
555
|
+
self._CheckNonnegativeInt(rngSeed,"RNG seed")
|
|
556
|
+
return
|
|
557
|
+
if self._key is None: raise ValueError("An RNG seed must be provided either when constructing the framework or for this call.")
|
|
558
|
+
|
|
559
|
+
def _ClearTrainState(self):
|
|
560
|
+
self._trainParams=None
|
|
561
|
+
self._trainDataIdxs=None
|
|
562
|
+
self._trainGlobals={}
|
|
563
|
+
self._trainLocalZs={}
|
|
564
|
+
self._trainCholesky=None
|
|
565
|
+
self._trainModelOut=None
|
|
566
|
+
|
|
567
|
+
def _ClearTestState(self):
|
|
568
|
+
self._testParams=None
|
|
569
|
+
self._testDataIdxs=None
|
|
570
|
+
self._testGlobals={}
|
|
571
|
+
self._testLocalZs={}
|
|
572
|
+
self._testCholesky=None
|
|
573
|
+
self._testModelOut=None
|
|
574
|
+
|
|
575
|
+
def _ClearJAX(self):
|
|
576
|
+
jax.clear_caches()
|
|
577
|
+
gc.collect()
|