fomtools 0.0.2__tar.gz → 0.0.4__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 (383) hide show
  1. {fomtools-0.0.2 → fomtools-0.0.4}/.github/workflows/docs.yml +9 -13
  2. {fomtools-0.0.2 → fomtools-0.0.4}/.github/workflows/update-skill.yml +7 -14
  3. {fomtools-0.0.2 → fomtools-0.0.4}/.gitignore +5 -0
  4. {fomtools-0.0.2 → fomtools-0.0.4}/AGENTS.md +27 -0
  5. fomtools-0.0.4/CONTRIBUTING.md +34 -0
  6. fomtools-0.0.4/Makefile +13 -0
  7. {fomtools-0.0.2/fomtools.egg-info → fomtools-0.0.4}/PKG-INFO +91 -42
  8. {fomtools-0.0.2 → fomtools-0.0.4}/README.md +83 -40
  9. fomtools-0.0.4/docs/api/canonicalizer.md +38 -0
  10. {fomtools-0.0.2 → fomtools-0.0.4}/docs/api/codegen.md +4 -0
  11. fomtools-0.0.4/docs/api/constraint.md +17 -0
  12. fomtools-0.0.4/docs/api/index.md +45 -0
  13. fomtools-0.0.4/docs/api/linearexpression.md +6 -0
  14. fomtools-0.0.4/docs/api/proxablefunctions.md +35 -0
  15. {fomtools-0.0.2 → fomtools-0.0.4}/docs/api/solvers.md +4 -1
  16. {fomtools-0.0.2 → fomtools-0.0.4}/docs/api/steps.md +4 -1
  17. fomtools-0.0.4/docs/api/verifier.md +7 -0
  18. fomtools-0.0.4/docs/assets/logo/favicon-16.png +0 -0
  19. fomtools-0.0.4/docs/assets/logo/favicon-32.png +0 -0
  20. fomtools-0.0.4/docs/assets/logo/favicon-48.png +0 -0
  21. fomtools-0.0.4/docs/assets/logo/favicon.svg +1 -0
  22. fomtools-0.0.4/docs/assets/logo/icon-512.png +0 -0
  23. fomtools-0.0.4/docs/assets/logo/icon-dark.svg +1 -0
  24. fomtools-0.0.4/docs/assets/logo/icon-mono.svg +1 -0
  25. fomtools-0.0.4/docs/assets/logo/icon-small.svg +1 -0
  26. fomtools-0.0.4/docs/assets/logo/icon.svg +1 -0
  27. fomtools-0.0.4/docs/assets/logo/logo-dark.svg +1 -0
  28. fomtools-0.0.4/docs/assets/logo/logo-mono.svg +1 -0
  29. fomtools-0.0.4/docs/assets/logo/logo.svg +1 -0
  30. fomtools-0.0.4/docs/get_started.md +140 -0
  31. fomtools-0.0.4/docs/index.md +101 -0
  32. fomtools-0.0.4/docs/quick_start.md +189 -0
  33. fomtools-0.0.4/docs/stylesheets/extra.css +85 -0
  34. {fomtools-0.0.2/docs/tutorials → fomtools-0.0.4/docs/tutorials/codegen}/_recorded_timings.json +2 -2
  35. fomtools-0.0.4/docs/tutorials/codegen/control.md +91 -0
  36. fomtools-0.0.4/docs/tutorials/codegen/control.py +293 -0
  37. fomtools-0.0.4/docs/tutorials/codegen/lasso.md +66 -0
  38. fomtools-0.0.4/docs/tutorials/codegen/lasso.py +230 -0
  39. fomtools-0.0.4/docs/tutorials/codegen/network.md +87 -0
  40. fomtools-0.0.4/docs/tutorials/codegen/network.py +304 -0
  41. fomtools-0.0.4/docs/tutorials/codegen/nnls.md +65 -0
  42. fomtools-0.0.4/docs/tutorials/codegen/nnls.py +205 -0
  43. fomtools-0.0.4/docs/tutorials/codegen/portfolio.md +80 -0
  44. fomtools-0.0.4/docs/tutorials/codegen/portfolio.py +258 -0
  45. fomtools-0.0.4/docs/tutorials/codegen.md +45 -0
  46. {fomtools-0.0.2 → fomtools-0.0.4}/docs/tutorials/cvxpygen.md +28 -15
  47. fomtools-0.0.4/docs/tutorials/cvxpygen_mpc.py +188 -0
  48. fomtools-0.0.4/docs/tutorials/helper_functions/codegen_benchmarks.py +144 -0
  49. fomtools-0.0.4/docs/tutorials/helper_functions/control.py +190 -0
  50. fomtools-0.0.4/docs/tutorials/helper_functions/lasso.py +116 -0
  51. fomtools-0.0.4/docs/tutorials/helper_functions/network.py +164 -0
  52. fomtools-0.0.4/docs/tutorials/helper_functions/nnls.py +85 -0
  53. fomtools-0.0.4/docs/tutorials/helper_functions/pepit_runner.py +411 -0
  54. fomtools-0.0.4/docs/tutorials/helper_functions/portfolio.py +94 -0
  55. fomtools-0.0.4/docs/tutorials/index.md +21 -0
  56. fomtools-0.0.4/docs/tutorials/verification/control.py +590 -0
  57. fomtools-0.0.4/docs/tutorials/verification/lasso.py +475 -0
  58. fomtools-0.0.4/docs/tutorials/verification/network.py +641 -0
  59. fomtools-0.0.4/docs/tutorials/verification/nnls.py +588 -0
  60. fomtools-0.0.4/docs/tutorials/verification/portfolio.py +366 -0
  61. fomtools-0.0.4/docs/tutorials/verification.md +29 -0
  62. {fomtools-0.0.2/docs/tutorials → fomtools-0.0.4/docs/user_guide}/codegen.md +186 -45
  63. fomtools-0.0.4/docs/user_guide/index.md +293 -0
  64. fomtools-0.0.4/docs/user_guide/sdp.md +76 -0
  65. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/__init__.py +1 -1
  66. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/canonicalizers/canonicalizer.py +10 -9
  67. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/canonicalizers/mip.py +28 -20
  68. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/canonicalizers/obbt_handler.py +4 -4
  69. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/canonicalizers/sdp.py +17 -17
  70. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/canonicalizers/simulator.py +24 -23
  71. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/__init__.py +13 -13
  72. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/__init__.py +10 -10
  73. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/budget.py +1 -1
  74. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/claude_sdk.py +4 -4
  75. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/cli_driver.py +9 -9
  76. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/cli_specs.py +7 -7
  77. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/codex_sdk.py +5 -5
  78. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/driver.py +8 -8
  79. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/pidev_sdk.py +6 -6
  80. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/prompts.py +173 -11
  81. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/sdk_base.py +11 -11
  82. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/stub.py +3 -3
  83. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/cli.py +18 -4
  84. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/config.py +81 -19
  85. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/emit.py +2 -2
  86. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/examples.py +38 -38
  87. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/extract.py +7 -7
  88. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/extract_scheduled.py +10 -10
  89. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/extract_unrolled.py +10 -10
  90. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/factorize.py +3 -3
  91. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/generate.py +379 -154
  92. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/ir.py +65 -27
  93. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/kernel_rules.py +127 -35
  94. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/objective.py +8 -8
  95. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/oracle.py +10 -10
  96. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/postprocess.py +95 -6
  97. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/schedule.py +13 -13
  98. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/spec.py +68 -2
  99. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/targets/base.py +40 -7
  100. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/targets/embedded_c.py +1410 -30
  101. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/targets/large_scale_cuda.py +20 -10
  102. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/targets/large_scale_openmp.py +30 -15
  103. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/constraint.py +85 -18
  104. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/cvxpygen/__init__.py +3 -3
  105. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/cvxpygen/api.py +4 -4
  106. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/cvxpygen/builders.py +36 -35
  107. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/cvxpygen/glue.py +2 -2
  108. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/cvxpygen/interface.py +5 -5
  109. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/cvxpygen/qpdata.py +3 -3
  110. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/cvxpygen/scaling.py +1 -1
  111. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/iteratetrajectory.py +11 -11
  112. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/linearexpression.py +50 -7
  113. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/__init__.py +28 -0
  114. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/elastic_net.py +44 -23
  115. fomtools-0.0.4/fomtools/proxable_functions/hinge_loss.py +111 -0
  116. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/huber_loss.py +38 -30
  117. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/ind_affine.py +48 -34
  118. fomtools-0.0.4/fomtools/proxable_functions/ind_ball_l1.py +105 -0
  119. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/ind_ball_linf.py +44 -11
  120. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/ind_box.py +47 -24
  121. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/ind_half_space.py +37 -14
  122. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/ind_nonnegative.py +34 -10
  123. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/ind_nonpositive.py +33 -10
  124. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/ind_point.py +40 -16
  125. fomtools-0.0.4/fomtools/proxable_functions/ind_polyhedral.py +119 -0
  126. fomtools-0.0.4/fomtools/proxable_functions/ind_simplex.py +92 -0
  127. fomtools-0.0.4/fomtools/proxable_functions/labeled_hinge_loss.py +124 -0
  128. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/least_squares.py +35 -21
  129. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/linear.py +30 -17
  130. fomtools-0.0.4/fomtools/proxable_functions/norm_inf.py +120 -0
  131. fomtools-0.0.4/fomtools/proxable_functions/norm_l1.py +143 -0
  132. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/quadratic.py +46 -25
  133. fomtools-0.0.4/fomtools/proxable_functions/soft_nonnegative.py +120 -0
  134. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/sq_l2_norm.py +19 -13
  135. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/sum_negative.py +33 -22
  136. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/sum_positive.py +34 -18
  137. fomtools-0.0.4/fomtools/proxable_functions/total_variation_1d.py +157 -0
  138. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/sets/__init__.py +5 -5
  139. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/sets/affine_subspace.py +3 -3
  140. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/sets/ball.py +4 -4
  141. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/sets/box.py +10 -10
  142. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/sets/polyhedron.py +4 -4
  143. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/sets/set.py +12 -12
  144. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/solvers/cuclarabel.py +1 -1
  145. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/solvers/factory.py +17 -0
  146. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/solvers/gurobi.py +36 -17
  147. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/solvers/highs.py +20 -16
  148. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/stepdecomposers/sdp_step_decomposer.py +1 -1
  149. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/stepdecomposers/simulator_step_decomposer.py +19 -18
  150. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/stepdecomposers/step_decomposer.py +46 -42
  151. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/steps/affine.py +7 -6
  152. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/steps/lin_prog.py +5 -5
  153. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/steps/quad_prog.py +5 -5
  154. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/steps/relu.py +6 -6
  155. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/steps/saturated_linear.py +6 -6
  156. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/steps/soft_threshold.py +3 -3
  157. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/steps/step.py +6 -6
  158. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/utils/matrix_utils.py +2 -2
  159. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/verifier.py +368 -249
  160. {fomtools-0.0.2 → fomtools-0.0.4/fomtools.egg-info}/PKG-INFO +91 -42
  161. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools.egg-info/SOURCES.txt +56 -12
  162. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools.egg-info/requires.txt +10 -1
  163. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools.egg-info/scm_file_list.json +57 -13
  164. fomtools-0.0.4/fomtools.egg-info/scm_version.json +8 -0
  165. fomtools-0.0.4/mkdocs.yml +112 -0
  166. fomtools-0.0.4/overrides/main.html +11 -0
  167. fomtools-0.0.4/overrides/partials/logo.html +3 -0
  168. {fomtools-0.0.2 → fomtools-0.0.4}/pyproject.toml +12 -1
  169. fomtools-0.0.4/scripts/export_marimo.py +142 -0
  170. {fomtools-0.0.2 → fomtools-0.0.4}/skills/README.md +2 -2
  171. {fomtools-0.0.2 → fomtools-0.0.4}/skills/fomtools/SKILL.md +42 -18
  172. {fomtools-0.0.2 → fomtools-0.0.4}/skills/fomtools/references/api.md +44 -23
  173. {fomtools-0.0.2 → fomtools-0.0.4}/skills/fomtools/references/codegen.md +21 -15
  174. {fomtools-0.0.2 → fomtools-0.0.4}/skills/fomtools/references/cvxpygen.md +4 -0
  175. {fomtools-0.0.2 → fomtools-0.0.4}/skills/update-prompt.md +3 -2
  176. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/conftest.py +46 -44
  177. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cli.py +8 -7
  178. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_dispatch.py +6 -6
  179. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_dedup.py +3 -3
  180. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_editable_ops.py +45 -8
  181. fomtools-0.0.4/tests/codegen/test_fused_ops.py +383 -0
  182. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_ir_extraction.py +13 -13
  183. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_large_scale.py +38 -1
  184. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_objective_termination.py +33 -30
  185. fomtools-0.0.4/tests/codegen/test_optimization_rounds.py +225 -0
  186. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_scheduled_ir.py +19 -19
  187. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_static_constraints.py +23 -7
  188. fomtools-0.0.4/tests/codegen/test_step_iterate.py +391 -0
  189. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_unrolled_ir.py +1 -1
  190. {fomtools-0.0.2 → fomtools-0.0.4}/tests/conftest.py +3 -3
  191. {fomtools-0.0.2 → fomtools-0.0.4}/tests/examples/comparison_lasso.py +14 -15
  192. {fomtools-0.0.2 → fomtools-0.0.4}/tests/examples/cut_experiment.py +15 -17
  193. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_add_constraint.py +30 -29
  194. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_affine_simulator.py +6 -6
  195. fomtools-0.0.4/tests/test_api.py +554 -0
  196. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_audit_regressions.py +31 -32
  197. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_boxpgd_sdp_mip.py +18 -17
  198. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_complementarity.py +39 -38
  199. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_convex_hull_cuts.py +30 -22
  200. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_copy_isolation.py +19 -18
  201. fomtools-0.0.4/tests/test_docs_examples.py +83 -0
  202. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_douglas_rachford.py +8 -7
  203. fomtools-0.0.4/tests/test_export_marimo.py +206 -0
  204. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_fixed_point_ista.py +9 -8
  205. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_fixed_point_nnls.py +11 -10
  206. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_ind_ball_l1.py +16 -15
  207. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_ind_polyhedral.py +20 -19
  208. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_ind_simplex.py +17 -16
  209. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_inf_norm_sdp.py +37 -36
  210. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_infinite_bounds.py +10 -10
  211. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_ista_sdp_mip.py +18 -19
  212. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_iterate_trajectory.py +45 -44
  213. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_l1_obj_norm.py +94 -93
  214. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_linear_program.py +7 -6
  215. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_linsolve_simulator.py +19 -18
  216. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_mip_solution_consistency.py +34 -33
  217. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_nn_verify_jax.py +6 -5
  218. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_nn_verify_torch.py +6 -5
  219. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_nnls_sdp_mip.py +9 -8
  220. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_norm_inf.py +19 -20
  221. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_obbt_handler.py +27 -23
  222. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_objective_bounds.py +18 -17
  223. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_partial_relu.py +7 -6
  224. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_partial_relu_sdp_sim.py +25 -24
  225. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_prox_decomposer.py +93 -92
  226. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_quadratic_program.py +22 -23
  227. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_relu_simulator.py +3 -3
  228. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_satlin.py +52 -51
  229. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_satlin_simulator.py +3 -3
  230. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_sdp_canonicalizer.py +13 -12
  231. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_sdp_cuts.py +114 -113
  232. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_sdp_hull.py +9 -8
  233. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_sdp_inequality.py +16 -15
  234. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_sdp_nnls.py +17 -16
  235. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_sets.py +15 -18
  236. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_simulator.py +35 -34
  237. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_soft_threshold_simulator.py +3 -3
  238. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_solve_kwargs.py +9 -8
  239. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_stack.py +26 -25
  240. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_subproblem_simulator.py +15 -14
  241. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_sum_squares.py +15 -14
  242. fomtools-0.0.4/tests/test_tighten_residuals.py +122 -0
  243. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_total_variation_1d.py +19 -22
  244. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_two_norm_mip.py +31 -30
  245. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_verifier.py +13 -12
  246. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_verifier_ista.py +7 -6
  247. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_warm_start.py +17 -16
  248. {fomtools-0.0.2 → fomtools-0.0.4}/uv.lock +35 -2
  249. fomtools-0.0.2/docs/api/canonicalizer.md +0 -8
  250. fomtools-0.0.2/docs/api/constraint.md +0 -5
  251. fomtools-0.0.2/docs/api/linearexpression.md +0 -1
  252. fomtools-0.0.2/docs/api/proxablefunctions.md +0 -45
  253. fomtools-0.0.2/docs/api/verifier.md +0 -2
  254. fomtools-0.0.2/docs/api.md +0 -11
  255. fomtools-0.0.2/docs/index.md +0 -101
  256. fomtools-0.0.2/docs/installation.md +0 -41
  257. fomtools-0.0.2/docs/tutorials/control.py +0 -343
  258. fomtools-0.0.2/docs/tutorials/cvxpygen_mpc.py +0 -147
  259. fomtools-0.0.2/docs/tutorials/lasso.py +0 -303
  260. fomtools-0.0.2/docs/tutorials/network.py +0 -377
  261. fomtools-0.0.2/docs/tutorials/nnls.py +0 -292
  262. fomtools-0.0.2/docs/tutorials/nnls_verification.py +0 -183
  263. fomtools-0.0.2/docs/tutorials/portfolio.py +0 -290
  264. fomtools-0.0.2/docs/tutorials.md +0 -18
  265. fomtools-0.0.2/fomtools/proxable_functions/hinge_loss.py +0 -72
  266. fomtools-0.0.2/fomtools/proxable_functions/ind_ball_l1.py +0 -67
  267. fomtools-0.0.2/fomtools/proxable_functions/ind_polyhedral.py +0 -86
  268. fomtools-0.0.2/fomtools/proxable_functions/ind_simplex.py +0 -56
  269. fomtools-0.0.2/fomtools/proxable_functions/labeled_hinge_loss.py +0 -74
  270. fomtools-0.0.2/fomtools/proxable_functions/norm_inf.py +0 -80
  271. fomtools-0.0.2/fomtools/proxable_functions/norm_l1.py +0 -109
  272. fomtools-0.0.2/fomtools/proxable_functions/soft_nonnegative.py +0 -85
  273. fomtools-0.0.2/fomtools/proxable_functions/total_variation_1d.py +0 -93
  274. fomtools-0.0.2/fomtools.egg-info/scm_version.json +0 -8
  275. fomtools-0.0.2/mkdocs.yml +0 -53
  276. fomtools-0.0.2/tests/test_docs_examples.py +0 -16
  277. fomtools-0.0.2/wrangler.toml +0 -5
  278. {fomtools-0.0.2 → fomtools-0.0.4}/.github/workflows/ci.yml +0 -0
  279. {fomtools-0.0.2 → fomtools-0.0.4}/.github/workflows/claude-code-review.yml +0 -0
  280. {fomtools-0.0.2 → fomtools-0.0.4}/.github/workflows/claude.yml +0 -0
  281. {fomtools-0.0.2 → fomtools-0.0.4}/.github/workflows/release.yml +0 -0
  282. {fomtools-0.0.2 → fomtools-0.0.4}/.pre-commit-config.yaml +0 -0
  283. {fomtools-0.0.2 → fomtools-0.0.4}/CLAUDE.md +0 -0
  284. {fomtools-0.0.2 → fomtools-0.0.4}/LICENSE +0 -0
  285. {fomtools-0.0.2 → fomtools-0.0.4}/docs/javascripts/mathjax.js +0 -0
  286. /fomtools-0.0.2/docs/tutorials/codegen_speedup_results.md → /fomtools-0.0.4/docs/tutorials/codegen/speedup_results.md +0 -0
  287. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/canonicalizers/__init__.py +0 -0
  288. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/agent/agents.yaml +0 -0
  289. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/dedup.py +0 -0
  290. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/harness.py +0 -0
  291. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/targets/__init__.py +0 -0
  292. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/codegen/wrapper.py +0 -0
  293. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/nn/__init__.py +0 -0
  294. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/nn/layers.py +0 -0
  295. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/proxable_functions/proximal_operator.py +0 -0
  296. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/solvers/__init__.py +0 -0
  297. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/solvers/clarabel.py +0 -0
  298. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/solvers/mip_solver.py +0 -0
  299. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/solvers/sdp_solver.py +0 -0
  300. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/solvers/solver.py +0 -0
  301. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/stepdecomposers/__init__.py +0 -0
  302. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/stepdecomposers/mip_step_decomposer.py +0 -0
  303. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/steps/__init__.py +0 -0
  304. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/utils/__init__.py +0 -0
  305. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/utils/convex_hull_cuts.py +0 -0
  306. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/utils/dict_utils.py +0 -0
  307. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/utils/envelopes.py +0 -0
  308. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/utils/math_utils.py +0 -0
  309. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/utils/range_utils.py +0 -0
  310. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools/utils/sparse_utils.py +0 -0
  311. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools.egg-info/dependency_links.txt +0 -0
  312. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools.egg-info/entry_points.txt +0 -0
  313. {fomtools-0.0.2 → fomtools-0.0.4}/fomtools.egg-info/top_level.txt +0 -0
  314. {fomtools-0.0.2 → fomtools-0.0.4}/setup.cfg +0 -0
  315. {fomtools-0.0.2 → fomtools-0.0.4}/tests/__init__.py +0 -0
  316. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/__init__.py +0 -0
  317. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/boxqp/embedded_c/fom_solver.c +0 -0
  318. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/cgqp/large_scale_cuda/fom_solver.cu +0 -0
  319. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/cgqp/large_scale_openmp/fom_solver.c +0 -0
  320. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/cvxpygen_qp/embedded_c/fom_solver.c +0 -0
  321. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/cvxpygen_qp_objective/embedded_c/fom_solver.c +0 -0
  322. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/dr/embedded_c/fom_solver.c +0 -0
  323. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/fista/embedded_c/fom_solver.c +0 -0
  324. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/fista_objective/embedded_c/fom_solver.c +0 -0
  325. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/halpern/embedded_c/fom_solver.c +0 -0
  326. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/indef_kkt/embedded_c/fom_solver.c +0 -0
  327. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/ista/embedded_c/fom_solver.c +0 -0
  328. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/ista/large_scale_cuda/fom_solver.cu +0 -0
  329. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/ista/large_scale_openmp/fom_solver.c +0 -0
  330. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/ista_objective/embedded_c/fom_solver.c +0 -0
  331. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/ista_objective/large_scale_cuda/fom_solver.cu +0 -0
  332. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/mpc/embedded_c/fom_solver.c +0 -0
  333. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/pdhg_lp/embedded_c/fom_solver.c +0 -0
  334. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/pdhg_lp/large_scale_cuda/fom_solver.cu +0 -0
  335. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/scheduled/embedded_c/fom_solver.c +0 -0
  336. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/scheduled_objective/embedded_c/fom_solver.c +0 -0
  337. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/golden/scheduled_objective/large_scale_cuda/fom_solver.cu +0 -0
  338. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_agent_budget.py +0 -0
  339. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_agent_integration.py +0 -0
  340. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_agent_notes.py +0 -0
  341. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_codegen_dispatch.py +0 -0
  342. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_bounds.py +0 -0
  343. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_builders.py +0 -0
  344. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_e2e.py +0 -0
  345. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_glue.py +0 -0
  346. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_objective.py +0 -0
  347. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_preflight.py +0 -0
  348. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_qpdata.py +0 -0
  349. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_register.py +0 -0
  350. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_scaling.py +0 -0
  351. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_cvxpygen_scheduled.py +0 -0
  352. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_dr_pipeline.py +0 -0
  353. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_editable_linsolve.py +0 -0
  354. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_emit_scaffold.py +0 -0
  355. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_examples_scale.py +0 -0
  356. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_factorize.py +0 -0
  357. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_fista_pipeline.py +0 -0
  358. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_float32.py +0 -0
  359. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_indefinite_kkt.py +0 -0
  360. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_oracle.py +0 -0
  361. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_pdhg_pipeline.py +0 -0
  362. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_postprocess.py +0 -0
  363. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_profiling.py +0 -0
  364. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_satlin_mpc.py +0 -0
  365. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_scheduled_pipeline.py +0 -0
  366. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_spec_roundtrip.py +0 -0
  367. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_stationarity_gate.py +0 -0
  368. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_stub_pipeline.py +0 -0
  369. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_termination_interval.py +0 -0
  370. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_unrolled_emission.py +0 -0
  371. {fomtools-0.0.2 → fomtools-0.0.4}/tests/codegen/test_wrapper_roundtrip.py +0 -0
  372. {fomtools-0.0.2 → fomtools-0.0.4}/tests/cvxpy.py +0 -0
  373. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_LP_pdhg.py +0 -0
  374. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_ista.py +0 -0
  375. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_linearexpression.py +0 -0
  376. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_mincost_flow.py +0 -0
  377. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_placeholder.py +0 -0
  378. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_portfolio.py +0 -0
  379. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_range_functions.py +0 -0
  380. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_sdp_selector.py +0 -0
  381. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_solver_boundary.py +0 -0
  382. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_sparse_utils.py +0 -0
  383. {fomtools-0.0.2 → fomtools-0.0.4}/tests/test_vec_LUhat.py +0 -0
