process-improve 2.2.0__tar.gz → 2.3.0__tar.gz

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.
Files changed (332) hide show
  1. {process_improve-2.2.0 → process_improve-2.3.0}/PKG-INFO +1 -1
  2. {process_improve-2.2.0 → process_improve-2.3.0}/pyproject.toml +90 -2
  3. {process_improve-2.2.0 → process_improve-2.3.0}/pyproject.toml.orig +90 -2
  4. process_improve-2.3.0/src/process_improve/_tool_registry.py +205 -0
  5. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/control.py +78 -49
  6. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/datasets.py +2 -3
  7. process_improve-2.3.0/src/process_improve/datasets/experiments/biscuit/NOTICE +38 -0
  8. process_improve-2.3.0/src/process_improve/datasets/experiments/biscuit/biscuit.csv +73 -0
  9. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/box-draper-2007.json +26 -11
  10. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/box-hunter-hunter-1978.json +250 -52
  11. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/davies-1956.json +255 -55
  12. process_improve-2.3.0/src/process_improve/datasets/experiments/reference_catalogue/mcmullen-jurica-2024.json +2578 -0
  13. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/pid-book.json +6 -2
  14. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/__init__.py +9 -1
  15. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/_shared.py +23 -0
  16. process_improve-2.3.0/src/process_improve/experiments/_analyses/diagnostics.py +202 -0
  17. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/glm.py +4 -3
  18. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/lack_of_fit.py +45 -18
  19. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/mixture.py +26 -11
  20. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_finite_fields.py +65 -1
  21. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_lm.py +241 -21
  22. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/analyze_experiment.py +11 -4
  23. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/fit_linear_model.py +1 -4
  24. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/recommend_next_step.py +1 -0
  25. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/run_sheet.py +36 -1
  26. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_uniform_sampling.py +3 -5
  27. process_improve-2.3.0/src/process_improve/experiments/across_blocks.py +352 -0
  28. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/analysis.py +341 -58
  29. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/augment.py +48 -23
  30. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/compare.py +12 -4
  31. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/datasets.py +42 -2
  32. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_mixture_constrained.py +3 -1
  33. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_mixture_process.py +1 -1
  34. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_response_surface.py +1 -6
  35. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_screening.py +15 -6
  36. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_split_plot.py +26 -2
  37. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_supersaturated.py +15 -17
  38. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/evaluate.py +58 -12
  39. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/layouts.py +2 -1
  40. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/next_step.py +161 -50
  41. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/optimal.py +2 -7
  42. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/optimization.py +13 -8
  43. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/random_effects.py +10 -2
  44. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/reference_catalogue.py +151 -75
  45. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/region.py +6 -2
  46. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/separating.py +3 -1
  47. process_improve-2.3.0/src/process_improve/experiments/surface_checks.py +416 -0
  48. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/trade_off.py +7 -1
  49. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/api.py +3 -1
  50. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/significance.py +78 -3
  51. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/yates.py +25 -0
  52. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/monitoring/control_charts.py +61 -49
  53. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/monitoring/metrics.py +7 -5
  54. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/__init__.py +10 -0
  55. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_adaptive.py +322 -323
  56. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_asca.py +79 -51
  57. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_categorical.py +74 -60
  58. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_common.py +30 -33
  59. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_cv_criteria.py +126 -143
  60. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_diagnostics.py +136 -72
  61. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_impute.py +13 -8
  62. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_limits.py +13 -8
  63. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_mbpca.py +76 -76
  64. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_mbpls.py +169 -174
  65. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_nipals.py +36 -32
  66. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_null.py +24 -20
  67. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_opls.py +60 -49
  68. process_improve-2.3.0/src/process_improve/multivariate/_optimal_inversion.py +1505 -0
  69. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_pca.py +340 -422
  70. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_pls.py +443 -595
  71. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_plsda.py +125 -61
  72. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_preprocessing.py +38 -136
  73. process_improve-2.3.0/src/process_improve/multivariate/_prm.py +404 -0
  74. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_procrustes.py +68 -63
  75. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_projection.py +8 -8
  76. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_resampling.py +15 -25
  77. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_tpls.py +339 -405
  78. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/methods.py +14 -0
  79. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/plots.py +14 -9
  80. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/tools.py +30 -2
  81. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/regression/_robust_regression.py +195 -75
  82. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/regression/_variance_components.py +54 -0
  83. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/regression/tools.py +17 -4
  84. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/ingest.py +19 -0
  85. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/tools.py +1 -0
  86. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/simulation/batch.py +17 -11
  87. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/simulation/recipes.py +4 -2
  88. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/simulation/tools.py +6 -4
  89. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/tool_spec.py +6 -190
  90. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/types.py +1 -0
  91. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/workbench/tools.py +3 -2
  92. process_improve-2.2.0/src/process_improve/experiments/_analyses/diagnostics.py +0 -102
  93. process_improve-2.2.0/src/process_improve/experiments/surface_checks.py +0 -180
  94. process_improve-2.2.0/src/process_improve/multivariate/_prm.py +0 -390
  95. {process_improve-2.2.0 → process_improve-2.3.0}/README.md +0 -0
  96. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/__init__.py +0 -0
  97. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/_extras.py +0 -0
  98. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/_linalg.py +0 -0
  99. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/_random.py +0 -0
  100. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/_remote_data.py +0 -0
  101. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/__init__.py +0 -0
  102. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/_batch_monitor.py +0 -0
  103. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/_batch_pca.py +0 -0
  104. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/_batch_plots.py +0 -0
  105. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/_batch_pls.py +0 -0
  106. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/_common.py +0 -0
  107. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/_gaps.py +0 -0
  108. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/_online.py +0 -0
  109. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/alignment_helpers.py +0 -0
  110. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/data_input.py +0 -0
  111. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/features.py +0 -0
  112. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/plotting.py +0 -0
  113. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/preprocessing.py +0 -0
  114. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/batch/tools.py +0 -0
  115. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/bivariate/__init__.py +0 -0
  116. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/bivariate/_elbow_peak.py +0 -0
  117. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/bivariate/methods.py +0 -0
  118. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/bivariate/tools.py +0 -0
  119. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/chemistry/__init__.py +0 -0
  120. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/chemistry/preprocessing.py +0 -0
  121. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/config.py +0 -0
  122. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/batch/batch-fake-data.csv +0 -0
  123. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/batch/details.txt +0 -0
  124. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/batch/dryer.csv +0 -0
  125. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/batch/nylon.csv +0 -0
  126. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/boilingpot.csv +0 -0
  127. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/corrosion.csv +0 -0
  128. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/golf.csv +0 -0
  129. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/pollutant.csv +0 -0
  130. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/NOTICE.md +0 -0
  131. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/anderson-whitcomb-2007.json +0 -0
  132. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/derringer-suich-1980.json +0 -0
  133. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/efi-mooc-quizzes.json +0 -0
  134. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/fries-hunter-1980.json +0 -0
  135. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/goos-2025.json +0 -0
  136. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/goos-jones-2011.json +0 -0
  137. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/jones-nachtsheim-2011.json +0 -0
  138. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/jones-nachtsheim-2013.json +0 -0
  139. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/lenth-1989.json +0 -0
  140. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/montgomery-2013.json +0 -0
  141. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/nair-ffd-tutorial-2004.json +0 -0
  142. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/nist-ehandbook-ch5.json +0 -0
  143. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/nunez-ares-et-al-2023.json +0 -0
  144. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/nunez-ares-goos-2019.json +0 -0
  145. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/nunez-ares-goos-2020.json +0 -0
  146. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/schmidt-launsby-2000.json +0 -0
  147. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/topics.json +0 -0
  148. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/reference_catalogue/ullmann-doe-2012.json +0 -0
  149. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/solar.csv +0 -0
  150. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/experiments/test_doe1.csv +0 -0
  151. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/monitoring/batch-yield-and-purity.csv +0 -0
  152. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/monitoring/rubber-colour.csv +0 -0
  153. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/C.csv +0 -0
  154. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/Hotellings_T2_A3.csv +0 -0
  155. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/Hotellings_T2_A6.csv +0 -0
  156. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/LDPE.csv +0 -0
  157. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/P.csv +0 -0
  158. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/R.csv +0 -0
  159. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/T.csv +0 -0
  160. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/U.csv +0 -0
  161. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/W.csv +0 -0
  162. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/LDPE/Yhat_A6.csv +0 -0
  163. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/cheddar-cheese.csv +0 -0
  164. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/dtu-pectin/NOTICE +0 -0
  165. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/dtu-pectin/extraction1.csv +0 -0
  166. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/dtu-pectin/extraction2.csv +0 -0
  167. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/dtu-pectin/extraction3.csv +0 -0
  168. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/dtu-pectin/ftir1.csv +0 -0
  169. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/dtu-pectin/ftir2.csv +0 -0
  170. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/dtu-pectin/ftir3.csv +0 -0
  171. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/kamyr.csv +0 -0
  172. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/solvents.csv +0 -0
  173. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tablet-spectra.csv +0 -0
  174. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/SOURCE +0 -0
  175. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group1.csv +0 -0
  176. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group2.csv +0 -0
  177. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group3.csv +0 -0
  178. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group4.csv +0 -0
  179. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/formulas_Group5.csv +0 -0
  180. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/process_conditions.csv +0 -0
  181. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group1.csv +0 -0
  182. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group2.csv +0 -0
  183. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group3.csv +0 -0
  184. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group4.csv +0 -0
  185. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/properties_Group5.csv +0 -0
  186. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/datasets/multivariate/tpls-pyphi/quality_indicators.csv +0 -0
  187. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/__init__.py +0 -0
  188. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/aliasing.py +0 -0
  189. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/box_cox.py +0 -0
  190. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/curvature.py +0 -0
  191. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/lenth.py +0 -0
  192. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/model_selection.py +0 -0
  193. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/ols_extractors.py +0 -0
  194. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/prediction.py +0 -0
  195. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/split_plot.py +0 -0
  196. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_analyses/transforms.py +0 -0
  197. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_blocking.py +0 -0
  198. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_classical.py +0 -0
  199. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_desirability.py +0 -0
  200. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_expression_text.py +0 -0
  201. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_moment_aberration.py +0 -0
  202. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_orthogonal_arrays.py +0 -0
  203. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_region_geometry.py +0 -0
  204. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_region_quadrature.py +0 -0
  205. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_rules.py +0 -0
  206. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/__init__.py +0 -0
  207. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/analyze_omars.py +0 -0
  208. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/augment_design.py +0 -0
  209. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/compare_designs.py +0 -0
  210. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/create_factorial_design.py +0 -0
  211. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/doe_knowledge.py +0 -0
  212. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/evaluate_design.py +0 -0
  213. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/generate_design.py +0 -0
  214. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/optimize_responses.py +0 -0
  215. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/recommend_strategy.py +0 -0
  216. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/search_design_catalog.py +0 -0
  217. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/simulate_design_performance.py +0 -0
  218. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/trade_off_table.py +0 -0
  219. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/_tools/visualize_doe.py +0 -0
  220. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/allocation.py +0 -0
  221. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/benchmark.py +0 -0
  222. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/crossed_arrays.py +0 -0
  223. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/design_catalog.py +0 -0
  224. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/design_measures.py +0 -0
  225. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs.py +0 -0
  226. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_constrained.py +0 -0
  227. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_covariates.py +0 -0
  228. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_factorial.py +0 -0
  229. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_mixture.py +0 -0
  230. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_omars.py +0 -0
  231. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_omars_ilp.py +0 -0
  232. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_optimal.py +0 -0
  233. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_space_filling.py +0 -0
  234. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_three_level.py +0 -0
  235. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/designs_utils.py +0 -0
  236. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/factor.py +0 -0
  237. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/gaussian_process.py +0 -0
  238. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/incomplete_blocks.py +0 -0
  239. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/knowledge/__init__.py +0 -0
  240. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/knowledge/api.py +0 -0
  241. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/knowledge/data/concepts.yaml +0 -0
  242. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/knowledge/data/decision_rules.yaml +0 -0
  243. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/knowledge/data/design_types.yaml +0 -0
  244. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/knowledge/data/diagnostics.yaml +0 -0
  245. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/knowledge/engine.py +0 -0
  246. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/knowledge/models.py +0 -0
  247. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/least_squares_means.py +0 -0
  248. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/marginal_means.py +0 -0
  249. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/model_comparison.py +0 -0
  250. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/models.py +0 -0
  251. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/multistratum.py +0 -0
  252. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/omars.py +0 -0
  253. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/omars_trade_off.py +0 -0
  254. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/projections.py +0 -0
  255. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/pseudocomponents.py +0 -0
  256. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/run_order.py +0 -0
  257. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/run_sheet.py +0 -0
  258. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/sequential.py +0 -0
  259. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/simulate.py +0 -0
  260. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/simulations.py +0 -0
  261. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/strategy/__init__.py +0 -0
  262. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/strategy/budget.py +0 -0
  263. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/strategy/domain_templates.py +0 -0
  264. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/strategy/engine.py +0 -0
  265. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/strategy/models.py +0 -0
  266. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/structures.py +0 -0
  267. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/three_level_components.py +0 -0
  268. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/tools.py +0 -0
  269. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/true_process.py +0 -0
  270. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/variance_dispersion.py +0 -0
  271. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/__init__.py +0 -0
  272. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/__init__.py +0 -0
  273. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/cube_plot.py +0 -0
  274. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/design_quality.py +0 -0
  275. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/diagnostics.py +0 -0
  276. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/effects.py +0 -0
  277. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/optimization_plots.py +0 -0
  278. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/registry.py +0 -0
  279. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/square_plot.py +0 -0
  280. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/experiments/visualization/plots/surfaces.py +0 -0
  281. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/interactions.py +0 -0
  282. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/mcp_server.py +0 -0
  283. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/monitoring/__init__.py +0 -0
  284. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/monitoring/evop.py +0 -0
  285. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/monitoring/tools.py +0 -0
  286. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_base.py +0 -0
  287. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_pca_pls.py +0 -0
  288. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/multivariate/_robust.py +0 -0
  289. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/py.typed +0 -0
  290. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/recipes.py +0 -0
  291. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/regression/__init__.py +0 -0
  292. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/regression/box_tidwell.py +0 -0
  293. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/regression/methods.py +0 -0
  294. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/regression/nonlinear.py +0 -0
  295. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/__init__.py +0 -0
  296. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/analysis.py +0 -0
  297. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/designed.py +0 -0
  298. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/diagnostics.py +0 -0
  299. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/mam.py +0 -0
  300. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/panel.py +0 -0
  301. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/recipes.py +0 -0
  302. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/sensory/validation.py +0 -0
  303. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/simulation/__init__.py +0 -0
  304. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/simulation/context.py +0 -0
  305. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/simulation/latent.py +0 -0
  306. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/simulation/model.py +0 -0
  307. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/tool_safety.py +0 -0
  308. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/univariate/__init__.py +0 -0
  309. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/univariate/classical.py +0 -0
  310. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/univariate/error_transmission.py +0 -0
  311. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/univariate/metrics.py +0 -0
  312. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/univariate/reference_distribution.py +0 -0
  313. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/univariate/serial_correlation.py +0 -0
  314. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/univariate/stated_values.py +0 -0
  315. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/univariate/tools.py +0 -0
  316. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/__init__.py +0 -0
  317. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/adapters/__init__.py +0 -0
  318. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/adapters/base.py +0 -0
  319. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/adapters/echarts_adapter.py +0 -0
  320. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/adapters/plotly_adapter.py +0 -0
  321. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/charts/__init__.py +0 -0
  322. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/charts/boxplot.py +0 -0
  323. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/colors.py +0 -0
  324. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/raincloud.py +0 -0
  325. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/spec.py +0 -0
  326. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/themes.py +0 -0
  327. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/visualization/tools.py +0 -0
  328. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/workbench/__init__.py +0 -0
  329. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/workbench/_expressions.py +0 -0
  330. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/workbench/project.py +0 -0
  331. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/workbench/recipe.py +0 -0
  332. {process_improve-2.2.0 → process_improve-2.3.0}/src/process_improve/workbench/variant.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: process-improve
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Designed Experiments; Latent Variables (PCA, PLS, multivariate methods with missing data); Process Monitoring; Batch data analysis.
5
5
  Keywords: Designed Experiments,Design of Experiments,DoE,Optimal Design,Response Surface,Latent Variables,PCA,PLS,Multivariate Data Analysis,Chemometrics,Process Monitoring,Batch data analysis
