Conversation
process-improve renamed the single-cell accessor to get_omars_trade_off_table_entry and the module to omars_trade_off. The figure reads every value from that function, so the script follows the rename. The rendered PNG is byte-identical, which confirms the rename changed no values.
Reads six candidate cell measures down the three-factor column of the OMARS trade-off table: the five alphabetic optimality criteria and the largest absolute correlation between two second-order terms. Each point is the best value attainable at that run count, found by listing every OMARS foldover of the size rather than by searching, so the curves are frontiers. The panels are grouped so the pairs are visible: the left column averages, the middle takes the matching worst case, the right does neither; the top row summarises X'X and the bottom row is prediction variance over the region. The five criteria are monotone in the run count at all three centre-run counts, which the correlation is not, and insets on the last panel show the correlation maps behind three of its points. The enumeration takes hours, so its output is carried in the script as literal data. Twelve of the cells were re-derived from naive numpy model matrices and agree to 5e-7. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The last panel had room for one more, and the three insets it carried all came from the one-centre-run series. The new one is the twenty-four-run design with two centre runs, at max |r| of 0.250, so the insets now sample two of the three series and the spacing across the panel is even. The inset dictionaries are keyed by centre-run count as well as run count, since a run count alone no longer identifies which design is shown. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The inset at thirteen runs did not carry its weight: it was a third example of a one-centre-run design, next to two others. Replacing it with the smallest design at each of the three centre-run counts, at nine, ten and eleven runs, turns three of the five insets into a controlled comparison. Those designs share the same four half-rows, so only the centre count separates them, and the maps show what the centre runs trade: the quadratic-to-interaction correlation falls from 0.707 to 0.645 to 0.606 while the quadratic-to-quadratic correlation rises from zero to 0.167 to 0.267. Each inset border and its leader line now take the colour of the series the design belongs to, so the reader can tell the three apart without labels. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The staircase showed run budgets from 9 to 57 with no fixed reference at either end, so a reader could not see where the standard designs sit on it. A DSD row now sits above the budgets and a Box-Behnken row below them, ruled off from the budgets and from each other, which is the span the OMARS family covers: the definitive screening design is its smallest member and the Box-Behnken design is among its largest. An anchor row is a named design rather than a budget, so its run count changes from column to column and is carried inside the cell rather than read off the row label. Both anchor rows come from the library's _reference_entry, so they cannot drift away from it any more than the budget cells can. The frontier outlines shift down a row to account for the DSD row above them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The DSD and Box-Behnken rows added in the previous commit duplicated what the table already said. A definitive screening design has 9, 13 or 17 runs, which are budgets on the table, so its row repeated those cells; it is now a DSD marker inside the cell it lands in. The Box-Behnken run counts are not budgets, so the figure adds a row for each of 15, 27, 46, 54 and 62. Those cells are marked BBD and filled in the Okabe-Ito bluish green to set them apart from the capability classes. Each column ends there: below its Box-Behnken cell every remaining row would say Full again on more runs, so those are left blank and the column closes. Reading down a column now shows the whole span of the family for that factor count, from the smallest design that exists to the standard response surface design that ends it. The frontier outlines lose the row offset they needed while a DSD row sat above them, and the figure is taller, since fourteen rows of two-line cells need the height. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The notes ran down a single column below the table and cost more vertical space than the table rows they explain. Two columns halve that: the figure is 2880 pixels tall against 3300, with the table itself unchanged. Matplotlib fills a multi-column legend column by column, so the entries are ordered to put the three capability classes on the left and the two design marks, the empty cells and the frontier outline on the right. Also drops the numpy and Line2D imports, neither of which is used since the anchor rows were replaced by in-place marks. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The note reading "19 runs, 15 parameters, model matrix rank 14" sat below the frontier, in the busiest part of the axes: its arrow crossed the parameter-count line and ran down through the definitive screening design markers to reach the marked point. It now sits above the frontier and to its left, where the axes are empty, and its arrow curves in from that side so it does not pass through the bold 21 that labels the four-factor frontier value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The leader was drawn at the default depth, so the frontier line and its shaded band covered the part of it that crossed them. It now sits above every series, and the line is thick enough to read against the shading it passes over. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The leader ran from the note down to the marked cross, which sits below the frontier, so it crossed the orange line and its shaded band to get there. It now ends at 23.5 runs, above and to the left of the line, and points towards the cross rather than touching it. The nearest approach is about five runs clear of the frontier. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
Stopping the leader at 23.5 runs left it too far short to read as pointing at anything. It curves again, and now ends at 21.4 runs, a little over one run above the frontier line at that point and close enough to the marked cross to be unambiguous, without touching the line or its shaded band. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The six existing panels answer "which measure should a cell of the trade-off table report" in the language of optimal design theory. Power is the measure a practitioner is most likely to reach for, and it was missing. The third row adds power for each of the three term types. It scores the full second-order model rather than the main-effects-and-quadratics model the six panels use, because the interactions have to be in the model for the middle panel to exist, so the row starts at the estimability frontier of 13 runs. All three curves are monotone in the run count, which puts power with the five alphabetic criteria rather than with max |r|. The row shows the ordering is main effect, then interaction, then quadratic, not what a reader would guess: at one sigma a main effect clears 0.8 power at 15 runs and an interaction at 19, while a quadratic does not reach it anywhere below 31. It also shows that centre runs cost main-effect and interaction power but buy quadratic power at the small sizes, which is a direct answer to what the extra centre runs are for. Two standard designs are now marked in every panel where they are defined. The definitive screening design sits exactly on the frontier for all six enumerated measures at its size, and is absent from the power row since 9 runs cannot fit a ten-parameter model. The Box-Behnken sits on the frontier for A, D, I, max |r| and quadratic power, and off it for E, G, main-effect power and interaction power: it spends its runs on curvature. Presentation changes that came with it: - Green and orange now belong to the two anchor designs, matching the colours they carry in the trade-off table, so the second and third centre-run series move to reddish purple and dark gold-brown. The choice was checked under simulated deuteranopia and protanopia; a deep violet was rejected because it collapses onto the blue series. - All nine panels share one x range and tick set, so a run count sits at the same position in every panel. - One frame colour for every panel. The max |r| panel used to carry a vermillion frame to mark it as the measure that is not monotone; among nine panels that reads as a highlight, and the reverses label says it in words. - A white patch behind the corner notes, which were colliding with a gridline in the I panel and with an inset leader in the max |r| panel. Companion to the OMARS designs section of the Design and Analysis of Experiments chapter in kgdunn/pid-book.
The figure carries its numbers as literals because the exhaustive enumeration behind them is far too slow to run at plot time. Until now that enumeration lived nowhere, so the literals had to be taken on trust and could not be re-derived if a definition changed. This module is where they come from. Run it with no arguments and it prints POWER_C and ANCHORS exactly as the figure spells them, ready to paste back. Run it with --verify and it re-derives a sample of the six enumerated panels the naive way, building the actual N x p model matrix for every candidate with numpy, and compares against the BEST and MAX_R literals; nothing is shared between the two paths except the definition of an OMARS foldover, so agreement is evidence. Twelve cells agree to 4.88e-07. The power enumeration is cheap for a structural reason worth recording: main effects are orthogonal to everything in an OMARS design, so their coefficient variance is 1 / (2 n_j), and everything else lives in an even block built straight from the sign-class counts. No N x p matrix is ever formed, so a run count takes seconds where the six-measure enumeration takes hours.
What a design of each size buys on the fed-batch bioreactor, in grams per litre of product at the recipe the fitted model recommends, over two hundred simulated campaigns per design. OMARS designs of 13 to 31 runs, with the 27-run Box-Behnken and face-centred central composite designs alongside. The figure carries its numbers as literals because two hundred campaigns per design take minutes; omars_worked_study_data.py regenerates them, and its --quick flag gives a twenty-seed smoke test. The reading it supports: below 27 runs the outcome is closer to a lottery than to a smaller version of the same study. The 13-run design leaves the median campaign where it started; the 17-run design gains at the median but its worst campaign loses 3.2 g/L; the 21-run design the search returned varies the feed rate in the same twelve runs as the 17-run design and finds it in only a third of campaigns. At 27 runs the worst of two hundred campaigns loses 0.35 g/L. Companion to the section "A worked OMARS study" in the Design and Analysis of Experiments chapter of kgdunn/pid-book. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The book section "A worked OMARS study" had one figure, the run-count trade-off, and was otherwise prose, code and tables. These six give each of its text-heavy subsections a picture: the current recipe and its replicate batches, the four-factor column of the OMARS trade-off table on its own, the thirty-batch plan in run order with the cassette boundary and the centre runs, the harvest titers by cassette, the full second-order model fitted in one step with the staged analysis's three terms marked, and the fitted model beside the true response with the current, recommended and best recipes on both. The study is reproduced once, in omars_worked_study_common.py, exactly as the chapter's code blocks run it, and that module asserts the values the chapter prints before any figure is drawn, so the figures cannot drift from the text. The effects script additionally checks that the terms significant at 5% in the one-step fit are the three the staged analysis selects, which the chapter states. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The four-factor column now carries its run counts and axis title above the cells, as a header. The effects and recipe figures put their legends in a lightly bordered box and move the labels that collided. The titer figure changes axis: run order showed nothing the plan grid does not, so the harvest titers are now read against the feed rate, the largest effect the study finds, one panel per cassette, with the mean at each feed level as a short bar and the centre runs starred. The stems and the heavy centre-run mean lines are gone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
The effects plot's x-axis extends further left so the legend box sits clear of the interaction intervals. The recipe figure's legend entries wrap onto two lines, so both boxes are narrower. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
…is found The book section carried a table beside this figure whose last two columns repeated the figure and whose other three, the fraction of campaigns in which the feed rate, the temperature-by-shift interaction and the temperature quadratic were declared active, had no picture. The figure now has two rows on a shared run-count axis: the gain in titer as before, and below it those three fractions as lines, with the Box-Behnken and central composite designs at 27 runs marked in each line's colour. The table can then be dropped from the section. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Parallel to pid-book #270, which adds the sections these figures illustrate. Merge this one first, so the book's PDF build can find the images.
What is added
doe/omars-metric-choice.pyand its PNGThe figure reads candidate cell measures down the three-factor column of the OMARS trade-off table: the five alphabetic optimality criteria (A, D, E, I, G), the largest absolute correlation between any two second-order terms, and, in a third row, the power to detect a main effect, a quadratic and a two-factor interaction of one noise standard deviation at the 5% level. Each point is the best value attainable at that run count, so the curves are frontiers rather than the output of a search. Three series per panel, for one, two and three centre runs, in blue, purple and dark brown (the brown chosen after checking the three remain distinct under simulated colour-vision deficiency).
Panels are grouped so the pairs are visible. Columns: the left pair averages, the middle pair takes the matching worst case, the right pair does neither. Rows: the top summarises
X'X, the middle is prediction variance over the region apart from the last panel, the bottom is power. All nine panels share one x-axis range and tick set. The Box-Behnken design (green star) and the definitive screening design (orange circle) are marked on every panel, in the colours the trade-off staircase already uses for them.Five correlation-map insets sit on the max |r| panel, on a common shading scale, each outlined and connected in the colour of its centre-run series. The three on the left are the smallest design at each centre-run count (9, 10 and 11 runs); they share the same four half-rows, so together they isolate what the centre runs do to the map: the quadratic-to-interaction correlation falls from 0.707 to 0.645 to 0.606 while the quadratic-to-quadratic correlation rises from zero to 0.167 to 0.267. The remaining two, at 24 runs with two centre runs and 31 runs with one, show the map at moderate and near-zero entanglement.
doe/omars_metric_choice_data.pyThe enumeration behind the literals in the figure script, committed so the numbers can be re-derived rather than trusted. Finding the best value at each size means enumerating every OMARS foldover of that size. That is tractable because a foldover is described entirely by how many times each sign pattern of {-1, 0, +1} appears in its half: every second-order term takes the same value on a half-row and on that row negated. Main effects are orthogonal, so their variance is
1/(2 n_j)directly; quadratics and interactions come from the even block; the full model is attempted only whereh >= k(k+1)/2, and any rank-deficient cell is rejected.--verifyrebuilds twelve cells from naive numpy model matrices and compares: largest absolute difference 4.9e-07.Two further checks that the data passes: all enumerated cells are monotone in the run count for the five alphabetic criteria, which is what the Loewner-order argument predicts, and
Gnever falls below its Kiefer-Wolfowitz floor ofp/N, touching it exactly at three of the sizes.doe/omars-worked-study-tradeoff.py, its PNG, anddoe/omars_worked_study_data.pyFor the new book section "A worked OMARS study". The same four-factor study on the fed-batch bioreactor simulator (
process_improve.simulation.batch) is run 200 times at 13, 17, 21, 27 and 31 OMARS runs and with the 27-run Box-Behnken and face-centred central composite designs, each campaign scored by the true titer at the recipe its fitted model recommends, minus the titer of the current recipe. The figure shows the median, the 10th-to-90th percentile band and the worst campaign for each design, against the best possible gain of 2.006 g/L. Two hundred campaigns per design take minutes, so the values are carried as literals; the data script regenerates them (--quickfor a 20-seed smoke test).Six more worked-study figures, and
doe/omars_worked_study_common.pyOne script per PNG, following the directory's convention, each importing the study from the common module:
omars-worked-study-recipe: the current recipe's temperature schedule with the four corners of the hold-temperature by shift-day range, beside titer against day for twenty replicate batches over the disturbance-free batch.omars-trade-off-column-k4: the four-factor column of the OMARS trade-off table on its own, at every odd run count from 9 to 31, with the staircase's fills, the estimability frontier outlined at 21 runs and the Box-Behnken design closing the column at 27. Every cell is read from the library.omars-worked-study-plan: the thirty batches in run order as a level grid, one row per factor, with the cassette boundary and the four centre runs outlined.omars-worked-study-titer: titer at harvest by run and cassette, centre runs starred, each cassette's centre-run mean drawn, the replicate mean as a reference.omars-worked-study-effects: the fifteen coefficients of the full second-order model fitted in one step, with 95% intervals on 15 df, the three terms the staged analysis selects filled. The script asserts that the terms significant at 5% are exactly those three, which the chapter states.omars-worked-study-surface: the fitted four-term model beside the true response over hold temperature and shift day at pH 7.1 and the high feed rate, on shared contour levels, with the current, recommended and best recipes marked on both.omars_worked_study_common.pyreproduces the study exactly as the chapter's code blocks run it (same factors, disturbance level, design seed, pair split, run order and disturbance seeds) and asserts the values the chapter prints (titer range 4.290 to 9.116 g/L, cassette effect -0.1272, recommended hold 29.54 °C, true titers 7.436 / 8.376 / 9.442 g/L) before any figure is drawn. A change in the library that moved any of those stops every figure script rather than silently redrawing it.Conventions followed
Matplotlib, Okabe-Ito palette matching the other OMARS figures in
doe/, scripts committed alongside the PNGs in the same directory, run from that directory to write the image next to themselves.🤖 Generated with Claude Code
https://claude.ai/code/session_01W1daHPaXLs7PPRsq8CmMMm