Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ To release a new version (e.g. from `1.0.0` -> `2.0.0`):

## [Unreleased]

### Breaking Changes
* Rename `max_rhat` attribute to `max_r_hat` in `ConvergenceCheckResult` and related `constants`, `analyzer` methods, and dictionary keys, improving naming consistency with proto schemas.

* Implement `reconstruction_batch_size` to chunk posterior reconstruction evaluations, fixing peak resource exhausted memory crashes on wide modeling pipelines.
* Update `tensorflow`, `tf-keras`, and `tensorflow[and-cuda]` dependencies to `>= 2.21.0, < 2.22` to address CVE-2026-2492.
* Fix `AttributeError: 'Dataset' object has no attribute 'roi_m'` when
Expand Down
4 changes: 2 additions & 2 deletions meridian/analysis/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3324,7 +3324,7 @@ def rhat_summary(self, bad_rhat_threshold: float = 1.2) -> pd.DataFrame:

* `n_params`: The number of respective parameters in the model.
* `avg_rhat`: The average R-hat value for the respective parameter.
* `max_rhat`: The maximum R-hat value for the respective parameter.
* `max_r_hat`: The maximum R-hat value for the respective parameter.
* `percent_bad_rhat`: The percentage of R-hat values for the respective
parameter that are greater than `bad_rhat_threshold`.
* `row_idx_bad_rhat`: The row indices of the R-hat values that are
Expand Down Expand Up @@ -3368,7 +3368,7 @@ def rhat_summary(self, bad_rhat_threshold: float = 1.2) -> pd.DataFrame:
constants.PARAM: param,
constants.N_PARAMS: np.prod(rhat[param].shape),
constants.AVG_RHAT: np.nanmean(rhat[param]), # pyrefly: ignore[no-matching-overload]
constants.MAX_RHAT: np.nanmax(rhat[param]), # pyrefly: ignore[no-matching-overload]
constants.MAX_R_HAT: np.nanmax(rhat[param]), # pyrefly: ignore[no-matching-overload]
constants.PERCENT_BAD_RHAT: np.nanmean(
rhat[param] > bad_rhat_threshold # pyrefly: ignore[unsupported-operation]
),
Expand Down
16 changes: 8 additions & 8 deletions meridian/analysis/review/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,41 +135,41 @@ def run(self) -> results.ConvergenceCheckResult:
rhats = self._analyzer.get_rhat()
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=RuntimeWarning)
max_rhats = {k: np.nanmax(v) for k, v in rhats.items()} # pyrefly: ignore[no-matching-overload]
max_r_hats = {k: np.nanmax(v) for k, v in rhats.items()} # pyrefly: ignore[no-matching-overload]

valid_rhat_items = [
item for item in max_rhats.items() if not np.isnan(item[1])
item for item in max_r_hats.items() if not np.isnan(item[1])
]
if not valid_rhat_items:
return results.ConvergenceCheckResult(
case=results.ConvergenceCases.CONVERGED,
config=self._config,
max_rhat=np.nan,
max_r_hat=np.nan,
max_parameter=np.nan, # pyrefly: ignore[bad-argument-type]
)

max_parameter, max_rhat = max(max_rhats.items(), key=lambda item: item[1])
max_parameter, max_r_hat = max(max_r_hats.items(), key=lambda item: item[1])

# Case 1: Converged.
if max_rhat < self._config.convergence_threshold:
if max_r_hat < self._config.convergence_threshold:
case = results.ConvergenceCases.CONVERGED

# Case 2: Not fully converged, but potentially acceptable.
elif (
self._config.convergence_threshold
<= max_rhat
<= max_r_hat
< self._config.not_fully_convergence_threshold
):
case = results.ConvergenceCases.NOT_FULLY_CONVERGED

# Case 3: Not converged and unacceptable.
else: # max_rhat >= divergence_threshold
else: # max_r_hat >= divergence_threshold
case = results.ConvergenceCases.NOT_CONVERGED

return results.ConvergenceCheckResult(
case=case,
config=self._config,
max_rhat=max_rhat,
max_r_hat=max_r_hat,
max_parameter=max_parameter,
)

Expand Down
5 changes: 2 additions & 3 deletions meridian/analysis/review/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,15 +165,14 @@ class ConvergenceCheckResult(CheckResult):

case: ConvergenceCases
config: configs.ConvergenceConfig
# TODO: Rename to max_r_hat.
max_rhat: float
max_r_hat: float
max_parameter: str

@property
def details(self) -> Mapping[str, Any]:
"""The check result details."""
return {
constants.RHAT: self.max_rhat,
constants.RHAT: self.max_r_hat,
constants.PARAMETER: self.max_parameter,
constants.CONVERGENCE_THRESHOLD: self.config.convergence_threshold,
}
Expand Down
8 changes: 4 additions & 4 deletions meridian/analysis/review/results_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def test_convergence_check_result_converged(self):
result = results.ConvergenceCheckResult(
case=results.ConvergenceCases.CONVERGED,
config=config,
max_rhat=1.0,
max_r_hat=1.0,
max_parameter="mock_var",
)
self.assertEqual(result.case.status, results.Status.PASS)
Expand All @@ -49,7 +49,7 @@ def test_convergence_check_result_needs_review(self):
result = results.ConvergenceCheckResult(
case=results.ConvergenceCases.NOT_FULLY_CONVERGED,
config=config,
max_rhat=3.0,
max_r_hat=3.0,
max_parameter="mock_var",
)
self.assertEqual(result.case.status, results.Status.FAIL)
Expand All @@ -65,7 +65,7 @@ def test_convergence_check_result_not_converged(self):
result = results.ConvergenceCheckResult(
case=results.ConvergenceCases.NOT_CONVERGED,
config=config,
max_rhat=11.0,
max_r_hat=11.0,
max_parameter="mock_var",
)
self.assertEqual(result.case.status, results.Status.FAIL)
Expand Down Expand Up @@ -381,7 +381,7 @@ def test_review_summary_repr(self):
mock_result = results.ConvergenceCheckResult(
case=results.ConvergenceCases.CONVERGED,
config=configs.ConvergenceConfig(),
max_rhat=1.0,
max_r_hat=1.0,
max_parameter="mock_var",
)
summary = results.ReviewSummary(
Expand Down
4 changes: 2 additions & 2 deletions meridian/analysis/visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,9 @@ def plot_rhat_boxplot(self) -> alt.Chart:

# If the MCMC sampling fails, the r-hat value calculated will be very large.
if (rhat[c.RHAT] > 1e10).any():
max_rhat = max(rhat[c.RHAT])
max_r_hat = max(rhat[c.RHAT])
raise model.MCMCSamplingError(
f'MCMC sampling failed with a maximum R-hat value of {max_rhat}.'
f'MCMC sampling failed with a maximum R-hat value of {max_r_hat}.'
)

# Drop any parameters with a deterministic prior, such as slope_m, which
Expand Down
2 changes: 1 addition & 1 deletion meridian/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@
PARAM = 'param'
N_PARAMS = 'n_params'
AVG_RHAT = 'avg_rhat'
MAX_RHAT = 'max_rhat'
MAX_R_HAT = 'max_r_hat'
PERCENT_BAD_RHAT = 'percent_bad_rhat'
ROW_IDX_BAD_RHAT = 'row_idx_bad_rhat'
COL_IDX_BAD_RHAT = 'col_idx_bad_rhat'
Expand Down
4 changes: 2 additions & 2 deletions meridian/model/model_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@
config=mock.create_autospec(
results.configs.ConvergenceConfig, spec_set=True
),
max_rhat=1.5,
max_r_hat=1.5,
max_parameter="beta",
)
CONVERGENCE_CHECK_RESULT_CONVERGED = results.ConvergenceCheckResult(
case=results.ConvergenceCases.CONVERGED,
config=mock.create_autospec(
results.configs.ConvergenceConfig, spec_set=True
),
max_rhat=1.01,
max_r_hat=1.01,
max_parameter="beta",
)
BASELINE_CHECK_RESULT_PASS = results.BaselineCheckResult(
Expand Down
Loading