m4opt 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 (177) hide show
  1. {m4opt-2.2.0 → m4opt-2.3.0}/.github/workflows/ci_cron_weekly.yml +1 -1
  2. {m4opt-2.2.0 → m4opt-2.3.0}/.github/workflows/ci_tests.yml +1 -1
  3. {m4opt-2.2.0 → m4opt-2.3.0}/.pre-commit-config.yaml +1 -1
  4. {m4opt-2.2.0 → m4opt-2.3.0}/CHANGES.rst +16 -0
  5. {m4opt-2.2.0 → m4opt-2.3.0}/PKG-INFO +1 -1
  6. {m4opt-2.2.0 → m4opt-2.3.0}/docs/dev/changes.rst +16 -0
  7. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/schedule.py +2 -0
  8. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_version.py +3 -3
  9. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/milp.py +19 -4
  10. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/tests/test_milp.py +11 -3
  11. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt.egg-info/PKG-INFO +1 -1
  12. {m4opt-2.2.0 → m4opt-2.3.0}/.github/dependabot.yml +0 -0
  13. {m4opt-2.2.0 → m4opt-2.3.0}/.github/workflows/publish.yml +0 -0
  14. {m4opt-2.2.0 → m4opt-2.3.0}/.gitignore +0 -0
  15. {m4opt-2.2.0 → m4opt-2.3.0}/.readthedocs.yml +0 -0
  16. {m4opt-2.2.0 → m4opt-2.3.0}/CITATION.cff +0 -0
  17. {m4opt-2.2.0 → m4opt-2.3.0}/MANIFEST.in +0 -0
  18. {m4opt-2.2.0 → m4opt-2.3.0}/README.rst +0 -0
  19. {m4opt-2.2.0 → m4opt-2.3.0}/docs/Makefile +0 -0
  20. {m4opt-2.2.0 → m4opt-2.3.0}/docs/_static/css/risk_table.css +0 -0
  21. {m4opt-2.2.0 → m4opt-2.3.0}/docs/_static/example.ecsv +0 -0
  22. {m4opt-2.2.0 → m4opt-2.3.0}/docs/_static/example.gif +0 -0
  23. {m4opt-2.2.0 → m4opt-2.3.0}/docs/conf.py +0 -0
  24. {m4opt-2.2.0 → m4opt-2.3.0}/docs/design/index.rst +0 -0
  25. {m4opt-2.2.0 → m4opt-2.3.0}/docs/design/milp.rst +0 -0
  26. {m4opt-2.2.0 → m4opt-2.3.0}/docs/dev/contributing.rst +0 -0
  27. {m4opt-2.2.0 → m4opt-2.3.0}/docs/dev/index.rst +0 -0
  28. {m4opt-2.2.0 → m4opt-2.3.0}/docs/dev/npr7150.rst +0 -0
  29. {m4opt-2.2.0 → m4opt-2.3.0}/docs/dev/related.rst +0 -0
  30. {m4opt-2.2.0 → m4opt-2.3.0}/docs/dev/testing.rst +0 -0
  31. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/cli.rst +0 -0
  32. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/constraints.rst +0 -0
  33. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/dynamics.rst +0 -0
  34. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/fov.rst +0 -0
  35. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/index.rst +0 -0
  36. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/milp.rst +0 -0
  37. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/missions.rst +0 -0
  38. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/observer.rst +0 -0
  39. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/skygrid.rst +0 -0
  40. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/synphot.rst +0 -0
  41. {m4opt-2.2.0 → m4opt-2.3.0}/docs/guide/utils.rst +0 -0
  42. {m4opt-2.2.0 → m4opt-2.3.0}/docs/index.rst +0 -0
  43. {m4opt-2.2.0 → m4opt-2.3.0}/docs/install/cplex.rst +0 -0
  44. {m4opt-2.2.0 → m4opt-2.3.0}/docs/install/deploy.rst +0 -0
  45. {m4opt-2.2.0 → m4opt-2.3.0}/docs/install/gurobi.rst +0 -0
  46. {m4opt-2.2.0 → m4opt-2.3.0}/docs/install/index.rst +0 -0
  47. {m4opt-2.2.0 → m4opt-2.3.0}/docs/make.bat +0 -0
  48. {m4opt-2.2.0 → m4opt-2.3.0}/docs/refs.bib +0 -0
  49. {m4opt-2.2.0 → m4opt-2.3.0}/docs/scenarios/dorado.rst +0 -0
  50. {m4opt-2.2.0 → m4opt-2.3.0}/docs/scenarios/index.rst +0 -0
  51. {m4opt-2.2.0 → m4opt-2.3.0}/docs/scenarios/sedmachine.rst +0 -0
  52. {m4opt-2.2.0 → m4opt-2.3.0}/docs/scenarios/ztf.rst +0 -0
  53. {m4opt-2.2.0 → m4opt-2.3.0}/licenses/DORADO_LICENSE.txt +0 -0
  54. {m4opt-2.2.0 → m4opt-2.3.0}/licenses/LICENSE.rst +0 -0
  55. {m4opt-2.2.0 → m4opt-2.3.0}/licenses/README.rst +0 -0
  56. {m4opt-2.2.0 → m4opt-2.3.0}/licenses/TEMPLATE_LICENCE.rst +0 -0
  57. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/__init__.py +0 -0
  58. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/__init__.py +0 -0
  59. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/animate.py +0 -0
  60. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/core.py +0 -0
  61. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/prime.py +0 -0
  62. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/tests/__init__.py +0 -0
  63. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/tests/conftest.py +0 -0
  64. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/tests/data/800.fits +0 -0
  65. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/tests/test_core.py +0 -0
  66. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/_cli/tests/test_end_to_end.py +0 -0
  67. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/conftest.py +0 -0
  68. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/__init__.py +0 -0
  69. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/_airmass.py +0 -0
  70. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/_atnight.py +0 -0
  71. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/_body_separation.py +0 -0
  72. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/_core.py +0 -0
  73. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/_earth_limb.py +0 -0
  74. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/_galactic.py +0 -0
  75. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/_logical.py +0 -0
  76. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/_positional.py +0 -0
  77. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/_radiation.py +0 -0
  78. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/tests/__init__.py +0 -0
  79. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/tests/test_body_separation.py +0 -0
  80. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/tests/test_earth_limb.py +0 -0
  81. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/tests/test_galactic.py +0 -0
  82. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/constraints/tests/test_positional.py +0 -0
  83. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/dynamics/__init__.py +0 -0
  84. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/dynamics/_roll.py +0 -0
  85. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/dynamics/_slew.py +0 -0
  86. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/dynamics/tests/__init__.py +0 -0
  87. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/dynamics/tests/test_roll.py +0 -0
  88. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/extern/__init__.py +0 -0
  89. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/fov/__init__.py +0 -0
  90. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/fov/_core.py +0 -0
  91. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/__init__.py +0 -0
  92. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_core.py +0 -0
  93. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_rubin/__init__.py +0 -0
  94. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_rubin/data/LICENSE.txt +0 -0
  95. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_rubin/data/README.rst +0 -0
  96. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_rubin/data/lsstCamSim.yaml +0 -0
  97. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_ultrasat/__init__.py +0 -0
  98. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_ultrasat/data/AllSS_grid_361.txt +0 -0
  99. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_ultrasat/data/LCS_nonoverlapping_grid.csv +0 -0
  100. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_ultrasat/data/README.rst +0 -0
  101. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_uvex.py +0 -0
  102. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_ztf/__init__.py +0 -0
  103. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_ztf/data/LICENSE.txt +0 -0
  104. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_ztf/data/README.rst +0 -0
  105. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/missions/_ztf/data/ZTF_Fields.txt +0 -0
  106. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/observer/__init__.py +0 -0
  107. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/observer/_core.py +0 -0
  108. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/observer/_earth_fixed.py +0 -0
  109. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/observer/_spice.py +0 -0
  110. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/observer/_tle.py +0 -0
  111. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/py.typed +0 -0
  112. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/skygrid/__init__.py +0 -0
  113. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/skygrid/_geodesic.py +0 -0
  114. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/skygrid/_healpix.py +0 -0
  115. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/skygrid/_sinusoidal.py +0 -0
  116. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/skygrid/_spiral.py +0 -0
  117. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/skygrid/tests/__init__.py +0 -0
  118. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/skygrid/tests/test_skygrid.py +0 -0
  119. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/__init__.py +0 -0
  120. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/_bandpass.py +0 -0
  121. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/_detector.py +0 -0
  122. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/_extrinsic.py +0 -0
  123. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/_math.py +0 -0
  124. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/__init__.py +0 -0
  125. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_core.py +0 -0
  126. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_galactic.py +0 -0
  127. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/__init__.py +0 -0
  128. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/data/10FebZen.txt +0 -0
  129. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/data/10JunZen.txt +0 -0
  130. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/data/10Nog.txt +0 -0
  131. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/data/10Nwh.txt +0 -0
  132. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/data/10Phx.txt +0 -0
  133. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/data/10Tuc.txt +0 -0
  134. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/data/10Zen.txt +0 -0
  135. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/data/README.rst +0 -0
  136. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_skybright/data/__init__.py +0 -0
  137. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_zodiacal/__init__.py +0 -0
  138. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_zodiacal/data/__init__.py +0 -0
  139. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_zodiacal/data/leinert_zodi.txt +0 -0
  140. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/background/_zodiacal/data/stis_zodi_high.ecsv +0 -0
  141. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/extinction/__init__.py +0 -0
  142. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/extinction/_dust.py +0 -0
  143. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/extinction/tests/__init__.py +0 -0
  144. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/extinction/tests/test_dust.py +0 -0
  145. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/tests/__init__.py +0 -0
  146. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/synphot/tests/test_detector.py +0 -0
  147. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/tests/__init__.py +0 -0
  148. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/tests/hypothesis.py +0 -0
  149. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/tests/plugins/__init__.py +0 -0
  150. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/tests/plugins/problem_size_limits.py +0 -0
  151. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/__init__.py +0 -0
  152. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/console.py +0 -0
  153. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/functional.py +0 -0
  154. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/numpy.py +0 -0
  155. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/optimization.py +0 -0
  156. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/pybtex/__init__.py +0 -0
  157. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/pybtex/styles/__init__.py +0 -0
  158. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/pybtex/styles/short_alpha.py +0 -0
  159. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/resource.py +0 -0
  160. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/sympy.py +0 -0
  161. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/tests/__init__.py +0 -0
  162. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/tests/conftest.py +0 -0
  163. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/tests/sphinx_roots/test-pybtex/conf.py +0 -0
  164. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/tests/sphinx_roots/test-pybtex/index.rst +0 -0
  165. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/tests/sphinx_roots/test-pybtex/refs.bib +0 -0
  166. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/tests/test_console.py +0 -0
  167. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/tests/test_optimization.py +0 -0
  168. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/tests/test_pybtex.py +0 -0
  169. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt/utils/typing_extensions.py +0 -0
  170. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt.egg-info/SOURCES.txt +0 -0
  171. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt.egg-info/dependency_links.txt +0 -0
  172. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt.egg-info/entry_points.txt +0 -0
  173. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt.egg-info/requires.txt +0 -0
  174. {m4opt-2.2.0 → m4opt-2.3.0}/m4opt.egg-info/top_level.txt +0 -0
  175. {m4opt-2.2.0 → m4opt-2.3.0}/pyproject.toml +0 -0
  176. {m4opt-2.2.0 → m4opt-2.3.0}/setup.cfg +0 -0
  177. {m4opt-2.2.0 → m4opt-2.3.0}/tox.ini +0 -0
@@ -29,7 +29,7 @@ jobs:
29
29
  steps:
30
30
  - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
31
31
  - name: Cache Astropy data
32
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
32
+ uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
33
33
  with:
34
34
  key: ${{ matrix.toxenv }}
35
35
  path: ~/.astropy/cache
@@ -75,7 +75,7 @@ jobs:
75
75
  with:
76
76
  fetch-depth: 0
77
77
  - name: Cache Astropy data
78
- uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb # v5.0.1
78
+ uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
79
79
  with:
80
80
  key: ${{ matrix.toxenv }}
81
81
  path: ~/.astropy/cache
@@ -28,7 +28,7 @@ repos:
28
28
  exclude_types: [python] # Covered by Ruff W291.
29
29
  - repo: https://github.com/astral-sh/ruff-pre-commit
30
30
  # Ruff version.
31
- rev: v0.14.11
31
+ rev: v0.14.13
32
32
  hooks:
33
33
  # Run the linter.
34
34
  - id: ruff-check
@@ -2,6 +2,22 @@
2
2
  Changes
3
3
  *******
4
4
 
5
+ 2.3.0 (2026-01-22)
6
+ ==================
7
+
8
+ - When available memory is limited by the ``--memory`` option, spool CPLEX's
9
+ node file to disk.
10
+
11
+ - If the solution is aborted because the best bound falls below the objective
12
+ lower cutoff, then record the solution status as
13
+ `aborted, lower cutoff reached`.
14
+
15
+ 2.2.1 (2026-01-16)
16
+ ==================
17
+
18
+ - Fix crash for sky maps with invalid pixels when using a fixed absolute
19
+ magnitude.
20
+
5
21
  2.2.0 (2026-01-15)