@@ -4,7 +4,7 @@ on:
4
4
  push:
5
5
  branches: [ main ]
6
6
  pull_request:
7
- branches: [ main ]
7
+ branches: [ main, documentation ]
8
8
 
9
9
  permissions:
10
10
  contents: write
@@ -54,13 +54,10 @@ jobs:
54
54
  - name: Export marimo notebooks
55
55
  env:
56
56
  GRB_LICENSE_FILE: ${{ github.workspace }}/gurobi.lic
57
- run: |
58
- for file in docs/tutorials/*.py; do
59
- marimo export html "$file" -o "${file%.py}.html"
60
- done
57
+ run: python scripts/export_marimo.py --force
61
58
 
62
59
  - name: Verify html generation
63
- run: ls docs/tutorials
60
+ run: ls -R docs/tutorials
64
61
 
65
62
  - name: Configure Git Credentials
66
63
  run: |
@@ -79,11 +76,10 @@ jobs:
79
76
  mkdocs-material-
80
77
 
81
78
  - name: Build MkDocs site
82
- run: mkdocs build
79
+ run: mkdocs build --strict
83
80
 
84
- - name: Deploy to Cloudflare Pages with Wrangler
85
- uses: cloudflare/wrangler-action@v4.0.0
86
- with:
87
- apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
88
- accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
89
- command: deploy
81
+ # Pull requests only build the site; pushes to main publish it to the gh-pages
82
+ # branch, which GitHub Pages serves at https://stellatogrp.github.io/fomtools/
83
+ - name: Deploy to GitHub Pages
84
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
85
+ run: mkdocs gh-deploy --force
@@ -20,6 +20,8 @@ jobs:
20
20
  - name: Checkout repository
21
21
  uses: actions/checkout@v4
22
22
  with:
23
+ # The PR branch itself (not the detached merge ref), so the sync commit can be pushed.
24
+ ref: ${{ github.head_ref }}
23
25
  fetch-depth: 1
24
26
 
25
27
  - name: Run Claude Code to update skill
@@ -27,17 +29,8 @@ jobs:
27
29
  uses: anthropics/claude-code-action@v1
28
30
  with:
29
31
  claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
30
- prompt_file: skills/update-prompt.md
31
-
32
- allowed_tools: |
33
- Bash(git status)
34
- Bash(git diff:*)
35
- Bash(git log:*)
36
- Bash(git add:*)
37
- Bash(git commit:*)
38
- Bash(find:*)
39
- Bash(grep:*)
40
- Bash(wc:*)
41
- Read
42
- Edit
43
- Write
32
+ # v1 inputs: `prompt` and `claude_args` (the v0 `prompt_file` and `allowed_tools`
33
+ # are ignored, and without a prompt the action skips silently).
34
+ prompt: 'Follow the instructions in skills/update-prompt.md.'
35
+ claude_args: >-
36
+ --allowedTools "Bash(git status),Bash(git diff:*),Bash(git log:*),Bash(git add:*),Bash(git commit:*),Bash(git push:*),Bash(find:*),Bash(grep:*),Bash(wc:*),Read,Edit,Write"
@@ -385,3 +385,8 @@ fomtools.project
385
385
  __marimo__/
386
386
  *.npz
387
387
  .ruff_cache/
388
+
389
+ .vscode/
390
+
391
+ # `marimo export html` output, generated by the docs workflow at build time.
392
+ docs/tutorials/**/*.html
@@ -82,3 +82,30 @@ execution of the CUDA checks; `FOMTOOLS_CODEGEN_BACKEND` selects the agent.
82
82
  - When the public API or docs change, the `update-skill` workflow refreshes
