Skip to content

mpt(): design-fixed covariates and impossible categories (MPT 3/6, #255) - #486

Merged
GidonFrischkorn merged 66 commits into
developfrom
255-mpt-design
Oct 14, 2026
Merged

GidonFrischkorn merged 66 commits into
developfrom
255-mpt-design

Conversation

@GidonFrischkorn

@GidonFrischkorn GidonFrischkorn commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

Part of #255.

Base branch: 255-mpt-constraints (stack rebased on develop 6f04c69, 2026-10-04; constraints fffa619 merged in, which carries core 4dfa85d and develop 186250e; develop 222a7ef merged in 2026-10-08; head 2b8c7de, 2026-10-09). PR 3 of 6 in the MPT stack. 20 files, +2244/-204.

What this adds

  • mpt(covariates = ): numeric data columns used in branch expressions, such as design-fixed guessing rates. dmpt() and rmpt() take covariate values too.
  • mpt_tree(impossible = ): response categories a tree cannot produce, such as distractor responses without distractors.
  • pp_check() and conditional_effects() skip the Poss_ columns the model generates (by name, so a user column that happens to start with Poss_ stays visible); revert_check_data.mpt() drops them. The overview table lists covariates.

Design decisions

  • Row-wise branch checks. mpt() cannot know the covariate values, so for trees that use covariates it leaves the sum check to check_data(); a tree that uses no covariate is decided at construction instead, at the interior points and the two corners, so that declaring a covariate elsewhere can no longer make mpt() accept a tree that check_data() then refuses. check_data.mpt() checks every row of a covariate tree with its observed values, at the five interior parameter test points of the construction check (a golden-ratio sequence, so every parameter gets its own value; one symmetric point would hide a swapped D / 1 - D) and at two near-boundary points with every parameter at 0.001 and at 0.999 (simplex groups at a corner), where a covariate slightly outside [0, 1] first pushes a branch out of (0, 1]. It stops when a tree's branches do not sum to 1 on its rows; when a branch is ≤ 0 or > 1 at an interior point, or above 1 or below -1e-6 at a corner (a residual branch written as 1 minus the others cancels to just below 0 there — -8.5e-20 for seven stages — so the corners tolerate rounding; a branch that is exactly 0 at an interior point is pointed to impossible); when a branch is NaN at a test point, naming the category and the values; when a branch of one tree is not finite on another tree's rows (brms evaluates every tree's term Idx_<tree> * (branch) on every row, e.g. 1/ss with ss = 0 where the other tree has no set size); and when a covariate that some branch uses is NA (the tree's branches cannot be computed, or brms would silently drop the row; the message says what to fill in). Messages name the tree, the category, the first row and only the covariates that tree uses.
  • Impossible categories reuse the m3 device. The category is dropped from that tree, whose remaining branches must sum to 1. check_data() builds a per-row indicator, and bmf2bf.mpt() writes Poss_dist * log(dist) + (1 - Poss_dist) * (-100), as m3 does for empty categories. Per row, because a category can be impossible in some trees and reachable in others. The refcat = NA multinomial renormalizes, so no branch gives up probability; the leak is exp(-100) = 3.7e-44, and 1 + exp(-100) == 1 in double precision.
  • Guards. A category impossible in every tree and a non-zero count in an impossible cell are errors. NA counts read as 0, and do not warn in cells the row's tree cannot produce.

Validation

  • Oberauer (2019), Exp. 1, vs the author's JAGS model (uses covariates): all four estimates inside the JAGS 95% CIs. meanPi 2.232 vs 2.420 [1.617, 3.436]; meanPb 2.437 vs 2.459; dPi -0.176 vs -0.236; dPb -0.592 vs -0.596. P(dPb < 0) = 1.000, max Rhat 1.0057.

  • MPT vs m3 on oberauer_lewandowsky_2019_e1 (with impossible): max difference of posterior means over seven quantities 0.034 (criterion < 0.05; 0.031 on 2026-09-04).

  • update() on a two-tree fit with an impossible category: identical standata. The validation script's FAIL verdict comes from its extra stancode comparison: update() writes the version header twice, which mixture2p shows too, so it is develop behaviour. The review of 2026-10-05 repeated this with an impossible category, two covariates, a factor sharing one tree and the tree column absent from the formulas: standata identical for update(fit, iter = ) and update(fit, formula. = ), log_lik() equal to a hand computation to 5.7e-14, no predicted counts in impossible cells.

  • impossible against the epsilon device it replaced (decided, D4; fresh fits on a git archive snapshot). The epsilon device is the article's own before impossible existed: dist = "(1 - Pi) * 0.001", npl = "(1 - Pi) * (10/15 - 0.001)" in the no-distractor tree. Same OL2019 model, formula and seed otherwise; fitted in the order impossible, epsilon, impossible, epsilon with seeds 2611 and 2612. Epsilon does not do worse:

    fit divergences max Rhat min bulk ESS sampling (slowest chain)
    impossible, seed 2611 0 1.0086 440 16.3 s
    epsilon, seed 2611 0 1.0088 548 13.5 s
    impossible, seed 2612 0 1.0048 709 12.8 s
    epsilon, seed 2612 0 1.0080 695 13.5 s

    Posterior means of the 14 population effects and SDs differ between the devices by at most 0.0216 logits (seed 2611, b_Pb_condoldsame) and 0.0125 (seed 2612, b_Pi_condoldsame), at most 0.0045 and 0.0016 on the probability scale. Refitting the same device with the other seed moves the means by up to 0.0138 (impossible) and 0.0125 (epsilon). 11 of the 14 differences keep their sign across the two seeds, as expected for two slightly different likelihoods fitted to the same data; all but one (0.0216, b_Pb_condoldsame, seed 2611) stay within the seed-to-seed band, and the 95% CrIs overlap almost entirely. So the case for impossible is that it states the structure and needs no compensating branch, not that it fits better.

Since 2026-10-08 (develop sync 2, open items)

  • Constraints 0ecaaaf merged in (2b8c7de). One conflict, the restriction-constant message: constraints' new wording with this level's remedy of a covariate column appended, in the order of the tree-level message. Both remedies give valid Stan.
  • The count line "More free parameters than degrees of freedom in one design cell" for models with covariates stays as is (GF, 2026-10-09).

Gates on 255-mpt-design (2b8c7de)

  • Focus tests, filter mpt|distributions|conditional|pp_check|update|data_check|helpers-prior|default-priors: 0 failed, 0 errors, 0 skipped, 2341 passed.
  • check() on an rsync copy: Status OK, 0 errors, 0 warnings, 0 notes.
  • Stancode of 2HTM, PCM and OL2019 identical, and print() of the seven reference models identical except the parameter_info() line of e7c8504, against d309b8a.
  • document(): no drift. git merge-tree against develop 222a7ef: clean.

Changes since the review fixes of 2026-10-05

  • Restriction constants of 0 or 1. The message claimed that such a constant turns a branch into the constant 0, which is false for D = 0 in the 2HTM. It now says the constant must lie strictly between 0 and 1 and points to the reduced tree with mpt_tree(impossible = ) (dd78044).
  • Identifiability check of PR 1, merged in, with covariates. The rank treated a covariate as a free parameter and took the maximum over single points, so g * x in a 2HTM and x * a + (1 - x) * b got false warnings. Covariates are now data. In bmm() the rank is taken over every distinct finite covariate setting in the data (no cap; see the fix-round section below for how the memory is bounded), and over five test values of the covariates when printing (38b6125, 624b1d5). With predictors and covariate data, the message names the parameters itself, because print() takes the covariates at test values. An independent check agreed with the printed rank on 13 of 13 models and found no crash on bad covariate input; check_data()'s own error comes first (stack_cleanup_2026-10-06/verify/round2/).

Review focus

  • The -100 device and the renormalization argument.
  • The row-wise checks in .mpt_validate_covariate_sums() (sums and branch range at five interior and two near-boundary parameter points, finiteness of each tree's branches on the other trees' rows, NA covariates). Not covered by design: branches that mix complements of several parameters ((1 - D) + D * d + D * (1 - d) * G) can still leave (0, 1] at a mixed corner; checking those would need 2n more points.
  • Deviations: the tree-column tests live here, with the four-category m3 test, because they use the impossible fixture. .mpt_tree_branch_errors() (renamed from .mpt_tree_sum_deviations() by PR 1) gains covariates and shares .mpt_test_points() with the data check. In dmpt() and rmpt(), covariates comes after log and unpack (4d4b63b). The test values are a golden-ratio sequence instead of a four-value cycle, so mpt() now also rejects a tree with five or more parameters whose typo equates parameters four places apart (accepted at 06aefe8); this also applies to models without covariates; PR 1 now uses the same sequence (1a7a242). .mpt_boundary_points() supplies the two corner points of the data check. A branch (1 - D)^n with n above about 370 underflows to 0 at an interior test point and is rejected.
  • .mpt_covariate_settings(): which covariate values enter the rank, and the fall-back to test values.

Decided 2026-10-06, implemented

  • mpt() no longer warns on covariate trees whose sum at the test values is not 1; check_data() decides for them (item 2, 0fe3dff).
  • dmpt() takes log and rmpt() takes unpack right after tree, before covariates, as dm3() and rm3() do (item 9a, 4d4b63b). A name given twice across pars, covariates and ... is an error (item 9b, cca2c8b, e6fc5dd).
  • A tree without rows still gets the finiteness check on the other trees' rows (item 10, 3b4f7b9).
  • ?mpt says which covariate checks stop bmm() and that posterior_predict(newdata = ) needs nTrials, Idx_<tree> and Poss_<category> (items 9c, 12, c12a086); a comment notes the accepted (1 - D)^n underflow (item 11, 1b034b9).

Changes since the re-review of 2026-10-09 (7678b20 to 81e65c4, 18 commits)

An internal review (2026-10-09) gave the verdict MERGE AFTER FIXES; four fix rounds, each round's tests mutation-checked, five independent reads. The covariate stacking and the cap removal held under review: an independent central-difference rank matched bmm on all 15 covariate designs.

  • The 20-settings cap is gone (a50ce19). Every distinct covariate setting is ranked, so a design that needs more than 20 of them to be identified is no longer under-ranked: a Bernstein-polynomial tree with 61 distinct settings reaches rank 3 of 3 and check_model() is correctly silent, while the same tree restricted to x in {0, 1} is rank 2 of 3 and the deficit is reported (an independent central-difference rank agrees in both). The first version of the lift cost 346 ms at 10 000 settings, so each derivative is now evaluated once over all settings instead of per setting: 12 ms.
  • Memory is bounded without a cap (83e20be). The stacked blocks enter a QR decomposition 2000 settings at a time and only the R factor is kept, which leaves the rank, the zero columns and the null space exactly as for the full stack. At 10 categories and 100 000 settings, extra peak memory drops from ~1074 MB to ~79 MB with the rank unchanged at 9 of 9, measured in interleaved runs.
  • Trees without rows drop out of the rank whether or not covariates are used (8b4f6ac). A 2HTM with shared D and g and only old rows passed silently while the same likelihood written g * x warned. When every tree or no tree has rows, the model rank stands; the message says the rank was taken in the trees with rows.
  • A non-finite derivative no longer loses the whole rank (721a814, ee36914, cd3457c). A cusp at one test value, or at one covariate value in the data, used to stop check_model() with LINPACK's "NA/NaN/Inf in foreign function call"; such values are now left out and the text says how many. The rank is reported as not computed only when fewer than two test values are finite, because a single remaining point can sit on a singular set and raise a false warning. Without a rank, bmm() reports the count bound and states the reason instead of pointing at print(), which cannot give a reason that comes from the data.
  • Corner rounding is accepted, NaN branches are refused by name (db8c6b1). See the row-wise checks above.
  • Deficit texts that use the data (8d4f557, fa36a83, d36d31e, 5c0d23d). With predictors, check_model() said "The model is not identified" and then that the predictors were not checked; it now says the tree parameters are not identified with intercept-only formulas. A parameter that only trees without rows contain is named as left open by the data. ?mpt says that print() and bmm() can disagree in either direction, and that the two read covariate values from different places.
  • Tests and docs: the maximum over test points is pinned (974685a), test points are spelled in hex (e1724bc), internal helpers are called through bmm::: (acc6965).

Changes from the merge of 2026-10-07 (2e346a7)

255-mpt-constraints at 84d05ac, which carries core's fix round 2, merged in; seven conflict hunks in four files, all resolved. What changes behaviour at this level:

  • The rank-deficit warning is narrowed (core 0f9f9e9, 00fb817). A deficit is announced rather than warned about only when a formula has a data predictor, or when a non-linear formula defines or reads one of the deficit's own parameters. An unrelated h ~ g next to a D * r deficit warns again, where before any non-linear formula anywhere downgraded every deficit. Where the rank could not be computed there is no set of involved parameters, so the narrowing is suspended there and any non-linear formula still counts.
  • A simplex member that a non-linear formula reads also marks its group's stick components as reached, so a deficit in the sticks is announced, not warned about (constraints 83e27b4).
  • The scientific-constant message gives a rewrite that fits the magnitude (p / 100000 as p / 1000 / 100 for large constants), shows brms's form only where it differs from the written one, and keeps this level's remedy of declaring the value as a covariate column (core 1f2e416).
  • A response count on the right-hand side of a formula is refused up front instead of failing later in brms with "Cannot handle circular dependency structures" (core 8a1b54b); the sub-parameter message now says identification is not checked (8339e91); fixing a simplex member names the remedy (84d05ac).

Update 2026-10-10: bmm() no longer ranks the data (35a19c9)

Core's two-rule messaging is merged in (see the update on #484). The rank in bmm() is now the same rank print() reports. For models with covariates both use five test values of each covariate, and bmm() does not read the data for identifiability. This supersedes the passages on covariate settings taken from the data, the trees-without-rows variant, the deficit texts that use the data, and the narrowed rank-deficit warning.

Why. I logged the model rank next to the data rank for every call with data in the MPT test file (110 calls) and for the article's models (8 calls). The data rank never exceeded the model rank. In 91 of the 110 test calls the two agreed. In 9 the data rank was lower (a tree with no rows in the data, or a covariate with one distinct value), and in 4 only the data rank could not be computed. bmm() now stays silent in those 13 cases. That is a decided trade: only the data rank saw them, and it needed the covariate-settings code to do so.

  • Removed: the data branch of .mpt_covariate_settings(), the data argument of .mpt_identifiability(), the trees-without-rows handling, and the data-specific wording. The Jacobian rank computation is unchanged.
  • Identifiability code in R/model_mpt.R goes from 382 to 211 lines.
  • The identifiability paragraph of ?mpt shrinks from 48 lines to 12 lines of prose plus a 4-line example.
  • Tests: every rank-value test is kept, including the Bernstein case, which reaches rank 3 at five test values. The tests of the removed behaviours are removed or rewritten, and one test documents that bmm() ignores the data.
  • Verified before a roxygen-only trim of ?mpt: NOT_CRAN=true devtools::test() FAIL 0, PASS 7371; devtools::check() 0 errors, 0 warnings, 0 notes. Filtered MPT tests pass after the trim (370). The same code is tested in full at the head of MPT article, ppp(), mpt_data_reshape() and mpt() API changes (MPT 6/6, #255) #489.

Update 2026-10-13: develop through #546 carried up, MPT fits in tests/local (head 084eca2)

This update replaces the "Gates" section above, if any.

Measured at this head: document() no drift. The full suite and R CMD check were run at the tip of the stack (#489).

CI on this head: R-CMD-check passes on macOS, Ubuntu and Windows (Windows check step 11 minutes), tests-source passes within the time budget (suite 439 s), and file-size passes.

Closes #528.

mpt() and dmpt()/rmpt() gain covariates = (data columns used in branch expressions, with a row-wise branch-sum check in check_data). mpt_tree() gains impossible =: Poss_ row indicators and a -100 linear predictor switch a category off for the trees that cannot produce it; pp_check() and conditional_effects() skip the Poss_ columns, and update() drops them before rebuilding the data.
…and impossible categories (#255)

The core now errors on trees whose branches do not sum to 1 and warns on missing
counts. Under covariates, a tree such as Gcorr / Gother sums to 1 only with the
observed values, so mpt() still warns for trees that use covariates and the
row-wise check in check_data() now errors instead of warning. Under impossible
categories, a missing count in a category the row's tree cannot produce is a
known 0 (oberauer_lewandowsky_2019_e1 records dist as NA in its two
no-distractor conditions), so it no longer counts toward the warning.
@GidonFrischkorn GidonFrischkorn added this to the 1.4.0 milestone Oct 4, 2026
The restriction-constant error said impossible branches are not
supported; at this level mpt_tree(impossible = ) exists, so the message
points to it.
check_data() evaluated the branch sums of covariate trees with every
parameter at 0.5, where D and 1 - D coincide, so a swapped complement
passed and fitted to a wrong value. The data check now shares the
construction check's test points. It also stops when a branch is <= 0
or > 1 for a row (a zero branch points to mpt_tree(impossible = )) and
when a covariate is NA in a row of a tree that does not use it, which
brms would otherwise drop.
The ^Poss_ prefix filters in pp_check() and conditional_effects() also
hid user predictors whose names start with Poss_, in any model. Both
now drop the names in the model's possibility indicators.
…checks (#255)

The four test points cycled four values, so parameters four places apart
always took the same value and a typo between them passed. Each symbol
now gets its own value from a golden-ratio sequence. check_data() also
checks branch ranges with all parameters near 0 and near 1, where a
covariate slightly below 0 first pushes a branch out of (0, 1]. An NA
covariate is reported as missing before the sum check, and only for
covariates some branch uses.
…he tree's covariates (#255)

brms evaluates every tree's branch term on every row, so a branch that
divides by a covariate equal to 0 in another tree's rows made the
likelihood NaN; check_data() now stops there and the NA advice no longer
says any value works. Branches that underflow to 0 at the near-boundary
points pass. Sum and range messages list only the covariates the tree
uses, and point covariate-free trees to their branch expressions.
# Conflicts:
#	R/model_mpt_expressions.R
#	tests/testthat/test-model_mpt_expressions.R
The message claimed that 0 or 1 turns a branch into the constant 0, which is
false for D = 0 in the 2HTM. It now says the constant must lie strictly
between 0 and 1 and points to the reduced tree with mpt_tree(impossible = ).
#255)

Models whose branches use covariates got a false identifiability warning (2HTM
with g * x, or x * a + (1 - x) * b), and a covariate could be listed as a free
parameter. Covariates are now data, and the rank is taken over the covariate
values in the data (five test values when printing). The count line says that
it holds per design cell when a tree uses covariates.
… the parameters (#255)

A non-finite or missing covariate value gave a spurious identifiability warning
before check_data()'s own error, and a tree without rows still counted when it
used no covariates. With predictors and covariate data, the message pointed to
print(), which takes the covariates at test values; it now names the
parameters and says at which values the derivatives vanish.
# Conflicts:
#	R/model_mpt.R
#	tests/testthat/test-model_mpt.R
…() (#255)

mpt() warned that a valid tree such as D + (1 - D) * Gcorr, (1 - D) * Gother did not sum to 1, because synthetic covariate values need not satisfy Gcorr + Gother = 1. It is now silent for trees that use covariates; check_data() still stops when the observed values do not sum to 1.
…s' rows (#255)

When the data had no rows for a tree, check_data() skipped that tree entirely, so a branch such as (1 - D) / ss with ss = 0 in the other trees' rows passed and the fit failed in Stan. check_data() now stops with the not-finite message; only the checks on the tree's own rows are skipped.
…255)

dmpt(x, pars, model, tree, FALSE) bound FALSE to covariates and silently returned the log density. log (dmpt) and unpack (rmpt) now come right after tree, as in dm3() and rm3(); covariates must be named.
A symbol given in two of pars, covariates and the further arguments silently took the first value. dmpt() and rmpt() now stop and name the duplicated symbols.
…eck (#255)

A valid branch (1 - D)^n underflows to 0 at the interior test point D = 0.85 from n = 392, and check_data() then refuses the data. Such sizes are accepted as unrealistic; the comment at the range test says so.
…ds (#255)

?mpt did not say when the covariate values in the data stop bmm(), nor that posterior_predict(newdata = ) needs the columns check_data() builds. Two sentences now cover both.
…rmpt() (#255)

Unnamed values in the further arguments were reported as "The value(s) of '' are given more than once". The check now looks at named values only.
The 20-setting spread could drop the one setting that identifies a
parameter and report a false 'not identified' warning. A subset can
only lower the rank, so every distinct setting is used. The settings
are evaluated in one vectorized pass per tree, so 10 000 distinct
settings take 12 ms in check_model().
Brings in core's fix round 2 (0f9f9e9 .. 8dc5e4c) through constraints, plus
constraints' own 83e27b4 simplex half and 84d05ac docs. Seven conflict hunks
in four files; in each one design's side carried the later text from the #486
fix rounds (corner test points, left-out non-finite derivatives, the chunked
QR, from_data) and constraints' side carried core's round 2.

- NEWS.md: both design bullets kept, the restrictions bullet taking
  constraints' "file of equalities and constants".
- mpt_tree()'s scientific-constant message: core's per-magnitude rescaling
  example and its paste() of the now pre-quoted .mpt_written_constants(),
  with design's covariates-column remedy appended.
- the @details identifiability paragraph: core's narrowing rule on top of
  design's sentences about which parameters are named, left-out test values
  and the count bound; man/mpt.Rd regenerated, not hand-edited.
- check_model.mpt(): design's structure (count_only, one unchecked, lead,
  from_data, .mpt_rank_deficit_text()) with constraints' reached and its
  simplex-stick loop grafted in. Design's nl_pars clause of unchecked is now
  gated on the deficit's own parameters being reached, which is core's F1
  fix; without a rank there is no involved set, so any non-linear formula
  still counts there.
- .mpt_tree_branch_errors()'s header: design's corner comment with core's
  concrete underflow example.

One change beyond the merge: the large-constant expectation in
test-model_mpt_expressions.R anchored the message's end at core's rescaling
example, which is no longer last now that the covariates remedy follows it;
the expectation matches the full ending instead.
…255)

One conflict, the restriction-constant message: constraints' new wording
with design's covariate remedy appended, in the order of design's tree
message. Probe: both remedies give valid Stan.
# Conflicts:
#	NEWS.md
#	tests/testthat/test-model_mpt.R
Take the simplified identifiability messaging from the core branch. The
rank is taken at interior test values and, for models with covariates, at
five test values of each covariate; bmm() no longer ranks the data, so the
per-row covariate-setting rank, the trees-without-rows variant and the
per-case wording are removed.
The range check visited five interior points and two corners, so a branch
that mixes a parameter with another one's complement (a * (1 - b) * G, or
2 * a * (1 - b) without covariates) could leave (0, 1] at a vertex that
none of them reaches.

Each branch is now also evaluated at the 2^k vertices of its own k
parameters, in check_data() for trees with covariates and in mpt() for
trees without. That is exact for branches multilinear in their parameters.
A parameter of higher degree, such as u in u * (1 - u), also takes 0.5; a
simplex group is one dimension whose vertices put the mass on one member.
A branch with more than 1024 grid points falls back to the two corners
plus 64 fixed pseudo-random vertices, and one message names the branches
that were only sampled. Errors name the vertex at which the branch leaves
(0, 1].
)

The vertex grid sat at 0.001 and 0.999, so a multilinear branch was seen
at (0.999)^k of its extreme: a * (1 - b) * G with G = 1.0015 reaches
1.0015 as a -> 1 and b -> 0 but showed 0.9995 and passed, in check_data()
and, without covariates, in mpt(). The grid now sits 1e-9 inside the box,
far below the check's tolerance of 1e-6, and the errors name the vertex
as 0 and 1. The tests of the vertex check pin the new grid and messages.
?mpt promised that bmm() stops whenever a branch leaves (0, 1]. The
vertex check is exact for branches in which no term contains a parameter
twice, and for a factor u * (1 - u). A branch such as 7 * u * (1 - u)^2
peaks at 1.037 at u = 1/3 and passes without a message. The covariates
argument now says where the check is exact and where it is approximate.
@GidonFrischkorn

Copy link
Copy Markdown
Collaborator Author

Update 2026-10-12: review fixes landed, constraints merged (head e709186)

An independent review of the #528 range check (branches of covariate trees must stay in (0, 1] for every row) derived the multilinearity argument and probed the implementation. Two fixes landed:

  • The vertices were evaluated at 0.001 and 0.999, so a multilinear branch was checked at (1 − ε)^k of its extreme: a * (1 - b) * G with G = 1.0015 passed although it reaches 1.0015 as a → 1, b → 0. The check now evaluates at the true vertices of the parameter box (inside the open box by 1e-9) and refuses these cases, naming the vertex.
  • ?mpt said bmm() stops when a branch leaves (0, 1], without qualification. It now says where the check is exact (multilinear branches, and degree-2 terms such as u * (1 - u) through the midpoint grid) and that higher-degree terms are checked approximately.

The level is merged with the current constraints (#485). The merge met one conflict, the identifiability paragraph of ?mpt, which both lower levels had rewritten; the two texts are combined and man/mpt.Rd regenerated with roxygen. Measured at this head: parse, document() without drift, byte-identical Stan code for the eight reference models before and after the merge, and the focused test files with no failure.

test-conventions.R (R8) bars new set.seed() calls; local_seed() gives
the same seed and restores the RNG state after the test.
# Conflicts:
#	tests/testthat/test-model_mpt.R
Base automatically changed from 255-mpt-constraints to develop October 14, 2026 05:01
@GidonFrischkorn
GidonFrischkorn merged commit 2c235f9 into develop Oct 14, 2026
7 of 8 checks passed
@GidonFrischkorn
GidonFrischkorn deleted the 255-mpt-design branch October 14, 2026 06:06
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.

mpt(): the range check of covariate branches passes branches that mix a parameter with another's complement

1 participant