6
22
  ==================
7
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: m4opt
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Multi-Mission Multi-Messenger Observation Planning Toolkit
5
5
  Author-email: M4OPT Developers <leo.singer@ligo.org>
6
6
  License-Expression: BSD-3-Clause
@@ -2,6 +2,22 @@
2
2
  Changes
3
3
  *******
4
4
 
5
+ 2.3.0 (2026-01-22)
6
+ ==================
7
+
8
+ - When available memory is limited by the ``--memory`` option, spool CPLEX's
9
+ node file to disk.
10
+
11
+ - If the solution is aborted because the best bound falls below the objective
12
+ lower cutoff, then record the solution status as
13
+ `aborted, lower cutoff reached`.
14
+
15
+ 2.2.1 (2026-01-16)
16
+ ==================
17
+
18
+ - Fix crash for sky maps with invalid pixels when using a fixed absolute
19
+ magnitude.
20
+
5
21
  2.2.0 (2026-01-15)
6
22
  ==================
7
23
 
@@ -355,6 +355,8 @@ def schedule(
355
355
  appmag_quantiles = stats.norm(
356
356
  loc=appmag_mu[:, np.newaxis], scale=appmag_sigma[:, np.newaxis]
357
357
  ).ppf(quantiles)
358
+ # FIXME: prune pixels with infinite distance
359
+ appmag_quantiles[np.isposinf(appmag_mu)] = np.inf
358
360
 
359
361
  with observing(
360
362
  observer_location=observer_locations[0],
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '2.2.0'
32
- __version_tuple__ = version_tuple = (2, 2, 0)
31
+ __version__ = version = '2.3.0'
32
+ __version_tuple__ = version_tuple = (2, 3, 0)
33
33
 
34
- __commit_id__ = commit_id = 'g76c65b409'
34
+ __commit_id__ = commit_id = 'gbb83227d0'
@@ -6,7 +6,7 @@ from gzip import GzipFile
6
6
  from io import BufferedWriter
7
7
  from pathlib import Path
8
8
  from shutil import copyfileobj
9
- from tempfile import NamedTemporaryFile
9
+ from tempfile import NamedTemporaryFile, gettempdir
10
10
  from unittest.mock import patch
11
11
 
12
12
  import cplex
@@ -25,6 +25,7 @@ __all__ = ("Model", "SolveSolution")
25
25
  class LowerCutoffCallback:
26
26
  def __init__(self, cutoff: float):
27
27
  self._cutoff = cutoff
28
+ self._reached = False
28
29
 
29
30
  def invoke(self, context: cplex.callbacks.Context):
30
31
  best_bound = context.get_double_info(cplex.callbacks.CallbackInfo.best_bound)
@@ -32,6 +33,7 @@ class LowerCutoffCallback:
32
33
  print(
33
34
  f"giving up because best bound ({best_bound}) <= cutoff ({self._cutoff})"
34
35
  )
36
+ self._reached = True
35
37
  context.abort()
36
38
 
37
39
 
@@ -89,6 +91,7 @@ class Model(_Model):
89
91
 
90
92
  self.context.solver.log_output = verbose
91
93
  self.context.cplex_parameters.threads = jobs
94
+ self.context.cplex_parameters.workdir = gettempdir()
92
95
 
93
96
  # Disable deterministic parallelism. We're OK with results being
94
97
  # slightly dependent upon timing.
@@ -110,7 +113,8 @@ class Model(_Model):
110
113
  )
111
114
 
112
115
  if np.isfinite(memory):
113
- self.context.cplex_parameters.mip.limits.treememory = memory.to_value(u.MiB)
116
+ self.context.cplex_parameters.mip.strategy.file = 3
117
+ self.context.cplex_parameters.workmem = memory.to_value(u.MiB)
114
118
 
115
119
  if lowercutoff is not None:
116
120
  # FIXME: Setting lowercutoff drastically hurts solution quality.
@@ -118,8 +122,9 @@ class Model(_Model):
118
122
  # See https://github.com/IBMDecisionOptimization/docplex/issues/20
119
123
  #
120
124
  # model.context.cplex_parameters.mip.tolerances.lowercutoff = cutoff
125
+ self._lower_cutoff = LowerCutoffCallback(lowercutoff)
121
126
  self.get_cplex().set_callback(
122
- LowerCutoffCallback(lowercutoff),
127
+ self._lower_cutoff,
123
128
  cplex.callbacks.Context.id.global_progress,
124
129
  )
125
130
 
@@ -186,7 +191,17 @@ class Model(_Model):
186
191
 
187
192
  def solve(self, **kwargs):
188
193
  with patch("docplex.mp.solution.SolveSolution", SolveSolution):
189
- return super().solve(**kwargs)
194
+ result = super().solve(**kwargs)
195
+ if (
196
+ (cutoff := getattr(self, "_lower_cutoff", None))
197
+ and cutoff._reached
198
+ and self.solve_details.status == "aborted"
199
+ ):
200
+ message = "aborted, lower cutoff reached"
201
+ self._solve_details._solve_status = message
202
+ if result is not None:
203
+ result.solve_details._solve_status = message
204
+ return result
190
205
 
191
206
  def min(self, *args):
192
207
  return np.asarray(super().min(*args)).view(VariableArray)
@@ -1,5 +1,7 @@
1
1
  """Test production problem size limits for MILP solvers."""
2
2
 
3
+ from tempfile import gettempdir
4
+
3
5
  import docplex.mp.model
4
6
  import numpy as np
5
7
  import pytest
@@ -24,26 +26,32 @@ def test_cplex_parameters():
24
26
  """Test configuration of CPLEX solver parameters."""
25
27
  m = Model()
26
28
  assert m.context.cplex_parameters.mip.pool.capacity.value == 0
29
+ assert m.context.cplex_parameters.mip.strategy.file.value == 1
27
30
  assert m.context.cplex_parameters.parallel.value == -1
28
31
  assert m.context.cplex_parameters.threads.value == 0
29
32
  assert m.context.cplex_parameters.timelimit.value == 1e75
30
- assert m.context.cplex_parameters.mip.limits.treememory.value == 1e75
33
+ assert m.context.cplex_parameters.workmem.value == 2048
34
+ assert m.context.cplex_parameters.workdir.value == gettempdir()
31
35
  assert m.context.solver.log_output
32
36
 
33
37
  m = Model(timelimit=1 * u.minute, jobs=3)
34
38
  assert m.context.cplex_parameters.mip.pool.capacity.value == 0
39
+ assert m.context.cplex_parameters.mip.strategy.file.value == 1
35
40
  assert m.context.cplex_parameters.parallel.value == -1
36
41
  assert m.context.cplex_parameters.threads.value == 3
37
42
  assert m.context.cplex_parameters.timelimit.value == 60
38
- assert m.context.cplex_parameters.mip.limits.treememory.value == 1e75
43
+ assert m.context.cplex_parameters.workmem.value == 2048
44
+ assert m.context.cplex_parameters.workdir.value == gettempdir()
39
45
  assert m.context.solver.log_output
40
46
 
41
47
  m = Model(timelimit=1 * u.minute, jobs=3, memory=5 * u.GiB)
42
48
  assert m.context.cplex_parameters.mip.pool.capacity.value == 0
49
+ assert m.context.cplex_parameters.mip.strategy.file.value == 3
43
50
  assert m.context.cplex_parameters.parallel.value == -1
44
51
  assert m.context.cplex_parameters.threads.value == 3
45
52
  assert m.context.cplex_parameters.timelimit.value == 60
46
- assert m.context.cplex_parameters.mip.limits.treememory.value == 5120
53
+ assert m.context.cplex_parameters.workmem.value == 5120
54
+ assert m.context.cplex_parameters.workdir.value == gettempdir()
47
55
  assert m.context.solver.log_output
48
56
 
49
57
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: m4opt
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Multi-Mission Multi-Messenger Observation Planning Toolkit
5
5
  Author-email: M4OPT Developers <leo.singer@ligo.org>
6
6
  License-Expression: BSD-3-Clause
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes