model_comparison
Compare fits of the same data with KS tests on their posteriors and the AIC.
The AIC uses the aic.yaml each fit writes (witch.fitter.aic_refit): the maximum likelihood
under one noise model shared by every fit of the same data, the one estimated from the raw
data before any model subtraction, so the chi-squared of different models are comparable.
Runs from before it was added have no aic.yaml and need refitting to be compared.
Two levels of checks guard the comparisons:
* comparable (check_comparable, needed for the AIC): same data and likelihood configuration,
same structures and parameter ties, and parameters fixed in both runs have the same value.
Raises ComparabilityError.
* nested (check_nested, needed for the KS tests): comparable, plus one run's free parameters
are a strict subset of the other's and its fixed values lie inside the other's priors.
Raises NestingError (a ComparabilityError).
ChainQualityError is raised when a chain is too short or too correlated for a KS test.
ChainQualityError
Bases: ValueError
Raised when an MCMC chain can't support a KS test.
Source code in witch/model_comparison.py
43 44 | |
ComparabilityError
Bases: ValueError
Raised when two runs are not fits of the same model family to the same data.
Source code in witch/model_comparison.py
35 36 | |
NestingError
Bases: ComparabilityError
Raised when two runs are not nested models of the same data.
Source code in witch/model_comparison.py
39 40 | |
Run
dataclass
A finished WITCH run loaded from its output directory.
Attributes:
| Name | Type | Description |
|---|---|---|
label |
str
|
Short name for the run. |
outdir |
str
|
The output directory of the run. |
cfg |
dict
|
The fully merged config the run used ( |
names |
list[str]
|
Fully qualified parameter names ( |
structures |
dict[str, str]
|
Map of |
values |
ndarray
|
Parameter values the run was set up with, fixed parameters keep these. |
priors |
ndarray
|
|
free |
ndarray
|
Boolean mask of the parameters sampled by the MCMC. |
chains |
ndarray
|
|
parameter_map |
tuple
|
The |
aic |
dict | None
|
Contents of the run's |
Source code in witch/model_comparison.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | |
free_names
property
Names of the free parameters, in the order of the chain columns.
k
property
Number of free parameters.
medians
property
Full parameter vector with the free parameters at their posterior medians.
aic_chisqs(runs, which='refit')
The chi-squared of each run from its aic.yaml.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
runs
|
list[Run]
|
Runs loaded with |
required |
which
|
str
|
"refit" for the maximum likelihood (what the AIC is defined at) or "start" for the starting point (MCMC medians, or the LM best fit). |
"refit"
|
Returns:
| Name | Type | Description |
|---|---|---|
chisqs |
list[float]
|
Chi-squared of each run, in the order of |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If a run has no |
ComparabilityError
|
If an |
Source code in witch/model_comparison.py
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 | |
aic_table(runs, chisqs)
AIC of each run, chisq + 2k, with the difference to the best run and Akaike weights.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
runs
|
list[Run]
|
Comparable runs, checked with |
required |
chisqs
|
list[float]
|
Chi-squared of each run against the same data and noise,
ideally at each run's maximum likelihood ( |
required |
Returns:
| Name | Type | Description |
|---|---|---|
rows |
list[dict]
|
One row per run, in the order of |
Raises:
| Type | Description |
|---|---|
ComparabilityError
|
If the runs are not comparable. |
Source code in witch/model_comparison.py
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 | |
chain_diagnostics(run)
Convergence diagnostics of each free parameter of a run.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
run
|
Run
|
The run to check. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
rows |
list[dict]
|
Per parameter effective sample size, autocorrelation time in steps (total samples / ESS), and split R-hat. |
Source code in witch/model_comparison.py
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 | |
check_comparable(a, b, rtol=1e-10)
Check that two runs fit the same data with the same family of models.
This is what the AIC needs: same data and likelihood configuration, the same model structures and parameter ties, and parameters fixed in both runs held at the same value. The runs don't have to be nested, ie: one can free alpha and the other gamma.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
a
|
Run
|
The runs to compare. |
required |
b
|
Run
|
The runs to compare. |
required |
rtol
|
float
|
Relative tolerance when comparing fixed values. |
1e-10
|
Raises:
| Type | Description |
|---|---|
ComparabilityError
|
If any of the conditions above are not met. |
Source code in witch/model_comparison.py
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | |
check_nested(small, big, rtol=1e-10)
Check that small is nested in big.
Nested here means: comparable (see check_comparable), every parameter free in small
is free in big, big has at least one more free parameter, and every parameter that
big frees but small fixes is held at a value inside big's prior so small is a
point in big's parameter space.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
small
|
Run
|
The run with fewer free parameters. |
required |
big
|
Run
|
The run with more free parameters. |
required |
rtol
|
float
|
Relative tolerance when comparing fixed values. |
1e-10
|
Returns:
| Name | Type | Description |
|---|---|---|
extra |
list[str]
|
The parameters free in |
Raises:
| Type | Description |
|---|---|
NestingError
|
If any of the conditions above are not met. |
Source code in witch/model_comparison.py
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | |
chisq_order_warnings(runs, chisqs)
Flag nested pairs where the larger model has the higher chi-squared, non-nested pairs are skipped. At the maximum likelihood this can't happen, at the posterior medians it can if a posterior is skewed or a chain is poorly converged, so it is worth a look.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
runs
|
list[Run]
|
Runs in the same order as |
required |
chisqs
|
list[float]
|
Chi-squared of each run. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
warnings |
list[str]
|
One message per offending pair. |
Source code in witch/model_comparison.py
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | |
data_signature(cfg)
The parts of a config that define the data and likelihood.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cfg
|
dict
|
A merged WITCH config. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
signature |
dict
|
Config subset that has to be identical for two runs to be comparable. |
Source code in witch/model_comparison.py
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | |
ess(chains)
Effective sample size of one parameter, summed over chains. Uses Geyer's initial positive sequence estimate of the autocorrelation time.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chains
|
ndarray
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
ess |
float
|
Effective number of independent samples. |
Source code in witch/model_comparison.py
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 | |
full_par_names(metamodel)
Get fully qualified parameter names and structure types of a metamodel.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
metamodel
|
MetaModel
|
The metamodel to name the parameters of. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
names |
list[str]
|
|
structures |
dict[str, str]
|
Map of |
Source code in witch/model_comparison.py
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
is_nested(small, big)
Whether small is nested in big, see check_nested.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
small
|
Run
|
The runs to check. |
required |
big
|
Run
|
The runs to check. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
nested |
bool
|
True if |
Source code in witch/model_comparison.py
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | |
ks_compare(small, big, min_ess=50.0)
Two-sample KS test of the shared free parameters of two nested runs. The p-value uses the effective sample sizes, since MCMC samples are correlated and the nominal KS p-value would be wildly overconfident.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
small
|
Run
|
The nested (smaller) run. |
required |
big
|
Run
|
The larger run. |
required |
min_ess
|
float
|
Minimum effective sample size per parameter for the test to be meaningful. |
50.0
|
Returns:
| Name | Type | Description |
|---|---|---|
rows |
list[dict]
|
One row per shared free parameter with the medians, KS statistic |
Raises:
| Type | Description |
|---|---|
NestingError
|
If the runs are not nested. |
ChainQualityError
|
If any shared parameter has fewer than |
Source code in witch/model_comparison.py
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 | |
load_run(outdir, label=None, burn_in=None)
Load a finished MCMC run from its output directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
outdir
|
str
|
Output directory containing |
required |
label
|
str | None
|
Short name for the run, defaults to the last two path components. |
None
|
burn_in
|
float | None
|
Fraction of each chain to drop, defaults to |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
run |
Run
|
The loaded run. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If any of the needed files are missing. |
ChainQualityError
|
If the saved chains don't line up with the saved metamodel. |
Source code in witch/model_comparison.py
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | |
nested_pairs(runs)
All (small, big) pairs of runs where small is nested in big.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
runs
|
list[Run]
|
Runs to search. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
pairs |
list[tuple[Run, Run]]
|
Nested pairs, ordered by the number of free parameters of |
Source code in witch/model_comparison.py
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | |
order_nested(runs)
Sort runs by number of free parameters and check every pair is nested.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
runs
|
list[Run]
|
Runs to compare. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ordered |
list[Run]
|
The runs from fewest to most free parameters. |
Raises:
| Type | Description |
|---|---|
NestingError
|
If any pair of runs is not nested. |
Source code in witch/model_comparison.py
344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 | |
rhat(chains)
Split R-hat (Gelman-Rubin) of one parameter. Each chain is split in half so drifts within a chain show up as well as disagreement between chains. Values above ~1.01-1.1 mean the chains have not converged to a common distribution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
chains
|
ndarray
|
|
required |
Returns:
| Name | Type | Description |
|---|---|---|
rhat |
float
|
The split R-hat. |
Source code in witch/model_comparison.py
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 | |
sort_comparable(runs)
Sort runs by number of free parameters and check every pair is comparable.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
runs
|
list[Run]
|
Runs to compare. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
ordered |
list[Run]
|
The runs from fewest to most free parameters. |
Raises:
| Type | Description |
|---|---|
ComparabilityError
|
If any pair of runs is not comparable. |
Source code in witch/model_comparison.py
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | |