Skip to content

Commit a3210de

Browse files
committed
add warning to prevent users from accidentally adding incomplete box constraints
1 parent 7d7ccea commit a3210de

File tree

3 files changed

+39
-1
lines changed

3 files changed

+39
-1
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Changelog
22

33
## v1.5.3
4+
- [#168](https://jugit.fz-juelich.de/IBG0/ModSim/hopsy/-/issues/168): Add warning to help users avoid unintended constraint violations
45
- [#167](https://jugit.fz-juelich.de/IBG0/ModSim/hopsy/-/issues/167): Fix example code for GEM sampling
5-
- [#166](https://jugit.fz-juelich.de/IBG0/ModSim/hopsy/-/issues/165): Add `simplify` kwarg to `hopsy.round`
6+
- [#166](https://jugit.fz-juelich.de/IBG0/ModSim/hopsy/-/issues/166): Add `simplify` kwarg to `hopsy.round`
67
- [#165](https://jugit.fz-juelich.de/IBG0/ModSim/hopsy/-/issues/165): Treat thinning=0 as no thinning instead of producing NaNs
78
- [#162](https://jugit.fz-juelich.de/IBG0/ModSim/hopsy/-/issues/162): Fix sign error in python implementation of parallel tempering
89
- [#161](https://jugit.fz-juelich.de/IBG-1/ModSim/hopsy/-/issues/161): Persist markov chain proposal after parallel sampling

src/hopsy/misc.py

+15
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,10 @@ def add_box_constraints(
503503
:math:`lb \leq x_i \leq ub`. If `lower_bound`` and ``upper_bound`` are both ``numpy.ndarray`` with
504504
appropriate length, then every dimension :math:`i` will be bound as :math:`lb_i \leq x_i \leq ub_i`.
505505
506+
Note: if equality constraints have already been added, the box constraints only apply to the reduced, full space.
507+
To apply box constraints to the full space, apply box constraints before equality constraints.
508+
This function will print a warning if it detects that equality constraints have already been added.
509+
506510
:param hopsy.Problem problem: Problem which should be constrained and which contains the matrix :math:`A` and vector :math:`b` in :math:`Ax \leq b`.
507511
508512
:param lower_bound: Specifies the lower bound(s).
@@ -514,6 +518,17 @@ def add_box_constraints(
514518
:return: A :class:`hopsy.Problem` bounded in all dimensions.
515519
:rtype: hopsy.Problem
516520
"""
521+
has_equality_constraints = (
522+
problem.transformation is not None or problem.shift is not None
523+
)
524+
if has_equality_constraints:
525+
_s.warnings.warn(
526+
"Note that box constraints applied after equality constraints/rounding are only applied to the "
527+
"reduced space. Apply box constraints first, if you want them to be applied to the full "
528+
"space.",
529+
UserWarning,
530+
)
531+
517532
if problem.A.shape[1] == 0:
518533
raise ValueError("Cannot determine dimension for empty inequality Ax <= b.")
519534

tests/misc.py

+22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import typing
22
import unittest
3+
import warnings
34

45
import numpy
56
import numpy as np
@@ -606,6 +607,27 @@ def test_starting_point_with_equality_constraints_when_rounded(self):
606607
original_starting_point = back_transform(problem, [problem.starting_point])[0]
607608
self.assertTrue(np.all(original_starting_point == starting_point))
608609

610+
def test_warning_when_adding_box_constraints_after_equality_constraints(self):
611+
with warnings.catch_warnings(record=True) as w:
612+
# Cause all warnings to always be triggered.
613+
warnings.simplefilter("always")
614+
A = np.vstack([np.identity(3), -np.identity(3)])
615+
b = np.array([100, 100, 100, 100, 100, 100])
616+
617+
problem = Problem(A, b)
618+
619+
A_eq = np.array([[2.0, 1.0, 0]])
620+
b_eq = np.array([8])
621+
622+
problem = add_equality_constraints(problem, A_eq, b_eq)
623+
624+
lb = [-5, -5]
625+
ub = [5, 5]
626+
627+
problem = add_box_constraints(problem, lb, ub, simplify=False)
628+
self.assertEqual(len(w), 1)
629+
self.assertTrue(issubclass(w[-1].category, UserWarning))
630+
609631
def test_starting_point_with_equality_constraints_simplify(self):
610632
A = np.array([]).reshape((0, 3))
611633
b = np.array([])

0 commit comments

Comments
 (0)