-
Notifications
You must be signed in to change notification settings - Fork 5
feat: new error class OutOfBoundsError
#438
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
lars-reimann
merged 87 commits into
main
from
262-custom-error-if-number-is-out-of-bounds
Jul 11, 2023
Merged
Changes from all commits
Commits
Show all changes
87 commits
Select commit
Hold shift + click to select a range
ca46067
Implement OutOfBoundsError
zzril 9a1ecf4
Fix formatting
zzril cbc8019
Fix typos
zzril 56aa484
Add test for OutOfBoundsError
zzril 63c8640
moved test_out_of_bounds_error.py
Marsmaennchen221 69ec60b
test: Added tests for `OutOfBoundsError`
Marsmaennchen221 3a40c6a
Fix type hints
zzril 0805bb8
feat: Added `NotImplementedError` if lower bound is greater than uppe…
Marsmaennchen221 293ac8c
Merge branch '262-custom-error-if-number-is-out-of-bounds' of https:/…
Marsmaennchen221 bb3a235
Merge branch 'main' into 262-custom-error-if-number-is-out-of-bounds
zzril 19d8569
Add None return type in __init__ methods
zzril 4575b58
style: apply automated linter fixes
megalinter-bot d23718b
style: apply automated linter fixes
megalinter-bot d529a4a
feat: Added check for compare to lower and upper bounds
Marsmaennchen221 b724bbd
Merge branch '262-custom-error-if-number-is-out-of-bounds' of https:/…
Marsmaennchen221 f089975
style: apply automated linter fixes
megalinter-bot 83f7952
style: apply automated linter fixes
megalinter-bot a560c59
Merge branch 'main' into 262-custom-error-if-number-is-out-of-bounds
zzril 6dc82b1
Add docstrings to public methods in _generic.py
zzril 97fdca4
Remove unused _is_float method from Bound classes
zzril 1d49617
Fix typo in OutOfBoundsError test
zzril 9f3ef11
Make str functions public
zzril 0f0132d
Rename compare functions.
zzril 97096c4
Fix bounds checking logic
zzril 1a09b5f
Add missing testcase
zzril 2ce2380
Improve exception messages and fix testcases
zzril 7a96093
Let the linter do the work
zzril c4d4fdc
style: apply automated linter fixes
megalinter-bot 72e3eae
style: apply automated linter fixes
megalinter-bot 6b73948
Add docstring to __init__ method
zzril e7e1f9f
Remove parameters section in class docstring
zzril 90079e0
Use OutOfBoundsError in image tests
zzril eab7354
style: apply automated linter fixes
megalinter-bot 0c5a23d
Use ValueError instead of NotImplementedError
zzril c41eb07
style: apply automated linter fixes
megalinter-bot cb8de4b
Make Infinity classed private
zzril 7a0792e
Merge branch '262-custom-error-if-number-is-out-of-bounds' of github.…
zzril 076558a
Create property for value to silence warnings
zzril bd54cbc
Add type hint for value property
zzril 1928ce2
Add additional type hints inside __init__ function
zzril 1a79d70
Fix unintended class call instead of constructor
zzril 6a2c516
Use local variable with different name
zzril 7407ee2
Remove private classes from __init__.py
zzril 4ab61c2
Improve warning messages for incorrect bounds
zzril 14474e0
style: apply automated linter fixes
megalinter-bot 42ef8b5
Document ValueError in docstring
zzril 58dd3d6
Merge branch 'main' into 262-custom-error-if-number-is-out-of-bounds
zzril ed983b8
Remove Infinity classes
zzril 9798190
Make string and check methods private
zzril 5ef248a
style: apply automated linter fixes
megalinter-bot d616bcc
Check for nan in Bound constructor
zzril 6f69b16
Apply suggestions from code review
zzril 915d652
style: apply automated linter fixes
megalinter-bot f2b82e9
Use OutOfBoundsError in Discretizer
zzril 4cf2547
style: apply automated linter fixes
megalinter-bot 79be629
Update Images to use name parameter
zzril 8649b98
Merge branch '262-custom-error-if-number-is-out-of-bounds' of github.…
zzril 5c37bb5
Add tests for name parameter
zzril aff945b
style: apply automated linter fixes
megalinter-bot 3885a43
Update class docstring to include name parameter
zzril fada030
Add nan and inf checks
zzril ff44d26
style: apply automated linter fixes
megalinter-bot abf1d55
style: apply automated linter fixes
megalinter-bot 6173601
Remove unnecessary checks
zzril 4a069ce
Fix equality comparisons
zzril 1361f49
style: apply automated linter fixes
megalinter-bot 61f78c2
Simplify infinity checks using isinf
zzril 6c62093
Use OutOfBoundsError in AdaBoost classifier
zzril a23062f
style: apply automated linter fixes
megalinter-bot 0a53227
style: apply automated linter fixes
megalinter-bot c2e0f10
Use new error in gradient boosting classifier
zzril eac8329
Merge branch 'main' into 262-custom-error-if-number-is-out-of-bounds
zzril aa3df4a
style: apply automated linter fixes
megalinter-bot babda19
style: apply automated linter fixes
megalinter-bot 74ea122
Use new error for nearest_neighbors classifier
zzril 053a289
Merge branch '262-custom-error-if-number-is-out-of-bounds' of github.…
zzril 6a42065
style: apply automated linter fixes
megalinter-bot acd0d85
Use OutOfBoundsError for RandomForest classifier
zzril de897c9
style: apply automated linter fixes
megalinter-bot 3b3eba1
Use OutOfBoundsError for SVM and inner classes
zzril d901936
style: apply automated linter fixes
megalinter-bot 2dc3797
Use OutOfBoundsError in regression models
zzril 347f2c5
style: apply automated linter fixes
megalinter-bot 0616612
style: apply automated linter fixes
megalinter-bot 6b5b15c
Use ClosedBounds for integers
zzril 5bf1dd6
style: apply automated linter fixes
megalinter-bot dd6f4d1
Merge branch 'main' into 262-custom-error-if-number-is-out-of-bounds
lars-reimann File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,281 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from abc import ABC, abstractmethod | ||
|
|
||
| from numpy import isinf, isnan | ||
|
|
||
|
|
||
| class OutOfBoundsError(ValueError): | ||
| """ | ||
| A generic exception that can be used to signal that a (float) value is outside its expected range. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| actual: float | ||
| The actual value that is outside its expected range. | ||
| name: str | None | ||
| The name of the offending variable. | ||
| lower_bound: Bound | None | ||
| The lower bound of the expected range. Use None if there is no lower Bound. | ||
| upper_bound: Bound | None | ||
| The upper bound of the expected range. Use None if there is no upper Bound. | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| actual: float, | ||
| *, | ||
| name: str | None = None, | ||
| lower_bound: Bound | None = None, | ||
| upper_bound: Bound | None = None, | ||
| ): | ||
| """ | ||
| Initialize an OutOfBoundsError. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| actual: float | ||
| The actual value that is outside its expected range. | ||
| name: str | None | ||
| The name of the offending variable. | ||
| lower_bound: Bound | None | ||
| The lower bound of the expected range. Use None if there is no lower Bound. | ||
| upper_bound: Bound | None | ||
| The upper bound of the expected range. Use None if there is no upper Bound. | ||
|
|
||
| Raises | ||
| ------ | ||
| ValueError | ||
| * If one of the given Bounds is +/-inf. (For infinite Bounds, pass None instead.) | ||
| * If one of the given Bounds is nan. | ||
| * If upper_bound < lower_bound. | ||
| * If actual does not lie outside the given interval. | ||
| * If actual is not a real number. | ||
| """ | ||
| # Validate bound parameters: | ||
| if lower_bound is None and upper_bound is None: | ||
| raise ValueError("Illegal interval: Attempting to raise OutOfBoundsError, but no bounds given.") | ||
| if (lower_bound is not None and isinf(lower_bound.value)) or ( | ||
| upper_bound is not None and isinf(upper_bound.value) | ||
| ): | ||
| raise ValueError("Illegal interval: Lower and upper bounds must be real numbers, or None if unbounded.") | ||
| # Validate actual parameter: | ||
| if isinf(actual) or isnan(actual): | ||
| raise ValueError("Attempting to raise OutOfBoundsError with actual value not being a real number.") | ||
| # Use local variables with stricter types to help static analysis: | ||
| _lower_bound: Bound = lower_bound if lower_bound is not None else OpenBound(float("-inf")) | ||
| _upper_bound: Bound = upper_bound if upper_bound is not None else OpenBound(float("inf")) | ||
| # Check bounds: | ||
| if _upper_bound.value < _lower_bound.value: | ||
| raise ValueError( | ||
| ( | ||
| f"Illegal interval: Attempting to raise OutOfBoundsError, but given upper bound {_upper_bound} is " | ||
| f"actually less than given lower bound {_lower_bound}." | ||
| ), | ||
| ) | ||
| # Check that actual is indeed outside the interval: | ||
| elif _lower_bound._check_lower_bound(actual) and _upper_bound._check_upper_bound(actual): | ||
| raise ValueError( | ||
| ( | ||
| f"Illegal interval: Attempting to raise OutOfBoundsError, but value {actual} is not actually" | ||
| f" outside given interval {_lower_bound._str_lower_bound()}, {_upper_bound._str_upper_bound()}." | ||
| ), | ||
| ) | ||
| # Raise the actual exception: | ||
| full_variable_name = actual if name is None else f"{name} (={actual})" | ||
| super().__init__( | ||
| f"{full_variable_name} is not inside {_lower_bound._str_lower_bound()}, {_upper_bound._str_upper_bound()}.", | ||
| ) | ||
|
|
||
|
|
||
| class Bound(ABC): | ||
| """ | ||
| Abstract base class for (lower or upper) Bounds on a float value. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| value: float | ||
| The value of the Bound. | ||
| """ | ||
|
|
||
| def __init__(self, value: float): | ||
| """ | ||
| Initialize a Bound. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| value: float | ||
| The value of the Bound. | ||
|
|
||
| Raises | ||
| ------ | ||
| ValueError | ||
| If value is nan or if value is +/-inf and the Bound type does not allow for infinite Bounds. | ||
| """ | ||
| if isnan(value): | ||
| raise ValueError("Bound must be a real number, not nan.") | ||
| self._value = value | ||
|
|
||
| def __str__(self) -> str: | ||
| """Get a string representation of the concrete value of the Bound.""" | ||
| return str(self.value) | ||
|
|
||
| @property | ||
| def value(self) -> float: | ||
| """Get the concrete value of the Bound.""" | ||
| return self._value | ||
|
|
||
| @abstractmethod | ||
| def _str_lower_bound(self) -> str: | ||
| """Get a string representation of the Bound as the lower Bound of an interval.""" | ||
|
|
||
| @abstractmethod | ||
| def _str_upper_bound(self) -> str: | ||
| """Get a string representation of the Bound as the upper Bound of an interval.""" | ||
|
|
||
| @abstractmethod | ||
| def _check_lower_bound(self, actual: float) -> bool: | ||
| """ | ||
| Check that a value does not exceed the Bound on the lower side. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| actual: float | ||
| The actual value that should be checked for not exceeding the Bound. | ||
| """ | ||
|
|
||
| @abstractmethod | ||
| def _check_upper_bound(self, actual: float) -> bool: | ||
| """ | ||
| Check that a value does not exceed the Bound on the upper side. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| actual: float | ||
| The actual value that should be checked for not exceeding the Bound. | ||
| """ | ||
|
|
||
|
|
||
| class ClosedBound(Bound): | ||
| """ | ||
| A closed Bound, i.e. the value on the border belongs to the range. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| value: float | ||
| The value of the Bound. | ||
| """ | ||
|
|
||
| def __init__(self, value: float): | ||
| """ | ||
| Initialize a ClosedBound. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| value: float | ||
| The value of the ClosedBound. | ||
|
|
||
| Raises | ||
| ------ | ||
| ValueError | ||
| If value is nan or if value is +/-inf. | ||
| """ | ||
| if value == float("-inf") or value == float("inf"): | ||
| raise ValueError("ClosedBound must be a real number, not +/-inf.") | ||
| super().__init__(value) | ||
|
|
||
| def _str_lower_bound(self) -> str: | ||
| """Get a string representation of the ClosedBound as the lower Bound of an interval.""" | ||
| return f"[{self}" | ||
|
|
||
| def _str_upper_bound(self) -> str: | ||
| """Get a string representation of the ClosedBound as the upper Bound of an interval.""" | ||
| return f"{self}]" | ||
|
|
||
| def _check_lower_bound(self, actual: float) -> bool: | ||
| """ | ||
| Check that a value is not strictly lower than the ClosedBound. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| actual: float | ||
| The actual value that should be checked for not exceeding the Bound. | ||
| """ | ||
| return actual >= self.value | ||
|
|
||
| def _check_upper_bound(self, actual: float) -> bool: | ||
| """ | ||
| Check that a value is not strictly higher than the ClosedBound. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| actual: float | ||
| The actual value that should be checked for not exceeding the Bound. | ||
| """ | ||
| return actual <= self.value | ||
|
|
||
|
|
||
| class OpenBound(Bound): | ||
| """ | ||
| An open Bound, i.e. the value on the border does not belong to the range. | ||
|
lars-reimann marked this conversation as resolved.
|
||
|
|
||
| Parameters | ||
| ---------- | ||
| value: float | ||
| The value of the OpenBound. | ||
| """ | ||
|
|
||
| def __init__(self, value: float): | ||
| """ | ||
| Initialize an OpenBound. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| value: float | ||
| The value of the OpenBound. | ||
|
|
||
| Raises | ||
| ------ | ||
| ValueError | ||
| If value is nan. | ||
| """ | ||
| super().__init__(value) | ||
|
|
||
| def __str__(self) -> str: | ||
| """Get a string representation of the concrete value of the OpenBound.""" | ||
| if self.value == float("-inf"): | ||
| return "-\u221e" | ||
| elif self.value == float("inf"): | ||
| return "\u221e" | ||
| else: | ||
| return super().__str__() | ||
|
|
||
| def _str_lower_bound(self) -> str: | ||
| """Get a string representation of the OpenBound as the lower Bound of an interval.""" | ||
| return f"({self}" | ||
|
|
||
| def _str_upper_bound(self) -> str: | ||
| """Get a string representation of the OpenBound as the upper Bound of an interval.""" | ||
| return f"{self})" | ||
|
|
||
| def _check_lower_bound(self, actual: float) -> bool: | ||
| """ | ||
| Check that a value is not lower or equal to the OpenBound. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| actual: float | ||
| The actual value that should be checked for not exceeding the Bound. | ||
| """ | ||
| return actual > self.value | ||
|
|
||
| def _check_upper_bound(self, actual: float) -> bool: | ||
| """ | ||
| Check that a value is not higher or equal to the OpenBound. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| actual: float | ||
| The actual value that should be checked for not exceeding the Bound. | ||
| """ | ||
| return actual < self.value | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.