Skip to content
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

Major/core rewrite #2

Open
wants to merge 84 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
71eec3b
WIP on bugfix/avoid_test_case_overwrites
jagerber48 Jul 14, 2024
6515420
some updates
jagerber48 Jul 15, 2024
8299f58
cleanup
jagerber48 Jul 15, 2024
f24ea0f
tests
jagerber48 Jul 15, 2024
6946067
some nan handling
jagerber48 Jul 15, 2024
7d3ec32
comment
jagerber48 Jul 15, 2024
c3dc427
revert test_uncertainties changes
jagerber48 Jul 15, 2024
d2388c2
operation type hints and cleanup
jagerber48 Jul 16, 2024
08b22dd
docstring
jagerber48 Jul 16, 2024
b1b534a
documentation
jagerber48 Jul 16, 2024
deaf4f9
Cache standard deviation calculation
jagerber48 Jul 16, 2024
2ceb966
Cleanup and documentation
jagerber48 Jul 16, 2024
7a53bbc
comments
jagerber48 Jul 16, 2024
1c121a7
raise on negative uncertainty
jagerber48 Jul 17, 2024
a602f84
new version of umath
jagerber48 Jul 17, 2024
d7ee99b
loop through args and kwargs instead of using inspect.signature
jagerber48 Jul 17, 2024
b841984
analytical and partial derivatives test
jagerber48 Jul 18, 2024
b277ab4
position only arguments
jagerber48 Jul 18, 2024
51c0f61
whitespace
jagerber48 Jul 18, 2024
71ec975
add value and uncertainty properties
jagerber48 Jul 18, 2024
18ae733
add asinh and hypot, function to add ufuncs
jagerber48 Jul 18, 2024
7636d9a
add UArray
jagerber48 Jul 18, 2024
22e6a07
return NotImplemented when we don't get a UFloat in a something conve…
jagerber48 Jul 18, 2024
3147eb0
a type hint
jagerber48 Jul 18, 2024
f536a4a
a test
jagerber48 Jul 18, 2024
6074bdf
hack to fix mean
jagerber48 Jul 18, 2024
2b5aa7d
fixed mean
jagerber48 Jul 18, 2024
37f8e7f
remove old comment
jagerber48 Jul 18, 2024
48cc179
refactor into new subpackage
jagerber48 Jul 19, 2024
7a83977
update test
jagerber48 Jul 19, 2024
25cc58a
import/cleanup
jagerber48 Jul 19, 2024
a6ef02f
inject function
jagerber48 Jul 19, 2024
21f2b92
refactor to add func_conversion
jagerber48 Jul 19, 2024
a09fad2
dataclass for uncertainty linear combination, hashes and immutability
jagerber48 Jul 19, 2024
084968c
ucombo file and some typing
jagerber48 Jul 19, 2024
6aa8e73
change import structure
jagerber48 Jul 19, 2024
9e2ce85
move docstring
jagerber48 Jul 19, 2024
d46526a
__slots__
jagerber48 Jul 19, 2024
316af60
re organize new/umath.py
jagerber48 Jul 21, 2024
de0a05d
Pull all numeric dummy method type stubs into numeric_base file
jagerber48 Jul 21, 2024
2b547a2
comment
jagerber48 Jul 21, 2024
1786ff2
cast weights to float
jagerber48 Jul 21, 2024
45fc4b5
undo reorder umath.py
jagerber48 Jul 21, 2024
32a2411
bind typevar to NumericBase
jagerber48 Jul 21, 2024
44709aa
Self typevar in UFloat
jagerber48 Jul 21, 2024
178e07d
hash type annotation
jagerber48 Jul 21, 2024
8cd237f
add formatting
jagerber48 Jul 21, 2024
8aff054
some type annotation
jagerber48 Jul 21, 2024
e467ebd
repr returns str for now. More readable in arrays.
jagerber48 Jul 21, 2024
d64126b
add correlated_values and covariance_matrix functions
jagerber48 Jul 21, 2024
2dd15f1
refactor ToUFunc to not require the ufloat_params input
jagerber48 Jul 21, 2024
91da3cb
some reorganization and comments
jagerber48 Jul 22, 2024
1f8fe7d
get rid of ufloat for now
jagerber48 Jul 22, 2024
400545c
no repr monkey patch
jagerber48 Jul 22, 2024
3ac5f02
refactor UAtom and UCombo, UCombo supports + and * now
jagerber48 Jul 23, 2024
7311885
move NotImplemented call out of ToUFunc wrapper and into the reflexiv…
jagerber48 Jul 24, 2024
cd10953
to_uarray_func
jagerber48 Jul 24, 2024
53f962c
rename
jagerber48 Jul 24, 2024
2dcebdb
updates, working on numpy
jagerber48 Jul 24, 2024
257e1aa
ExpandedUCombo dict functions
jagerber48 Jul 24, 2024
c541517
more accessors for expanded uncertainty
jagerber48 Jul 24, 2024
dde5156
UAtom __str__
jagerber48 Jul 24, 2024
b9e254c
some UArray tests
jagerber48 Jul 24, 2024
52af895
incorporate changes from other branch
jagerber48 Jul 25, 2024
ddcd285
tag and strip
jagerber48 Jul 25, 2024
9ca6f1a
begin modifying tests
jagerber48 Jul 25, 2024
3b26d4a
copy test --- basically reverse behavior of some tests
jagerber48 Jul 25, 2024
6db3a54
slots test
jagerber48 Jul 25, 2024
7f98ec4
test comparison ops
jagerber48 Jul 25, 2024
b6d2fed
bug call out
jagerber48 Jul 25, 2024
2152c26
type hint
jagerber48 Jul 25, 2024
7208615
test_wrapped_func_no_args_no_kwargs
jagerber48 Jul 25, 2024
ad94860
support None function again
jagerber48 Jul 25, 2024
5188151
another wrap test
jagerber48 Jul 26, 2024
7658770
merge
jagerber48 Aug 16, 2024
6d54393
Various tests, including covariance tests
jagerber48 Aug 17, 2024
56c4d3c
more tests
jagerber48 Aug 17, 2024
f17825f
wrap tests
jagerber48 Aug 17, 2024
ee9776d
all test_uncertainties tests passing
jagerber48 Aug 17, 2024
74ad6ab
single input function derivative comparisons
jagerber48 Aug 17, 2024
64b144a
double input tests
jagerber48 Aug 17, 2024
baa676a
test double inputs
jagerber48 Aug 17, 2024
b6795c9
tests
jagerber48 Aug 17, 2024
9d61883
some tests
jagerber48 Aug 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
begin modifying tests
jagerber48 committed Jul 25, 2024
commit 9ca6f1aa1cf5fda761a5075387b0576956017ca4
168 changes: 114 additions & 54 deletions tests/test_uncertainties.py
Original file line number Diff line number Diff line change
@@ -4,10 +4,14 @@
import random # noqa
from math import isnan

import uncertainties.core as uncert_core
from uncertainties.core import ufloat, AffineScalarFunc, ufloat_fromstr
import pytest

from uncertainties import formatting
from uncertainties import umath
from uncertainties.new.func_conversion import numerical_partial_derivative
from uncertainties.new.umath import float_funcs_dict
from uncertainties.new.ufloat import UFloat, ufloat, ufloat_fromstr
from uncertainties.ops import modified_operators, modified_ops_with_reflection
from helpers import (
power_special_cases,
power_all_cases,
@@ -44,10 +48,12 @@ def test_value_construction():

# Negative standard deviations should be caught in a nice way
# (with the right exception):
try:
x = ufloat(3, -0.1)
except uncert_core.NegativeStdDev:
pass
# TODO: Right now the new code allows negative std_dev. It won't affect any
# downstream calculations.
# try:
# x = ufloat(3, -0.1)
# except uncert_core.NegativeStdDev:
# pass

## Incorrect forms should not raise any deprecation warning, but
## raise an exception:
@@ -108,8 +114,18 @@ def test_ufloat_fromstr():
# NaN value:
"nan+/-3.14e2": (float("nan"), 314),
# "Double-floats"
"(-3.1415 +/- 1e-4)e+200": (-3.1415e200, 1e196),
"(-3.1415e-10 +/- 1e-4)e+200": (-3.1415e190, 1e196),
# TODO: The current version of Variable stores the passed in std_dev as an
# instance attribute and so it can take in 1e196 and calculate its string
# representation. However, if you try to do anything with this Variable, even
# multiply it by 1.0, the std_dev is recalculated and you get an overflow
# exception (from trying to square it). The new code always lazily calculates
# std_dev from the uncertainty linear combination so it hits this issue the
# first time __str__ is called and these two tests fail right away. I consider
# this to be a fluke of the old implementation rather than a regression. That
# is, it's not like the old version can really handle large numbers while the
# new can't. The truth is neither can handle these large of numbers.
# "(-3.1415 +/- 1e-4)e+200": (-3.1415e200, 1e196),
# "(-3.1415e-10 +/- 1e-4)e+200": (-3.1415e190, 1e196),
# Special float representation:
"-3(0.)": (-3, 0),
}
@@ -140,38 +156,97 @@ def test_ufloat_fromstr():
###############################################################################


# Test of correctness of the fixed (usually analytical) derivatives:
def test_fixed_derivatives_basic_funcs():
"""
Pre-calculated derivatives for operations on AffineScalarFunc.
"""

def check_op(op, num_args):
"""
Makes sure that the derivatives for function '__op__' of class
AffineScalarFunc, which takes num_args arguments, are correct.
If num_args is None, a correct value is calculated.
"""
# TODO: This test is a bit deprecated in the new approach. In the old code the
# AffineScalarFunc has a mapping from Variables to derivatives (related to
# LinearCombination) where derivatives are the partial derivatives "with respect to
# that Variable" in the function that generated the AffineScalarFunc. The Variable
# additionally has a std_dev that gets scaled by that derivative to calculate std_dev.
# This function tests that these derivatives stored on the AffineScalarFunc match the
# expected values for the derivatives of the function.
# ###
# In the new approach the UCombo is a linear combination of UAtom where each UAtom is
# a unity variance independent random variable. The std_devs get encoded into the
# coefficient that scales the UAtom, but as the UCombo passes through operations the
# partial derivatives multiply the scaling coefficients. But no memory is retained
# about if the scaling coefficient is due to the std_dev originally associated with
# the UFloat that generated the UAtom, or if it has arisen due to partial derivatives
# in some functional operation. Therefore, it doesn't make sense to compare the
# components of the resulting UCombo to the derivatives of the input function.
# ###
# I think the equivalent thing to test here is that the uncertainty linear combination
# on f(x+dx, y+dy) is equal to (df/dx dx + df/dy dy). This is checked by the new
# test_deriv_propagation below.


# # Test of correctness of the fixed (usually analytical) derivatives:
# def test_fixed_derivatives_basic_funcs():
# """
# Pre-calculated derivatives for operations on AffineScalarFunc.
# """
#
# def check_op(op, num_args):
# """
# Makes sure that the derivatives for function '__op__' of class
# AffineScalarFunc, which takes num_args arguments, are correct.
#
# If num_args is None, a correct value is calculated.
# """
#
# op_string = "__%s__" % op
# func = getattr(AffineScalarFunc, op_string)
# numerical_derivatives = uncert_core.NumericalDerivatives(
# # The __neg__ etc. methods of AffineScalarFunc only apply,
# # by definition, to AffineScalarFunc objects: we first map
# # possible scalar arguments (used for calculating
# # derivatives) to AffineScalarFunc objects:
# lambda *args: func(*map(uncert_core.to_affine_scalar, args))
# )
# compare_derivatives(func, numerical_derivatives, [num_args])
#
# # Operators that take 1 value:
# for op in uncert_core.modified_operators:
# check_op(op, 1)
#
# # Operators that take 2 values:
# for op in uncert_core.modified_ops_with_reflection:
# check_op(op, 2)


# Randomly generated but static test values.
deriv_propagation_cases = [
("__abs__", (1.1964838601545966,), 0.047308407404731856),
("__pos__", (1.5635699242286414,), 0.38219529954774223),
("__neg__", (-0.4520304708235554,), 0.8442835926901457),
("__trunc__", (0.4622631416873926,), 0.6540076679531033),
("__add__", (-0.7581877519537352, 1.6579645792821753), 0.5083165826806606),
("__radd__", (-0.976869259500134, 1.1542019729184076), -0.732839320238539),
("__sub__", (1.0233545960703134, 0.029354693323845993), 0.7475621525040559),
("__rsub__", (0.49861518245313663, -0.9927317702800833), -0.5421488555485847),
("__mul__", (0.0654070362874073, 1.9216078105121919), 0.6331001122119122),
("__rmul__", (-0.4006772142682373, 0.19628658198222926), 0.3300416314362784),
("__truediv__", (-0.5573378968194893, 0.28646277014641486), -0.42933306560556384),
("__rtruediv__", (1.7663869752268884, -0.1619387546963642), 0.6951025849642374),
("__floordiv__", (0.11750026664733992, -1.0120567560937617), -0.9557126076209381),
("__rfloordiv__", (-1.2872736512072698, -1.4416464249395973), -0.28262518984780205),
("__pow__", (0.34371967038364515, -0.8313605840956209), -0.6267147080961244),
("__rpow__", (1.593375683248082, 1.9890969272006154), 0.7171353266792271),
("__mod__", (0.7478106873313131, 1.2522332955942628), 0.5682413634363304),
("__rmod__", (1.5227432102303133, -0.5177923078991333), -0.25752786270795935),
]


@pytest.mark.parametrize("func, args, std_dev", deriv_propagation_cases)
def test_deriv_propagation(func, args, std_dev):
ufloat_args = (UFloat(arg, std_dev) for arg in args)
float_args = (ufloat.n for ufloat in ufloat_args)
output = getattr(UFloat, func)(*ufloat_args)

for idx, ufloat in enumerate(ufloat_args):
deriv = numerical_partial_derivative(func, idx, *float_args)
for atom, input_weight in output.uncertainty.expanded_dict:
output_weight = output.uncertainty.expanded_dict(atom)
assert output_weight == deriv * input_weight

op_string = "__%s__" % op
func = getattr(AffineScalarFunc, op_string)
numerical_derivatives = uncert_core.NumericalDerivatives(
# The __neg__ etc. methods of AffineScalarFunc only apply,
# by definition, to AffineScalarFunc objects: we first map
# possible scalar arguments (used for calculating
# derivatives) to AffineScalarFunc objects:
lambda *args: func(*map(uncert_core.to_affine_scalar, args))
)
compare_derivatives(func, numerical_derivatives, [num_args])

# Operators that take 1 value:
for op in uncert_core.modified_operators:
check_op(op, 1)

# Operators that take 2 values:
for op in uncert_core.modified_ops_with_reflection:
check_op(op, 2)


def test_copy():
@@ -229,21 +304,6 @@ def test_copy():
## they can be unpickled):


# Subclass without slots:
class NewVariable_dict(uncert_core.Variable):
pass


# Subclass with slots defined by a tuple:
class NewVariable_slots_tuple(uncert_core.Variable):
__slots__ = ("new_attr",)


# Subclass with slots defined by a string:
class NewVariable_slots_str(uncert_core.Variable):
__slots__ = "new_attr"


def test_pickling():
"Standard pickle module integration."