83
83
  `skills/fomtools/`; check its output rather than editing the skill by hand
84
84
  in the same PR.
85
+
86
+ ## Tutorial math notation
87
+
88
+ All `mo.md` cells in `docs/tutorials/` (verification and codegen notebooks) use one notation per concept, following Boyd-style conventions. Keep new notebooks in sync with this table.
89
+
90
+ | Concept | Write | Never |
91
+ |---|---|---|
92
+ | Reals, ones | `\mathbf{R}`, `\mathbf{R}^n_+`, `\mathbf{1}` | `{\bf R}`, `\R`, `\mathbb{R}` |
93
+ | Transpose | `A^T` | `A^\top` |
94
+ | Calligraphic | `\mathcal{X}` | `{\cal X}` |
95
+ | Problem keywords | `\text{minimize}`, `\text{maximize}`, `\text{subject to}` | `\textnormal{...}` |
96
+ | Delimiters | inline `$...$`, display `$$...$$`, problems and updates as bare `\begin{array}{ll}`/`{rcl}` | `\(...\)`, `\[...\]` |
97
+ | Soft-thresholding | `\mathbf{soft}_{\lambda\eta}` (`soft_threshold` step) | `{\cal T}_\lambda`, `S_\lambda` |
98
+ | Projection | `\Pi_C`: `\Pi_{\mathbf{R}^n_+}` (`relu` step), `\Pi_{[l,u]}` (`satlin` step / `IndBox`) | `\mathbf{ReLU}`, `\mathbf{prox}_{\mathbb{I}_C}` |
99
+ | Proximal operator | `\mathbf{prox}_f` | `\operatorname{prox}` |
100
+ | Fractions | inline slash: `(1/2) z^T P z`, `(t_k - 1)/t_{k+1}` | `\frac` |
101
+
102
+ Norms always carry a subscript: `\|v\|_2`, `\|v\|_\infty` (never `\lVert v \rVert` or a bare `\|v\|`).
103
+ Define soft-thresholding where it is first used: `\mathbf{soft}_\kappa(v)_i = \mathbf{sign}(v_i)\max\{|v_i| - \kappa, 0\}`, applied element-wise.
104
+ Matplotlib labels (`label=`, `xlabel`, `ylabel`, `title`) are rendered by mathtext, not KaTeX: write `\geq`/`\leq` there (mathtext rejects `\ge`/`\le`), and keep `\|v\|_2` norms.
105
+
106
+ Symbol roles: `x` problem parameter in `\mathcal{X}`; `z` decision variable and primal iterate, `z^0 \in \mathcal{Z}`, `z^\star` solution; `v, w` dual or auxiliary iterates; `y` (lasso) or `w` (NNLS) for the un-extrapolated proximal output of a momentum method, matching the helper code; `k` iteration, `K` horizon, `\eta` step size, `L` smoothness constant, `r^k` residual, `R` PEP initial radius.
107
+ Search-box bounds are `x_\mathrm{lb}, x_\mathrm{ub}` and `z^0_\mathrm{lb}, z^0_\mathrm{ub}`; problem-data bounds keep `_{\min}, _{\max}`. Code names such as `x_lb` or `s0_samples` appear only in backticks in prose. When a math letter differs from the code name, say so once.
108
+
109
+ Iterations are written `k \to k+1` for `k = 0, 1, \dots`. A momentum coefficient `\beta_k` uses the same index as the helper code (slot `k` uses `beta_k`, and `k` starts at 1), so the update reads `z^{k+1} = w^{k+1} + \beta_{k+1}(w^{k+1} - w^k)` with `\beta_1 = 0`. Use "Nesterov-type momentum" for `\beta_k = (k-1)/(k+2)` and reserve "FISTA" for the `t_k` schedule `\beta_k = (t_{k-1}-1)/t_k`.
110
+
111
+ Known clashes kept on purpose because both uses are standard: control input `u_t` vs. OSQP upper bound `u(x)`, LQR weight `R` vs. PEP radius `R`, and `\mu` as network cost, portfolio return, or strong-convexity constant.
@@ -0,0 +1,34 @@
1
+ # Contributing to FOMTools
2
+
3
+ ## Development setup
4
+
5
+ Follow the environment instructions in [docs/get_started.md](docs/get_started.md) to create a
6
+ virtual environment and install `fomtools` in editable mode with its `dev` and `docs` extras.
7
+
8
+ Install the pre-commit hooks once per clone:
9
+
10
+ pre-commit install
11
+
12
+ ## Tests and linting
13
+
14
+ pytest # Run the test suite
15
+ ruff check # Check code style
16
+ ruff format # Auto-format
17
+ pre-commit run --all-files # Run all hooks
18
+
19
+ ## Previewing the documentation
20
+
21
+ Install the documentation dependencies and start the local MkDocs server:
22
+
23
+ uv sync --extra docs
24
+ make docs-serve
25
+
26
+ The `docs-serve` target recursively discovers marimo notebooks under `docs/tutorials`, exports
27
+ missing or stale notebooks to ignored HTML files, and then starts MkDocs. Restart the server after
28
+ editing a notebook so that its HTML is refreshed.
29
+
30
+ Use these targets to export or validate the documentation without starting the server:
31
+
32
+ make docs-export # Export only missing or stale notebooks
33
+ make docs-export-force # Regenerate every notebook
34
+ make docs-build # Force export, then run a strict MkDocs build
@@ -0,0 +1,13 @@
1
+ .PHONY: docs-export docs-export-force docs-serve docs-build
2
+
3
+ docs-export:
4
+ uv run --extra docs python scripts/export_marimo.py
5
+
6
+ docs-export-force:
7
+ uv run --extra docs python scripts/export_marimo.py --force
8
+
9
+ docs-serve: docs-export
10
+ uv run --extra docs mkdocs serve
11
+
12
+ docs-build: docs-export-force
13
+ uv run --extra docs mkdocs build --strict
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fomtools
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: FOMTools: A Python Toolbox for Analysis of First-Order Methods
5
5
  Author-email: Vinit Ranjan <vranjan1@mit.edu>, Daniel Deza <dd7022@princeton.edu>, Anushka Acharya <aa9078@princeton.edu>, Jisun Park <jisunpark@princeton.edu>, Bartolomeo Stellato <bstellato@princeton.edu>