6
6
  Author: Kevin Dunn
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "process-improve"
3
- version = "2.2.0"
3
+ version = "2.3.0"
4
4
  description = "Designed Experiments; Latent Variables (PCA, PLS, multivariate methods with missing data); Process Monitoring; Batch data analysis."
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -107,7 +107,7 @@ dev = [
107
107
  "pytest-cov>=7.0.0",
108
108
  "pytest-xdist>=3.8.0",
109
109
  "pydata-sphinx-theme>=0.16.1",
110
- "ruff>=0.11.0,<0.17",
110
+ "ruff>=0.11.0,<0.18",
111
111
  "sphinx>=8.1.3",
112
112
  "nbsphinx>=0.9.5",
113
113
  "ipykernel>=6.29",
@@ -301,3 +301,91 @@ known-third-party = [
301
301
  "pytest",
302
302
  "pandas",
303
303
  ]
304
+
305
+ [tool.mutmut]
306
+ source_paths = ["src"]
307
+ only_mutate = [
308
+ "src/process_improve/multivariate/_limits.py",
309
+ "src/process_improve/monitoring/metrics.py",
310
+ "src/process_improve/monitoring/evop.py",
311
+ "src/process_improve/monitoring/control_charts.py",
312
+ "src/process_improve/multivariate/_nipals.py",
313
+ "src/process_improve/multivariate/_preprocessing.py",
314
+ "src/process_improve/multivariate/_projection.py",
315
+ "src/process_improve/multivariate/_impute.py",
316
+ "src/process_improve/multivariate/_robust.py",
317
+ "src/process_improve/multivariate/_diagnostics.py",
318
+ "src/process_improve/multivariate/_resampling.py",
319
+ "src/process_improve/multivariate/_null.py",
320
+ "src/process_improve/multivariate/_procrustes.py",
321
+ "src/process_improve/multivariate/_prm.py",
322
+ "src/process_improve/multivariate/_opls.py",
323
+ "src/process_improve/multivariate/_pca.py",
324
+ "src/process_improve/multivariate/_pls.py",
325
+ "src/process_improve/multivariate/_mbpca.py",
326
+ "src/process_improve/multivariate/_mbpls.py",
327
+ "src/process_improve/multivariate/_tpls.py",
328
+ "src/process_improve/multivariate/_cv_criteria.py",
329
+ "src/process_improve/multivariate/_plsda.py",
330
+ "src/process_improve/multivariate/_asca.py",
331
+ "src/process_improve/multivariate/_categorical.py",
332
+ "src/process_improve/multivariate/_adaptive.py",
333
+ ]
334
+ pytest_add_cli_args = [
335
+ "-o",
336
+ "addopts=",
337
+ "-p",
338
+ "no:cacheprovider",
339
+ ]
340
+ pytest_add_cli_args_test_selection = [
341
+ "tests/test_multivariate_limits.py",
342
+ "tests/test_multivariate.py",
343
+ "tests/test_audit_regressions_multivariate.py",
344
+ "tests/test_multiblock_validation.py",
345
+ "tests/test_multivariate_introspection.py",
346
+ "tests/test_monitoring.py",
347
+ "tests/test_monitoring_helpers.py",
348
+ "tests/test_audit_regressions_univariate_monitoring.py",
349
+ "tests/test_evop.py",
350
+ "tests/test_control_chart_cusum.py",
351
+ "tests/test_control_chart_ewma.py",
352
+ "tests/test_control_chart_hw_golden.py",
353
+ "tests/test_nipals_quick_regress.py",
354
+ "tests/test_multivariate_adaptive.py",
355
+ "tests/test_multivariate_centring_traps.py",
356
+ "tests/test_multivariate_projection.py",
357
+ "tests/test_multivariate_pls_missing.py",
358
+ "tests/test_multivariate_prm.py",
359
+ "tests/test_contributions_missing_cells.py",
360
+ "tests/test_multivariate_contribution_plots.py",
361
+ "tests/test_multivariate_meda.py",
362
+ "tests/test_multivariate_resampler.py",
363
+ "tests/test_multivariate_null.py",
364
+ "tests/test_multivariate_gpa.py",
365
+ "tests/test_multivariate_opls.py",
366
+ "tests/test_multivariate_tsr.py",
367
+ "tests/test_pca_nipals_mixomics_crosscheck.py",
368
+ "tests/test_multivariate_nipals_seed.py",
369
+ "tests/test_r2_per_variable_cumulative.py",
370
+ "tests/test_multivariate_sample_weight.py",
371
+ "tests/test_multivariate_inversion.py",
372
+ "tests/test_multivariate_inversion_worked_example.py",
373
+ "tests/test_multivariate_feature_names.py",
374
+ "tests/test_sklearn_compat.py",
375
+ "tests/test_multivariate_robustness.py",
376
+ "tests/test_tool_spec.py",
377
+ "tests/test_multiblock_reference.py",
378
+ "--deselect",
379
+ "tests/test_multiblock_reference.py::TestFMCReference",
380
+ "tests/test_multiblock_weighting.py",
381
+ "tests/test_multivariate_categorical.py",
382
+ "tests/test_mbpls_cross_validation.py",
383
+ "tests/test_mbpls_regressor.py",
384
+ "tests/test_multivariate_tpls_validation.py",
385
+ "tests/test_multivariate_tpls_display.py",
386
+ "tests/test_multivariate_tpls_feature_importance.py",
387
+ "tests/test_multivariate_dataframedict.py",
388
+ "tests/test_multivariate_cv_criteria.py",
389
+ "tests/test_multivariate_plsda.py",
390
+ "tests/test_multivariate_asca.py",
391
+ ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "process-improve"
3
- version = "2.2.0"
3
+ version = "2.3.0"
4
4
  description = 'Designed Experiments; Latent Variables (PCA, PLS, multivariate methods with missing data); Process Monitoring; Batch data analysis.'
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -152,7 +152,7 @@ dev = [
152
152
  "pytest-cov>=7.0.0",
153
153
  "pytest-xdist>=3.8.0",
154
154
  "pydata-sphinx-theme>=0.16.1",
155
- "ruff>=0.11.0,<0.17", # upper-bound so a future ruff release cannot silently break the lint gate (see mypy)
155
+ "ruff>=0.11.0,<0.18", # upper-bound so a future ruff release cannot silently break the lint gate (see mypy)
156
156
  "sphinx>=8.1.3",
157
157
  "nbsphinx>=0.9.5",
158
158
  "ipykernel>=6.29",
@@ -449,3 +449,91 @@ docstring-quotes = "double"
449
449
 
450
450
  [tool.ruff.lint.isort]
451
451
  known-third-party=["pytest", "pandas"]
452
+
453
+ # Mutation testing: see "Mutation testing" in CONTRIBUTING.md. Modules join `only_mutate` one at a
454
+ # time, each with no survivor, starting with those whose numbers users act on; the tests listed
455
+ # are those that reach them.
456
+ [tool.mutmut]
457
+ source_paths = ["src"]
458
+ only_mutate = [
459
+ "src/process_improve/multivariate/_limits.py",
460
+ "src/process_improve/monitoring/metrics.py",
461
+ "src/process_improve/monitoring/evop.py",
462
+ "src/process_improve/monitoring/control_charts.py",
463
+ "src/process_improve/multivariate/_nipals.py",
464
+ "src/process_improve/multivariate/_preprocessing.py",
465
+ "src/process_improve/multivariate/_projection.py",
466
+ "src/process_improve/multivariate/_impute.py",
467
+ "src/process_improve/multivariate/_robust.py",
468
+ "src/process_improve/multivariate/_diagnostics.py",
469
+ "src/process_improve/multivariate/_resampling.py",
470
+ "src/process_improve/multivariate/_null.py",
471
+ "src/process_improve/multivariate/_procrustes.py",
472
+ "src/process_improve/multivariate/_prm.py",
473
+ "src/process_improve/multivariate/_opls.py",
474
+ "src/process_improve/multivariate/_pca.py",
475
+ "src/process_improve/multivariate/_pls.py",
476
+ "src/process_improve/multivariate/_mbpca.py",
477
+ "src/process_improve/multivariate/_mbpls.py",
478
+ "src/process_improve/multivariate/_tpls.py",
479
+ "src/process_improve/multivariate/_cv_criteria.py",
480
+ "src/process_improve/multivariate/_plsda.py",
481
+ "src/process_improve/multivariate/_asca.py",
482
+ "src/process_improve/multivariate/_categorical.py",
483
+ "src/process_improve/multivariate/_adaptive.py",
484
+ ]
485
+ # The runner drops pytest.ini's addopts: no xdist workers and no coverage inside each mutant run.
486
+ pytest_add_cli_args = ["-o", "addopts=", "-p", "no:cacheprovider"]
487
+ pytest_add_cli_args_test_selection = [
488
+ "tests/test_multivariate_limits.py",
489
+ "tests/test_multivariate.py",
490
+ "tests/test_audit_regressions_multivariate.py",
491
+ "tests/test_multiblock_validation.py",
492
+ "tests/test_multivariate_introspection.py",
493
+ "tests/test_monitoring.py",
494
+ "tests/test_monitoring_helpers.py",
495
+ "tests/test_audit_regressions_univariate_monitoring.py",
496
+ "tests/test_evop.py",
497
+ "tests/test_control_chart_cusum.py",
498
+ "tests/test_control_chart_ewma.py",
499
+ "tests/test_control_chart_hw_golden.py",
500
+ "tests/test_nipals_quick_regress.py",
501
+ "tests/test_multivariate_adaptive.py",
502
+ "tests/test_multivariate_centring_traps.py",
503
+ "tests/test_multivariate_projection.py",
504
+ "tests/test_multivariate_pls_missing.py",
505
+ "tests/test_multivariate_prm.py",
506
+ "tests/test_contributions_missing_cells.py",
507
+ "tests/test_multivariate_contribution_plots.py",
508
+ "tests/test_multivariate_meda.py",
509
+ "tests/test_multivariate_resampler.py",
510
+ "tests/test_multivariate_null.py",
511
+ "tests/test_multivariate_gpa.py",
512
+ "tests/test_multivariate_opls.py",
513
+ "tests/test_multivariate_tsr.py",
514
+ "tests/test_pca_nipals_mixomics_crosscheck.py",
515
+ "tests/test_multivariate_nipals_seed.py",
516
+ "tests/test_r2_per_variable_cumulative.py",
517
+ "tests/test_multivariate_sample_weight.py",
518
+ "tests/test_multivariate_inversion.py",
519
+ "tests/test_multivariate_inversion_worked_example.py",
520
+ "tests/test_multivariate_feature_names.py",
521
+ "tests/test_sklearn_compat.py",
522
+ "tests/test_multivariate_robustness.py",
523
+ "tests/test_tool_spec.py",
524
+ "tests/test_multiblock_reference.py",
525
+ # Its FMC case study reads a script from docs/, which mutmut does not copy, and its data from the web.
526
+ "--deselect",
527
+ "tests/test_multiblock_reference.py::TestFMCReference",
528
+ "tests/test_multiblock_weighting.py",
529
+ "tests/test_multivariate_categorical.py",
530
+ "tests/test_mbpls_cross_validation.py",
531
+ "tests/test_mbpls_regressor.py",
532
+ "tests/test_multivariate_tpls_validation.py",
533
+ "tests/test_multivariate_tpls_display.py",
534
+ "tests/test_multivariate_tpls_feature_importance.py",
535
+ "tests/test_multivariate_dataframedict.py",
536
+ "tests/test_multivariate_cv_criteria.py",
537
+ "tests/test_multivariate_plsda.py",
538
+ "tests/test_multivariate_asca.py",
539
+ ]
@@ -0,0 +1,205 @@
1
+ """(c) Kevin Dunn, 2010-2026. MIT License.
2
+
3
+ The ``@tool_spec`` decorator, the registry it fills, and ``clean``.
4
+
5
+ These are the parts of :mod:`process_improve.tool_spec` that every ``tools.py``
6
+ module imports. They live apart from tool discovery, which names every
7
+ ``tools.py`` module, so that a tool module depends on this file alone and not,
8
+ through discovery, on every other tool module. ``tool_spec.py`` re-exports all
9
+ of it, and callers import it from there.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import math
15
+ from collections.abc import Callable
16
+ from typing import Any
17
+
18
+ import numpy as np
19
+ from pydantic import BaseModel
20
+
21
+ # ---------------------------------------------------------------------------
22
+ # Registry
23
+ # ---------------------------------------------------------------------------
24
+
25
+ #: Maps tool name -> decorated callable. Populated by ``@tool_spec``.
26
+ _TOOL_REGISTRY: dict[str, Callable[..., Any]] = {}
27
+
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # RNG metadata validation
31
+ # ---------------------------------------------------------------------------
32
+
33
+
34
+ def _validate_rng_metadata(name: str, rng: dict[str, Any]) -> None:
35
+ """Reject ``rng`` payloads that don't match the published contract.
36
+
37
+ The contract is intentionally narrow so downstream consumers (the
38
+ factorial reproducible-export service) can introspect specs without
39
+ defensive type-checking. See ``CLAUDE.md`` for the full schema.
40
+ """
41
+ if not isinstance(rng, dict):
42
+ raise TypeError(f"@tool_spec(name={name!r}): 'rng' must be a dict, got {type(rng).__name__}.")
43
+ if "uses_rng" not in rng or not isinstance(rng["uses_rng"], bool):
44
+ raise ValueError(f"@tool_spec(name={name!r}): 'rng' must have a boolean 'uses_rng' key.")
45
+ allowed_keys = {"uses_rng", "seed_param", "default_seed", "note"}
46
+ extra = set(rng) - allowed_keys
47
+ if extra:
48
+ raise ValueError(
49
+ f"@tool_spec(name={name!r}): unknown keys in 'rng': {sorted(extra)}. Allowed: {sorted(allowed_keys)}."
50
+ )
51
+ if not rng["uses_rng"]:
52
+ # Deterministic tools shouldn't carry a seed_param / default_seed.
53
+ if rng.get("seed_param") is not None or rng.get("default_seed") is not None:
54
+ raise ValueError(
55
+ f"@tool_spec(name={name!r}): 'seed_param' / 'default_seed' must be omitted when uses_rng is False."
56
+ )
57
+ return
58
+ seed_param = rng.get("seed_param")
59
+ if seed_param is not None and not isinstance(seed_param, str):
60
+ raise TypeError(
61
+ f"@tool_spec(name={name!r}): 'seed_param' must be a string or None, got {type(seed_param).__name__}."
62
+ )
63
+ default_seed = rng.get("default_seed")
64
+ if default_seed is not None and not isinstance(default_seed, int):
65
+ raise TypeError(
66
+ f"@tool_spec(name={name!r}): 'default_seed' must be an int or None, got {type(default_seed).__name__}."
67
+ )
68
+ if seed_param is None and default_seed is not None:
69
+ raise ValueError(f"@tool_spec(name={name!r}): 'default_seed' requires a 'seed_param' name.")
70
+
71
+
72
+ # ---------------------------------------------------------------------------
73
+ # Decorator
74
+ # ---------------------------------------------------------------------------
75
+
76
+
77
+ def tool_spec( # noqa: PLR0913
78
+ name: str,
79
+ description: str,
80
+ *,
81
+ input_model: type[BaseModel],
82
+ examples: str = "",
83
+ category: str = "",
84
+ rng: dict[str, Any] | None = None,
85
+ ) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
86
+ """Mark a function as an agent-callable tool.
87
+
88
+ Parameters
89
+ ----------
90
+ name:
91
+ Unique tool name exposed to the LLM (snake_case).
92
+ description:
93
+ Natural-language description of what the tool does and when to use it.
94
+ input_model:
95
+ A :class:`pydantic.BaseModel` subclass describing the tool's
96
+ input. The model is the single source of truth for both the
97
+ function's Python signature and the MCP JSON Schema (derived
98
+ via :meth:`BaseModel.model_json_schema`). Every input model
99
+ **must** set ``model_config = ConfigDict(extra="forbid")`` so
100
+ unknown keys are rejected (closes the SEC-15
101
+ kwarg-injection vector at the schema layer).
102
+
103
+ The decorated function then receives the parsed model as its
104
+ single positional argument::
105
+
106
+ class FooInput(BaseModel):
107
+ model_config = ConfigDict(extra="forbid")
108
+ values: list[float] = Field(..., min_length=2)
109
+
110
+ @tool_spec(name="foo", description="...", input_model=FooInput)
111
+ def foo(spec: FooInput) -> dict:
112
+ return {"n": len(spec.values)}
113
+ examples:
114
+ Optional natural-language -> tool-call mappings appended to
115
+ ``description`` so the LLM sees worked examples.
116
+ category:
117
+ Optional category string (e.g. ``"univariate"``). Used for
118
+ filtering with :func:`get_tool_specs`.
119
+ rng:
120
+ Optional reproducibility metadata; see :func:`_validate_rng_metadata`
121
+ for the contract.
122
+
123
+ Returns
124
+ -------
125
+ Callable
126
+ The original function, with an attached ``_tool_spec`` dict
127
+ and ``_input_model`` attribute.
128
+ """
129
+ if rng is not None:
130
+ _validate_rng_metadata(name, rng)
131
+
132
+ if not (isinstance(input_model, type) and issubclass(input_model, BaseModel)):
133
+ raise TypeError(
134
+ f"@tool_spec(name={name!r}): input_model must be a pydantic BaseModel subclass; got {input_model!r}."
135
+ )
136
+ extra_policy = input_model.model_config.get("extra")
137
+ if extra_policy != "forbid":
138
+ raise ValueError(
139
+ f"@tool_spec(name={name!r}): input_model {input_model.__name__} "
140
+ "must set model_config = ConfigDict(extra='forbid') so unknown "
141
+ "kwargs are rejected (ENG-04 / SEC-15 contract)."
142
+ )
143
+
144
+ def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
145
+ """Attach the assembled tool spec to ``func`` and return it unchanged."""
146
+ full_description = description
147
+ if examples:
148
+ full_description = f"{description}\n\nExamples\n--------\n{examples}"
149
+
150
+ spec: dict[str, Any] = {
151
+ "name": name,
152
+ "description": full_description,
153
+ "input_schema": input_model.model_json_schema(),
154
+ }
155
+ if category:
156
+ spec["category"] = category
157
+ if rng is not None:
158
+ spec["rng"] = dict(rng)
159
+
160
+ func._tool_spec = spec # type: ignore[attr-defined]
161
+ func._input_model = input_model # type: ignore[attr-defined]
162
+ _TOOL_REGISTRY[name] = func
163
+ return func
164
+
165
+ return decorator
166
+
167
+
168
+ # ---------------------------------------------------------------------------
169
+ # Serialisation helper
170
+ # ---------------------------------------------------------------------------
171
+
172
+
173
+ def clean(value: Any) -> Any: # noqa: PLR0911, ANN401
174
+ """Recursively convert numpy scalars / arrays to plain Python types.
175
+
176
+ All ``tools.py`` modules should call ``clean(result)`` before returning
177
+ so that every tool output is JSON-serialisable.
178
+ """
179
+ if isinstance(value, dict):
180
+ # Keys need unwrapping too: json.dumps rejects numpy scalar keys (a
181
+ # dict keyed by np.int64 group labels from a pandas groupby is the
182
+ # common case), and previously they leaked through untouched. Only
183
+ # numpy scalars are unwrapped - other key types pass through so
184
+ # hashability is never at risk.
185
+ return {(k.item() if isinstance(k, np.generic) else k): clean(v) for k, v in value.items()}
186
+ if isinstance(value, (list, tuple, set, frozenset)):
187
+ return [clean(v) for v in value]
188
+ # np.bool_ subclasses neither np.integer nor Python bool, and json.dumps
189
+ # rejects it; it must be tested before the numeric branches.
190
+ if isinstance(value, np.bool_):
191
+ return bool(value)
192
+ if isinstance(value, np.integer):
193
+ return int(value)
194
+ if isinstance(value, np.floating):
195
+ v = float(value)
196
+ return None if math.isnan(v) or math.isinf(v) else v
197
+ if isinstance(value, float):
198
+ return None if math.isnan(value) or math.isinf(value) else value
199
+ if isinstance(value, np.ndarray):
200
+ return clean(value.tolist())
201
+ if isinstance(value, np.generic):
202
+ # Catch-all for the remaining numpy scalar types (datetime64,
203
+ # complex, str_, ...): unwrap to the closest Python equivalent.
204
+ return clean(value.item())
205
+ return value
@@ -22,21 +22,29 @@ solves that choice as a quadratic program in the scaled space of a fitted
22
22
  With the quadratic caps the problem is a convex quadratically-constrained QP.
23
23
  The workhorse is the penalty-form pure QP solved with `osqp
24
24
  <https://osqp.org>`_ (the ``control`` extra); the hard-cap mode wraps the
25
- same QP in an outer scalar iteration on the two penalty multipliers, which is
26
- exact for this convex problem and converges in a handful of inner solves at
27
- this size (a few dozen decision variables).
25
+ same QP in an outer iteration on the two penalty multipliers. It raises the
26
+ multiplier of each broken cap until both caps hold, then bisects each
27
+ multiplier down in turn, taking a step only when both caps still hold: a
28
+ schedule returned with status ``"ok"`` meets both caps to within 1%, and a
29
+ cap that binds alone ends with its statistic just inside it. When both caps
30
+ bind, a step down in one multiplier can break the other cap, so a statistic
31
+ can stay well inside its cap and the schedule can fall short of the capped
32
+ optimum.
28
33
 
29
34
  The formulation follows the latent-variable batch control literature.
30
35
  Flores-Cerrillo and MacGregor (2004) give the quality-tracking objective, the
31
- movement-suppression term, the soft T2 term, and the SPE check on the
32
- measurements so far that gates whether a correction is computed at all;
33
- Yabuki and MacGregor (1997) give the no-correction dead band, their
34
- "no-control region"; Garcia-Munoz, Kourti and MacGregor (2004) give the
35
- per-decision-point score covariance and limits, and the prediction interval
36
- at a decision point is built in the same spirit, from the training batches
37
- re-projected under that point's missingness pattern; Arteaga and Ferrer (2002)
38
- give the trimmed score regression used to estimate the scores of a partially
39
- observed row, which Golshan et al. (2010) also apply in the LV-MPC setting.
36
+ movement-suppression term and the soft T2 term, and they check the SPE of the
37
+ measurements so far before a correction is computed, suggesting that the
38
+ schedule from the last decision point be kept when that check fails, which is
39
+ what the gate here does. The dead band plays the role of the "no-control
40
+ region" of Yabuki and MacGregor (1997). Garcia-Munoz, Kourti and MacGregor
41
+ (2004) compute Hotelling's T2 of on-line score estimates against the
42
+ covariance of the reference batches' estimates at each time, as Nomikos and
43
+ MacGregor (1995) suggested; the per-decision-point limits here, and the
44
+ prediction interval at a decision point, are built the same way, from the
45
+ training batches re-projected under that point's missingness pattern.
46
+ Arteaga and Ferrer (2002) give the trimmed score regression used to estimate
47
+ the scores of a partially observed row.
40
48
 
41
49
  Two departures from Flores-Cerrillo and MacGregor are deliberate. Their
42
50
  optimisation is over an adjustment to the scores, with the remaining
@@ -44,18 +52,20 @@ trajectories recovered by inverting the PLS model, which is what keeps those
44
52
  trajectories consistent with past operation. Here the decision variables are
45
53
  the future MV columns themselves, so actuator bounds and rate limits apply
46
54
  exactly in engineering units, and the terms that hold the answer inside the
47
- model's region are stated explicitly instead. Second, the SPE of the
48
- *candidate* row is penalised and capped, not only checked on the
49
- measurements so far, because a candidate written directly in the MV columns
50
- can leave the model plane in a way a score adjustment cannot.
51
-
52
- A practical caveat: models of this kind are identified on *recorded* (noisy,
53
- realised) trajectories, while the corrector outputs *setpoints*. That is the
54
- standard identification practice, and it attenuates the apparent gain
55
- slightly (the regression sees the control error as input noise); the
56
- executed-policy evaluation in :func:`evaluate_control_policies`
57
- (:mod:`process_improve.simulation`) measures the realised effect rather than
58
- trusting the model's own prediction.
55
+ model's region are stated explicitly instead. Golshan et al. (2010) give both
56
+ forms for latent-variable MPC: over a correction to the scores, and directly
57
+ over the future manipulated variables. Second, the SPE of the *candidate* row
58
+ is penalised and capped, not only checked on the measurements so far: a
59
+ trajectory reconstructed from adjusted scores lies on the model plane by
60
+ construction, and one written directly in the MV columns need not.
61
+
62
+ A practical caveat: models of this kind are identified on *recorded*
63
+ trajectories (the realised values plus measurement noise), while the
64
+ corrector outputs *setpoints*. The substitution is sound where the control
65
+ loops track their setpoints; the executed-policy evaluation in
66
+ :func:`evaluate_control_policies` (:mod:`process_improve.simulation`)
67
+ measures the realised effect rather than trusting the model's own
68
+ prediction.
59
69
 
60
70
  References
61
71
  ----------
@@ -71,6 +81,9 @@ Garcia-Munoz, S., Kourti, T. and MacGregor, J.F., "Model Predictive
71
81
  Monitoring for Batch Processes", Industrial & Engineering Chemistry
72
82
  Research, 43, 5929-5941, 2004.
73
83
 
84
+ Nomikos, P. and MacGregor, J.F., "Multivariate SPC Charts for Monitoring
85
+ Batch Processes", Technometrics, 37, 41-59, 1995.
86
+
74
87
  Yabuki, Y. and MacGregor, J.F., "Product quality control in semibatch
75
88
  reactors using midcourse correction policies", Industrial & Engineering
76
89
  Chemistry Research, 36, 1268-1275, 1997.
@@ -138,9 +151,10 @@ def _solve_qp(
138
151
  eps_abs=1e-10,
139
152
  eps_rel=1e-10,
140
153
  max_iter=100_000,
141
- polish=True,
154
+ polishing=True,
142
155
  )
143
- result = problem.solve()
156
+ # Explicit, as osqp warns that its default will become True; the status check below raises instead.
157
+ result = problem.solve(raise_error=False)
144
158
  status = str(result.info.status)
145
159
  if "solved" not in status.lower():
146
160
  raise RuntimeError(
@@ -660,10 +674,11 @@ class MidCourseCorrector:
660
674
  answers the monitoring question at a decision point (the predicted final
661
675
  quality with its interval, and the validity statistics of the batch so
662
676
  far). :meth:`correct` adds the decision: it checks the batch-so-far
663
- against the model (the SPE validity gate of Flores-Cerrillo and
664
- MacGregor, 2004), applies the no-correction dead band (Yabuki and
665
- MacGregor, 1997) in target mode, builds the per-decision-point reference
666
- limits (Garcia-Munoz et al., 2004), solves the QP, and returns the full
677
+ against the model (the SPE check of Flores-Cerrillo and MacGregor, 2004,
678
+ keeping the current schedule when it fails), applies the no-correction
679
+ dead band (in the role of the no-control region of Yabuki and MacGregor,
680
+ 1997) in target mode, builds the per-decision-point reference limits
681
+ (Garcia-Munoz et al., 2004), solves the QP, and returns the full
667
682
  corrected schedule ready to implement (or to hand to
668
683
  :meth:`process_improve.simulation.BioreactorSimulator.simulate_batch`).
669
684
 
@@ -795,13 +810,15 @@ class MidCourseCorrector:
795
810
  and the *candidate* pattern (monitoring plus the future MV columns,
796
811
  which the optimiser treats as observed) for the QP's SPE cap and the
797
812
  score covariance behind its T2 term. Limits: the g-chi-squared SPE
798
- limit of Nomikos and MacGregor on each pattern's training SPE values,
799
- and the F-distribution T2 limit on the candidate-pattern score
800
- estimates with their own covariance (Garcia-Munoz et al., 2004).
813
+ limit (Box's approximation, as Nomikos and MacGregor, 1995, use it)
814
+ on each pattern's training SPE values, and the F-distribution T2
815
+ limit on the candidate-pattern score estimates with their own
816
+ covariance (Garcia-Munoz et al., 2004).
801
817
 
802
818
  Also returned: ``rmse_k`` (Series over the targets, original units),
803
819
  the prediction error of the model at this decision point, from the
804
- candidate-pattern estimates of the training batches; and the
820
+ candidate-pattern estimates of the training batches (an in-sample
821
+ error, since the model was fitted on those batches); and the
805
822
  condition numbers of the two pattern operators. Results are cached
806
823
  per ``k``.
807
824
  """
@@ -903,7 +920,10 @@ class MidCourseCorrector:
903
920
  uses the model's prediction error *at this decision point*
904
921
  (``limits_at(k).rmse_k``) with the leverage of the estimated scores,
905
922
  so it is wide early in the batch, when the estimate rests on few
906
- observed columns, and narrows as the batch runs.
923
+ observed columns, and narrows as the batch runs. It is the prediction
924
+ interval of a least-squares regression of the quality on the scores,
925
+ with the scores treated as known; check its coverage on held-out
926
+ batches before relying on it.
907
927
 
908
928
  Parameters
909
929
  ----------
@@ -1190,9 +1210,12 @@ def _oracle_remaining( # noqa: PLR0913 - explicit oracle inputs
1190
1210
  Direct search (Nelder-Mead, then Powell from the same start) over
1191
1211
  ``n_knots`` linearly interpolated knot values per manipulated variable
1192
1212
  from sample ``k`` onward, with the identical seed, so the disturbances
1193
- match the batch being corrected. This is the ceiling for any mid-course
1194
- scheme at that decision point, because the objective is the true process,
1195
- not a model of it.
1213
+ match the batch being corrected. It estimates the ceiling for any
1214
+ mid-course scheme at that decision point: the objective is the true
1215
+ process, not a model of it, and the disturbances still to come are
1216
+ known to it, as they are to no scheme running on a plant. The search is
1217
+ local, within the full operating bounds and without rate limits, so the
1218
+ value is a lower bound on that ceiling.
1196
1219
  """
1197
1220
  from scipy.optimize import minimize # noqa: PLC0415
1198
1221
 
@@ -1325,13 +1348,18 @@ def evaluate_control_policies( # noqa: PLR0913, PLR0915, C901 - one executed co
1325
1348
  run the nominal schedule.
1326
1349
  - **oracle-from-k**: for every batch the mid-course policy corrected,
1327
1350
  the remaining schedule is instead optimised against the simulator
1328
- itself at the same decision point (:func:`_oracle_remaining`). This is
1329
- the ceiling for any mid-course scheme at that decision point; the gap
1330
- to the mid-course row is the price of using an empirical model with
1331
- limited historical excitation.
1332
- - **adapted**: every batch runs the true optimal schedule for its own
1333
- initial conditions from time zero
1334
- (``simulator.optimal_trajectory``), the perfect-feedforward ceiling.
1351
+ itself at the same decision point, with the batch's own seed
1352
+ (:func:`_oracle_remaining`). This estimates the ceiling for any
1353
+ mid-course scheme at that decision point. The gap to the mid-course
1354
+ row mixes the error of an empirical model with limited historical
1355
+ excitation, the oracle's knowledge of the disturbances still to come,
1356
+ and the corrector's own limits (movement penalty, validity caps,
1357
+ tighter bounds and rate limits).
1358
+ - **adapted**: every batch runs the schedule that maximises the
1359
+ disturbance-free titer for its own initial conditions, found before
1360
+ the batch starts from the simulator's own equations
1361
+ (``simulator.optimal_trajectory``): feedforward adaptation with a
1362
+ perfect process model, an estimate of the feedforward ceiling.
1335
1363
 
1336
1364
  Parameters
1337
1365
  ----------
@@ -1357,10 +1385,11 @@ def evaluate_control_policies( # noqa: PLR0913, PLR0915, C901 - one executed co
1357
1385
  Fit one model per feed class (labels from the training campaign;
1358
1386
  test batches are assigned to the nearest class centroid in
1359
1387
  standardised Z). With ``False``, or when class labels are
1360
- unavailable, a single global model is used; the executed experiments
1361
- behind this module found the global linear model averages the
1362
- class-dependent gain direction away, so per-class models are the
1363
- default.
1388
+ unavailable, a single global model is used. The effect of a schedule
1389
+ change depends on the feed class and one linear model averages it:
1390
+ on the default configuration the global model corrects five batches
1391
+ for a mean executed gain of about 0.5 g/L, against eight batches and
1392
+ about 1.4 g/L per class, so per-class models are the default.
1364
1393
  target_side, dead_band, weights, bounds, rate_limits, spe_cap, t2_cap, n_knots, method, ridge
1365
1394
  Corrector settings, passed to :class:`MidCourseCorrector`. ``bounds``
1366
1395
  defaults to the simulator's operating bounds tightened inward by
@@ -72,9 +72,8 @@ def _per_batch_table(sheet: pd.DataFrame, batch_ids: list[Hashable]) -> pd.DataF
72
72
  def load_nylon() -> dict[Hashable, pd.DataFrame]:
73
73
  """Return the nylon autoclave reactor batch dataset.
74
74
 
75
- Trajectory data from an industrial nylon polymerization autoclave,
76
- used widely in the batch analysis and monitoring literature. Variables
77
- ``Tag01`` to ``Tag10`` are temperatures, pressures, and flows recorded
75
+ Trajectory data from an industrial nylon polymerization autoclave.
76
+ Variables ``Tag01`` to ``Tag10`` are temperatures, pressures, and flows recorded
78
77
  during each batch. Batch durations vary slightly (113 to 135 samples),
79
78
  so resample or align the batches to a common length before unfolding
80
79
  (see :func:`process_improve.batch.resample_to_reference`).