Skip to content

fix: encode GAMS EPS as np.finfo(float).tiny (closes #39) - #115

Merged
elainethale merged 1 commit into
mainfrom
eh/eps-to-tiny
May 27, 2026
Merged

fix: encode GAMS EPS as np.finfo(float).tiny (closes #39)#115
elainethale merged 1 commit into
mainfrom
eh/eps-to-tiny

Conversation

@elainethale

Copy link
Copy Markdown
Member

Summary

  • BREAKING: gdxpds.special.NUMPY_SPECIAL_VALUES[-1] is now np.finfo(float).tiny (~2.22e-308), not np.finfo(float).eps (machine epsilon, ~2.22e-16). EPS detection is exact equality on the sentinel, so a legitimate small float (e.g. 1e-200, machine epsilon) now round-trips as itself instead of silently mapping to GAMS EPS on write. Closes GAMS eps isn't the same as Numpy EPS #39.
  • Migration: if you wrote numpy.finfo(float).eps to mean GAMS EPS, switch to numpy.finfo(float).tiny. gdxpds.special.NUMPY_SPECIAL_VALUES[-1] is always the canonical sentinel.
  • Bumps to v4.0.0. Implements Item D of dev/modernization-wrapup.md, the final release in the modernization arc.

What changed

  • src/gdxpds/special.py: NUMPY_SPECIAL_VALUES[-1] = np.finfo(float).tiny; is_np_eps and convert_np_to_gdx_svs use exact equality on the sentinel (no more |val - eps| < eps band).
  • src/gdxpds/_gdxcc_engine.py _coerce_value_col: write-side eps_mask = arr == eps (exact equality); docstring updated.
  • src/gdxpds/_transfer_engine.py _substitute_value_col: same exact-equality switch; docstrings now make explicit that gt.SpecialValues.EPS is the negative-zero bit pattern (distinguishable from +0.0 only via the sign bit, not ==).
  • Tests: tests/test_specials.py, tests/test_engine_parity.py, tests/test_write.py all swap epstiny. New regressions: test_small_float_is_not_eps and test_write_parity_small_floats_not_eps (1e-200 survives the full 2x2 write x read engine matrix).
  • Docs: doc/source/overview.md special-values table, "drop EPS" snippet, intro paragraph, and a new "v4.0.0 breaking changes" subsection in the migration section.
  • CHANGES.txt: v4.0.0 entry.
  • src/gdxpds/__init__.py: __version__ = "4.0.0".

Test plan

  • Lint (ruff check + ruff format --check) clean.
  • pytest tests passes on .venv-gams-34 (pandas 3.0).
  • pytest tests passes on .venv-gams-49 (pandas 2.2, gamsapi 49.6.0).
  • pytest tests passes on .venv-gams-51 (pandas 2.2, gamsapi 51.3.0).
  • .venv-no-gams wheel build + gdxpds info succeed as expected.
  • 1e-200 write/read round-trip preserved on both engines (covered by the new regressions).

🤖 Generated with Claude Code

BREAKING CHANGE: gdxpds.special.NUMPY_SPECIAL_VALUES[-1] is now
np.finfo(float).tiny (~2.22e-308), not np.finfo(float).eps (~2.22e-16).
EPS detection is exact equality on the sentinel, so a legitimate small
float (e.g. 1e-200, machine epsilon) now round-trips as itself instead
of silently mapping to GAMS EPS on write. If your code wrote
numpy.finfo(float).eps to mean GAMS EPS, switch to
numpy.finfo(float).tiny.

Bumps to v4.0.0. Updates the gdxcc and gams.transfer engines, special
value helpers, EPS-related tests, overview.md migration notes, and
CHANGES.txt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes issue #39 by changing the numpy encoding of GAMS EPS from np.finfo(float).eps (~2.22e-16) to np.finfo(float).tiny (~2.22e-308), and switching EPS detection from a tolerance band (|val - eps| < eps) to exact equality on the sentinel. This is a breaking change bumping the package to v4.0.0; legitimate small floats (e.g. 1e-200, machine epsilon) now round-trip as themselves instead of being silently coerced to GAMS EPS on write. Changes are made consistently across the canonical mapping module, both I/O engines, tests (including new cross-engine parity regressions), docs, and changelog.

Changes:

  • Replace np.finfo(float).eps with np.finfo(float).tiny as the canonical EPS sentinel in special.py, and switch all EPS detection (is_np_eps, convert_np_to_gdx_svs, gdxcc _coerce_value_col, transfer _substitute_value_col) to exact equality.
  • Add regression tests (test_small_float_is_not_eps, test_write_parity_small_floats_not_eps) ensuring 1e-200 and machine epsilon survive the full write/read engine matrix.
  • Update version to 4.0.0, plus CHANGES.txt and doc/source/overview.md (special-values table, EPS-drop snippet, new v4.0.0 migration subsection).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/gdxpds/special.py Canonical sentinel switched to tiny; is_np_eps and convert_np_to_gdx_svs use exact equality; docstrings updated.
src/gdxpds/_gdxcc_engine.py Write-side EPS detection switched to exact equality; read-side docstring updated to tiny.
src/gdxpds/_transfer_engine.py Same exact-equality switch for _substitute_value_col; docstrings clarify gt EPS is -0.0 bit pattern.
src/gdxpds/init.py Version bump to 4.0.0.
tests/test_specials.py Swap epstiny; add test_small_float_is_not_eps regression.
tests/test_engine_parity.py Swap epstiny; add cross-engine small-float regression.
tests/test_write.py Swap epstiny in test_write_known_value_columns.
doc/source/overview.md Update special-values table, snippet, intro; add v4.0.0 migration subsection.
CHANGES.txt New v4.0.0 entry describing the breaking change.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@elainethale
elainethale merged commit aad60d3 into main May 27, 2026
4 checks passed
@elainethale
elainethale deleted the eh/eps-to-tiny branch May 27, 2026 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GAMS eps isn't the same as Numpy EPS

2 participants