6
6
  License: Apache 2.0
@@ -41,22 +41,40 @@ Provides-Extra: nn
41
41
  Requires-Dist: torch; extra == "nn"
42
42
  Requires-Dist: jax<0.11.2; extra == "nn"
43
43
  Requires-Dist: flax; extra == "nn"
44
+ Provides-Extra: all
45
+ Requires-Dist: fomtools[codegen,cvxpygen,gurobi,nn]; extra == "all"
46
+ Requires-Dist: fomtools[gpu]; sys_platform != "darwin" and extra == "all"
47
+ Requires-Dist: fomtools[cuda]; extra == "all"
44
48
  Provides-Extra: docs
45
49
  Requires-Dist: fomtools[gurobi]; extra == "docs"
46
50
  Requires-Dist: marimo; extra == "docs"
47
51
  Requires-Dist: matplotlib; extra == "docs"
48
- Requires-Dist: mkdocs; extra == "docs"
52
+ Requires-Dist: mkdocs<2,>=1.6; extra == "docs"
49
53
  Requires-Dist: mkdocstrings-python; extra == "docs"
50
54
  Requires-Dist: mkdocs-marimo; extra == "docs"
51
55
  Requires-Dist: mkdocs-material; extra == "docs"
56
+ Requires-Dist: networkx; extra == "docs"
57
+ Requires-Dist: pepit; extra == "docs"
52
58
  Requires-Dist: pymdown-extensions; extra == "docs"
53
59
  Dynamic: license-file
54
60
 
55
- FOMTools
56
- ========
61
+ <h1>
62
+ <picture>
63
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/stellatogrp/fomtools/main/docs/assets/logo/logo-dark.svg">
64
+ <img alt="FOMTools" src="https://raw.githubusercontent.com/stellatogrp/fomtools/main/docs/assets/logo/logo.svg" height="72">
65
+ </picture>
66
+ </h1>
67
+
68
+ [![PyPI](https://img.shields.io/pypi/v/fomtools.svg)](https://pypi.org/project/fomtools/)
69
+ [![Docs](https://img.shields.io/badge/docs-stellatogrp.github.io%2Ffomtools-blue.svg)](https://stellatogrp.github.io/fomtools/)
57
70
 
58
71
  **Development, analysis, and code generation for first-order methods.**
59
72
 
73
+ [Documentation](https://stellatogrp.github.io/fomtools/) |
74
+ [PyPI](https://pypi.org/project/fomtools/) |
75
+ [Paper](https://arxiv.org/abs/2610.26743) |
76
+ [Benchmarks](https://github.com/stellatogrp/fomtools_experiments)
77
+
60
78
  FOMTools is an open-source Python package for developing, analyzing, and deploying
61
79
  first-order methods for parametric linear and convex quadratic optimization. You write the
62
80
  iteration in Python, with syntax close to its mathematical description, and FOMTools records
@@ -75,65 +93,73 @@ you can:
75
93
 
76
94
  ## Example
77
95
 
78
- The following code verifies FISTA on a family of Lasso problems
79
- ```
80
- minimize 0.5 ||D s - x||_2^2 + lambda ||s||_1
81
- ```
96
+ The following code verifies projected gradient descent on the parametric quadratic program
97
+
98
+ $$
99
+ \begin{array}{ll}
100
+ \text{minimize} & (1/2) z^T P z + q^T z \\
101
+ \text{subject to} & z \geq 0,
102
+ \end{array}
103
+ $$
82
104
 
83
- where `s in R^n` is the optimization variable and the data vector `x` ranges over the box
84
- `[0, 1]^m`. After each iteration it computes the worst-case fixed-point residual
85
- `||s^{k+1} - s^k||_inf` over all such `x`.
105
+ where $z \in \mathbf{R}^n$ is the optimization variable, $P$ is a fixed symmetric positive
106
+ semidefinite matrix, and the parameter $q$ ranges over the box $[-1, 1]^n$. Starting from
107
+ $z^0 = 0$, each iteration takes a gradient step with step size $\eta$ and projects onto the
108
+ nonnegative orthant,
109
+
110
+ $$
111
+ z^{k+1} = \Pi_{\mathbf{R}^n_+}\left(z^k - \eta(Pz^k + q)\right),
112
+ $$
113
+
114
+ and FOMTools computes the worst-case fixed-point residual $`\|z^K - z^{K-1}\|_\infty`$ over all
115
+ $q \in [-1, 1]^n$.
86
116
 
87
117
  ```python3
88
118
  import numpy as np
89
119
  import fomtools as ft
90
120
 
91
- # Problem data
92
- m, n, lambd, K = 10, 15, 0.1, 10
93
- D = np.random.normal(size=(m, n))
94
- eta = 1 / np.linalg.norm(D, 2) ** 2
95
-
96
- VP = ft.Verifier() # MIP verification with HiGHS; use solver="gurobi" for Gurobi
121
+ K, n, eta = 5, 3, 0.01
122
+ A = np.random.default_rng(0).normal(size=(n, n))
123
+ P = A @ A.T
97
124
 
98
- # Parameter set and initial iterate
99
- x = VP.add_param(m, lb=0, ub=1)
100
- s = [VP.add_iterate(n, lb=0, ub=0)]
101
- w, beta = s[0], 1.0
125
+ vp = ft.Verifier() # HiGHS by default; solver="gurobi" for Gurobi
126
+ q = vp.parameter(n, lb=-1, ub=1) # q in [-1, 1]^n
127
+ z = vp.iterate(n, lb=0, ub=0) # z^0 = 0
102
128
 
103
- results = []
104
129
  for k in range(K):
105
- # FISTA iteration
106
- s.append(VP.step.soft_threshold(w - eta * D.T @ (D @ w - x), lambd=lambd * eta))
107
- beta_old, beta = beta, 0.5 * (1 + np.sqrt(1 + 4 * beta**2))
108
- w = s[k + 1] + (beta_old - 1) / beta * (s[k + 1] - s[k])
109
-
110
- # Worst-case residual after k + 1 iterations
111
- VP.set_objective_norm(s[k + 1] - s[k], np.inf)
112
- results.append(VP.solve())
130
+ z_prev, z = z, vp.relu(z - eta * (P @ z + q))
131
+
132
+ vp.maximize(ft.norm(z - z_prev, np.inf))
133
+ print("worst-case residual:", vp.solve())
134
+ print("worst-case q:", q.value)
113
135
  ```
114
136
 
115
137
  Steps can also be written with proximal operators and projections, e.g.,
116
- `VP.prox(NormL1(lambd), v, eta)` or `VP.project(Box(l, u), v)`. See the
117
- [documentation](https://github.com/stellatogrp/fomtools/tree/main/docs) for the full list of proxable functions and sets.
138
+ `vp.prox(NormL1(lambd), v, eta)` or `vp.project(Box(l, u), v)`. See the
139
+ [documentation](https://stellatogrp.github.io/fomtools/) for the full list of proxable functions and sets.
118
140
 
119
141
  ## Beyond worst-case verification
120
142
 
121
143
  **Simulation.** Write the same loop with `ft.Verifier(method="simulator")`, and give
122
- parameters sample arrays of shape `(N, d)` (or let them be sampled from their bounds). The
144
+ parameters sample arrays of shape `(N, d)`, as in `vp.parameter(d, samples=X)`. The
123
145
  simulator runs all samples as batched operations, on a GPU through CuPy when one is
124
146
  available. It then reports summary statistics and a KL-inverse bound on the probability that
125
147
  the residual exceeds a tolerance `epsilon`:
126
148
 
127
149
  ```python3
128
- VP.solve(epsilon=1e-3)
129
- VP["quantiles"][0.95], VP["risk_bound"], VP["throughput"]
150
+ vp.solve(epsilon=1e-3)
151
+ vp["quantiles"][0.95], vp["risk_bound"], vp["throughput"]
130
152
  ```
131
153
 
132
- **Code generation.** Pass the recorded iterates, unchanged, to `codegen`. It produces a
133
- specialized solver together with a Python wrapper:
154
+ **Code generation.** Keep the iterates in a list and pass them, unchanged, to `codegen`.
155
+ It produces a specialized solver together with a Python wrapper:
134
156
 
135
157
  ```python3
136
- result = VP.codegen("out/fista", iterates=s, code_optimization=True)
158
+ z = [vp.iterate(n, lb=0, ub=0)]
159
+ for k in range(K):
160
+ z.append(vp.relu(z[-1] - eta * (P @ z[-1] + q)))
161
+
162
+ result = vp.codegen("out/pgd", iterates=z, code_optimization=True)
137
163
  ```
138
164
 
139
165
  Targets include embedded C, multicore CPUs (OpenMP), and GPUs (CUDA). The package works with
@@ -174,11 +200,26 @@ Optional extras:
174
200
  | `codegen` | LLM coding-agent backends for code generation |
175
201
  | `cvxpygen` | CVXPYgen integration |
176
202
  | `nn` | Neural-network verification (PyTorch, JAX, Flax) |
203
+ | `all` | All of the above (GPU packages skipped on macOS) |
177
204
  | `dev` | Development tools |
178
205
  | `docs` | Documentation build dependencies |
179
206
 
180
207
  For example, `pip install "fomtools[gurobi,codegen]"`, or
181
- `pip install -e ".[gurobi,codegen]"` from source.
208
+ `pip install -e ".[gurobi,codegen]"` from source. Use `pip install "fomtools[all]"`
209
+ to install every optional feature.
210
+
211
+ ### Agent skill
212
+
213
+ FOMTools ships an [Agent Skill](https://agentskills.io/) in
214
+ [`skills/fomtools`](skills/fomtools/SKILL.md) that teaches coding agents (Claude
215
+ Code, Codex, Cursor, and others) the FOMTools API. Install it with the
216
+ [`skills` CLI](https://github.com/vercel-labs/skills) (requires Node.js):
217
+
218
+ ```bash
219
+ npx skills add stellatogrp/fomtools -g
220
+ ```
221
+
222
+ See [Get started](https://stellatogrp.github.io/fomtools/get_started/) for details.
182
223
 
183
224
  FOMTools is not a solver. It relies on mathematical programming solvers such as
184
225
  [HiGHS](https://highs.dev) (the default) and [Gurobi](https://www.gurobi.com) to solve
@@ -186,9 +227,14 @@ the verification problems it builds.
186
227
 
187
228
  ## Examples
188
229
 
189
- The [tutorials](https://github.com/stellatogrp/fomtools/tree/main/docs/tutorials) cover sparse coding (Lasso), nonnegative least squares,
190
- network optimization, optimal control, and portfolio rebalancing. Code to reproduce the
191
- experiments in our [paper](https://arxiv.org/abs/2610.26743) is available at
230
+ The [tutorials](https://stellatogrp.github.io/fomtools/tutorials/) cover sparse coding (Lasso), nonnegative least squares,
231
+ network optimization, optimal control, and portfolio rebalancing.
232
+
233
+ ## Benchmarks
234
+
235
+ The benchmarks and the code to reproduce the experiments in our
236
+ [paper](https://arxiv.org/abs/2610.26743) (verification, simulation, and code generation on
237
+ CPU and GPU) are in a separate repository,
192
238
  [stellatogrp/fomtools_experiments](https://github.com/stellatogrp/fomtools_experiments).
193
239
 
194
240
  ## Contributing
@@ -215,3 +261,6 @@ If you use FOMTools for published work, we encourage you to cite our
215
261
  year = {2026},
216
262
  }
217
263
  ```
264
+
265
+ The mixed-integer verification method is described in
266
+ [Exact Verification of First-Order Methods via Mixed-Integer Linear Programming](https://arxiv.org/abs/2412.11330).
@@ -1,8 +1,20 @@
1
- FOMTools
2
- ========
1
+ <h1>
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/stellatogrp/fomtools/main/docs/assets/logo/logo-dark.svg">
4
+ <img alt="FOMTools" src="https://raw.githubusercontent.com/stellatogrp/fomtools/main/docs/assets/logo/logo.svg" height="72">
5
+ </picture>
6
+ </h1>
7
+
8
+ [![PyPI](https://img.shields.io/pypi/v/fomtools.svg)](https://pypi.org/project/fomtools/)
9
+ [![Docs](https://img.shields.io/badge/docs-stellatogrp.github.io%2Ffomtools-blue.svg)](https://stellatogrp.github.io/fomtools/)
3
10
 
4
11
  **Development, analysis, and code generation for first-order methods.**
5
12
 
13
+ [Documentation](https://stellatogrp.github.io/fomtools/) |
14
+ [PyPI](https://pypi.org/project/fomtools/) |
15
+ [Paper](https://arxiv.org/abs/2610.26743) |
16
+ [Benchmarks](https://github.com/stellatogrp/fomtools_experiments)
17
+
6
18
  FOMTools is an open-source Python package for developing, analyzing, and deploying
7
19
  first-order methods for parametric linear and convex quadratic optimization. You write the
8
20
  iteration in Python, with syntax close to its mathematical description, and FOMTools records
@@ -21,65 +33,73 @@ you can:
21
33
 
22
34
  ## Example
23
35
 
24
- The following code verifies FISTA on a family of Lasso problems
25
- ```
26
- minimize 0.5 ||D s - x||_2^2 + lambda ||s||_1
27
- ```
36
+ The following code verifies projected gradient descent on the parametric quadratic program
37
+
38
+ $$
39
+ \begin{array}{ll}
40
+ \text{minimize} & (1/2) z^T P z + q^T z \\
41
+ \text{subject to} & z \geq 0,
42
+ \end{array}
43
+ $$
28
44
 
29
- where `s in R^n` is the optimization variable and the data vector `x` ranges over the box
30
- `[0, 1]^m`. After each iteration it computes the worst-case fixed-point residual
31
- `||s^{k+1} - s^k||_inf` over all such `x`.
45
+ where $z \in \mathbf{R}^n$ is the optimization variable, $P$ is a fixed symmetric positive
46
+ semidefinite matrix, and the parameter $q$ ranges over the box $[-1, 1]^n$. Starting from
47
+ $z^0 = 0$, each iteration takes a gradient step with step size $\eta$ and projects onto the
48
+ nonnegative orthant,
49
+
50
+ $$
51
+ z^{k+1} = \Pi_{\mathbf{R}^n_+}\left(z^k - \eta(Pz^k + q)\right),
52
+ $$
53
+
54
+ and FOMTools computes the worst-case fixed-point residual $`\|z^K - z^{K-1}\|_\infty`$ over all
55
+ $q \in [-1, 1]^n$.
32
56
 
33
57
  ```python3
34
58
  import numpy as np
35
59
  import fomtools as ft
36
60
 
37
- # Problem data
38
- m, n, lambd, K = 10, 15, 0.1, 10
39
- D = np.random.normal(size=(m, n))
40
- eta = 1 / np.linalg.norm(D, 2) ** 2
41
-
42
- VP = ft.Verifier() # MIP verification with HiGHS; use solver="gurobi" for Gurobi
61
+ K, n, eta = 5, 3, 0.01
62
+ A = np.random.default_rng(0).normal(size=(n, n))
63
+ P = A @ A.T
43
64
 
44
- # Parameter set and initial iterate
45
- x = VP.add_param(m, lb=0, ub=1)
46
- s = [VP.add_iterate(n, lb=0, ub=0)]
47
- w, beta = s[0], 1.0
65
+ vp = ft.Verifier() # HiGHS by default; solver="gurobi" for Gurobi
66
+ q = vp.parameter(n, lb=-1, ub=1) # q in [-1, 1]^n
67
+ z = vp.iterate(n, lb=0, ub=0) # z^0 = 0
48
68
 
49
- results = []
50
69
  for k in range(K):
51
- # FISTA iteration
52
- s.append(VP.step.soft_threshold(w - eta * D.T @ (D @ w - x), lambd=lambd * eta))
53
- beta_old, beta = beta, 0.5 * (1 + np.sqrt(1 + 4 * beta**2))
54
- w = s[k + 1] + (beta_old - 1) / beta * (s[k + 1] - s[k])
55
-
56
- # Worst-case residual after k + 1 iterations
57
- VP.set_objective_norm(s[k + 1] - s[k], np.inf)
58
- results.append(VP.solve())
70
+ z_prev, z = z, vp.relu(z - eta * (P @ z + q))
71
+
72
+ vp.maximize(ft.norm(z - z_prev, np.inf))
73
+ print("worst-case residual:", vp.solve())
74
+ print("worst-case q:", q.value)
59
75
  ```
60
76
 
61
77
  Steps can also be written with proximal operators and projections, e.g.,
62
- `VP.prox(NormL1(lambd), v, eta)` or `VP.project(Box(l, u), v)`. See the
63
- [documentation](https://github.com/stellatogrp/fomtools/tree/main/docs) for the full list of proxable functions and sets.
78
+ `vp.prox(NormL1(lambd), v, eta)` or `vp.project(Box(l, u), v)`. See the
79
+ [documentation](https://stellatogrp.github.io/fomtools/) for the full list of proxable functions and sets.
64
80
 
65
81
  ## Beyond worst-case verification
66
82
 
67
83
  **Simulation.** Write the same loop with `ft.Verifier(method="simulator")`, and give
68
- parameters sample arrays of shape `(N, d)` (or let them be sampled from their bounds). The
84
+ parameters sample arrays of shape `(N, d)`, as in `vp.parameter(d, samples=X)`. The
69
85
  simulator runs all samples as batched operations, on a GPU through CuPy when one is
70
86
  available. It then reports summary statistics and a KL-inverse bound on the probability that
71
87
  the residual exceeds a tolerance `epsilon`:
72
88
 
73
89
  ```python3
74
- VP.solve(epsilon=1e-3)
75
- VP["quantiles"][0.95], VP["risk_bound"], VP["throughput"]
90
+ vp.solve(epsilon=1e-3)
91
+ vp["quantiles"][0.95], vp["risk_bound"], vp["throughput"]
76
92
  ```
77
93
 
78
- **Code generation.** Pass the recorded iterates, unchanged, to `codegen`. It produces a
79
- specialized solver together with a Python wrapper:
94
+ **Code generation.** Keep the iterates in a list and pass them, unchanged, to `codegen`.
95
+ It produces a specialized solver together with a Python wrapper:
80
96
 
81
97
  ```python3
82
- result = VP.codegen("out/fista", iterates=s, code_optimization=True)
98
+ z = [vp.iterate(n, lb=0, ub=0)]
99
+ for k in range(K):
100
+ z.append(vp.relu(z[-1] - eta * (P @ z[-1] + q)))
101
+
102
+ result = vp.codegen("out/pgd", iterates=z, code_optimization=True)
83
103
  ```
84
104
 
85
105
  Targets include embedded C, multicore CPUs (OpenMP), and GPUs (CUDA). The package works with
@@ -120,11 +140,26 @@ Optional extras:
120
140
  | `codegen` | LLM coding-agent backends for code generation |
121
141
  | `cvxpygen` | CVXPYgen integration |
122
142
  | `nn` | Neural-network verification (PyTorch, JAX, Flax) |
143
+ | `all` | All of the above (GPU packages skipped on macOS) |
123
144
  | `dev` | Development tools |
124
145
  | `docs` | Documentation build dependencies |
125
146
 
126
147
  For example, `pip install "fomtools[gurobi,codegen]"`, or
127
- `pip install -e ".[gurobi,codegen]"` from source.
148
+ `pip install -e ".[gurobi,codegen]"` from source. Use `pip install "fomtools[all]"`
149
+ to install every optional feature.
150
+
151
+ ### Agent skill
152
+
153
+ FOMTools ships an [Agent Skill](https://agentskills.io/) in
154
+ [`skills/fomtools`](skills/fomtools/SKILL.md) that teaches coding agents (Claude
155
+ Code, Codex, Cursor, and others) the FOMTools API. Install it with the
156
+ [`skills` CLI](https://github.com/vercel-labs/skills) (requires Node.js):
157
+
158
+ ```bash
159
+ npx skills add stellatogrp/fomtools -g
160
+ ```
161
+
162
+ See [Get started](https://stellatogrp.github.io/fomtools/get_started/) for details.
128
163
 
129
164
  FOMTools is not a solver. It relies on mathematical programming solvers such as
130
165
  [HiGHS](https://highs.dev) (the default) and [Gurobi](https://www.gurobi.com) to solve
@@ -132,9 +167,14 @@ the verification problems it builds.
132
167
 
133
168
  ## Examples
134
169
 
135
- The [tutorials](https://github.com/stellatogrp/fomtools/tree/main/docs/tutorials) cover sparse coding (Lasso), nonnegative least squares,
136
- network optimization, optimal control, and portfolio rebalancing. Code to reproduce the
137
- experiments in our [paper](https://arxiv.org/abs/2610.26743) is available at
170
+ The [tutorials](https://stellatogrp.github.io/fomtools/tutorials/) cover sparse coding (Lasso), nonnegative least squares,
171
+ network optimization, optimal control, and portfolio rebalancing.
172
+
173
+ ## Benchmarks
174
+
175
+ The benchmarks and the code to reproduce the experiments in our
176
+ [paper](https://arxiv.org/abs/2610.26743) (verification, simulation, and code generation on
177
+ CPU and GPU) are in a separate repository,
138
178
  [stellatogrp/fomtools_experiments](https://github.com/stellatogrp/fomtools_experiments).
139
179
 
140
180
  ## Contributing
@@ -161,3 +201,6 @@ If you use FOMTools for published work, we encourage you to cite our
161
201
  year = {2026},
162
202
  }
163
203
  ```
204
+
205
+ The mixed-integer verification method is described in
206
+ [Exact Verification of First-Order Methods via Mixed-Integer Linear Programming](https://arxiv.org/abs/2412.11330).
@@ -0,0 +1,38 @@
1
+ # Canonicalizers
2
+
3
+ Canonicalizers translate a verification problem into a solver-ready formulation
4
+ (MIP or SDP), including optimization-based bound tightening (OBBT).
5
+
6
+ ::: fomtools.canonicalizers.Canonicalizer
7
+
8
+ ## MIP canonicalizer
9
+
10
+ ::: fomtools.canonicalizers.mip
11
+ options:
12
+ show_root_heading: false
13
+ show_root_toc_entry: false
14
+ heading_level: 3
15
+
16
+ ## OBBT handler
17
+
18
+ ::: fomtools.canonicalizers.obbt_handler
19
+ options:
20
+ show_root_heading: false
21
+ show_root_toc_entry: false
22
+ heading_level: 3
23
+
24
+ ## Simulator
25
+
26
+ ::: fomtools.canonicalizers.simulator
27
+ options:
28
+ show_root_heading: false
29
+ show_root_toc_entry: false
30
+ heading_level: 3
31
+
32
+ ## SDP canonicalizer
33
+
34
+ ::: fomtools.canonicalizers.sdp
35
+ options:
36
+ show_root_heading: false
37
+ show_root_toc_entry: false
38
+ heading_level: 3
@@ -5,6 +5,10 @@ methods, plus a tiny Python wrapper. An LLM coding agent translates each step of
5
5
  the algorithm into the target language and iterates against a Python reference
6
6
  oracle until the generated code is numerically verified.
7
7
 
8
+ The [code generation guide](../user_guide/codegen.md) is the narrative reference for
9
+ the symbols below, and the [tutorials](../tutorials/codegen.md) work through five
10
+ complete examples.
11
+
8
12
  ::: fomtools.codegen.generate_code
9
13
 
10
14
  ::: fomtools.codegen.CodegenConfig
@@ -0,0 +1,17 @@
1
+ # Constraints
2
+
3
+ Constraints are equalities and inequalities over linear expressions, used to
4
+ encode assumptions on the problem data and the initial conditions.
5
+
6
+ ::: fomtools.constraint.Constraint
7
+ ::: fomtools.constraint.EqualityConstraint
8
+ ::: fomtools.constraint.InequalityConstraint
9
+
10
+ ## Objective atoms
11
+
12
+ `ft.norm` and `ft.sum_squares` build the objective passed to
13
+ [`Verifier.maximize`](verifier.md#fomtools.Verifier.maximize), for example
14
+ `vp.maximize(ft.norm(z - z_prev, np.inf))`.
15
+
16
+ ::: fomtools.constraint.norm
17
+ ::: fomtools.constraint.sum_squares