Skip to content

Commit

Permalink
Merge pull request #40 from dprada/main
Browse files Browse the repository at this point in the history
Adding functions are_compatible, are_close and are_equal
  • Loading branch information
dprada authored Mar 29, 2023
2 parents 7586b6c + b115117 commit 3102e38
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 68 deletions.
5 changes: 3 additions & 2 deletions pyunitwizard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
del get_versions, versions

# Add imports here
from .main import unit, quantity, get_form, is_quantity, is_unit, concatenate
from .main import unit, quantity, get_form, is_quantity, is_unit
from .main import get_value, get_unit, get_value_and_unit, change_value
from .main import convert
from .main import get_standard_units, standardize, get_dimensionality, compatibility, similarity, check
from .main import get_standard_units, standardize, get_dimensionality
from .main import concatenate, stack
from .main import are_compatible, are_equal, are_close, check
from . import configure
from . import kernel as _kernel

Expand Down
99 changes: 82 additions & 17 deletions pyunitwizard/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,8 @@ def is_unit(quantity_or_unit: QuantityOrUnit, parser: Optional[str]=None) -> boo
"""
if isinstance(quantity_or_unit, str):
try:
quantity_or_unit = convert(quantity_or_unit, to_type='unit', parser=parser)
form = get_form(quantity_or_unit)
output = dict_is_quantity[form](quantity_or_unit)
quantity_or_unit = convert(quantity_or_unit, parser=parser)
output = (get_value(quantity_or_unit)==1)
except:
return False
else:
Expand Down Expand Up @@ -191,8 +190,15 @@ def change_value(quantity: QuantityLike,
return dict_change_value[form](quantity, value)

def similarity(quantity_or_unit_1: QuantityOrUnit,
quantity_or_unit_2: QuantityOrUnit,
relative_tolerance: float=1e-08) -> bool:
quantity_or_unit_2: QuantityOrUnit,
relative_tolerance: float=1e-08) -> bool:

return are_close(quantity_or_unit_1, quantity_or_unit_2, rtol=relative_tolerance)

def are_close(quantity_1: QuantityLike,
quantity_2: QuantityLike,
rtol: float=1e-05,
atol: float=1e-08) -> bool:
""" Compares whether two quantities are similiar within a specified tolerance.
Parameters
Expand All @@ -211,20 +217,73 @@ def similarity(quantity_or_unit_1: QuantityOrUnit,
bool
Whether the quantities or units are similar.
"""
is_compatible = compatibility(quantity_or_unit_1, quantity_or_unit_2)
compatible = are_compatible(quantity_1, quantity_2)

if compatible:

value_1, unit_1 = get_value_and_unit(quantity_1)
value_2 = get_value(quantity_2, to_unit=unit_1)

if is_compatible:
if isinstance(value_1, (list, tuple, np.ndarray)):

form_1 = get_form(quantity_or_unit_1)
if form_1 == 'string':
quantity_or_unit_1 = convert(quantity_or_unit_1)
form_1 = get_form(quantity_or_unit_1)
quantity_or_unit_2 = convert(quantity_or_unit_2, to_form=form_1)
ratio = quantity_or_unit_1 / quantity_or_unit_2
return (abs(ratio - 1.0) < relative_tolerance)
return np.allclose(value_1, value_2, rtol=rtol, atol=atol)

else:

check_atol = (abs(value_1-value_2) < atol)
check_rtol = (abs(value_1/value_2 - 1.0) < rtol)

return (check_atol and check_rtol)

return False

def are_equal(quantity_or_unit_1: QuantityOrUnit,
quantity_or_unit_2: QuantityOrUnit) -> bool:
""" Compares whether two quantities are similiar within a specified tolerance.
Parameters
----------
quantity_or_unit_1 : QuantityOrUnit
A quantity or a unit
quantity_or_unit_2 : QuantityOrUnit
A quantity or a unit
relative_tolerance : float
The relative tolerance to compare the quantities.
Returns
-------
bool
Whether the quantities or units are similar.
"""
compatible = are_compatible(quantity_or_unit_1, quantity_or_unit_2)

if compatible:

if is_quantity(quantity_or_unit_1) and is_quantity(quantity_or_unit_2):

value_1, unit_1 = get_value_and_unit(quantity_or_unit_1)
value_2 = get_value(quantity_or_unit_2, to_unit=unit_1)

if isinstance(value_1, (list, tuple, np.ndarray)):

return np.all(np.equal(value_1, value_2))

else:

return value_1==value_2

elif is_unit(quantity_or_unit_1) and is_unit(quantity_or_unit_2):

unit_1 = convert(quantity_or_unit_1)
unit_2 = convert(quantity_or_unit_2)

return unit_1==unit_2

return False


def get_dimensionality(quantity_or_unit: QuantityOrUnit) -> Dict[str, int]:
""" Returns the dimensionality of the quantity or unit.
Expand Down Expand Up @@ -277,8 +336,14 @@ def _dimensionality_dict_to_array(dimensionality: Dict[str, int]) -> np.ndarray:

return np.array(dim_list, dtype=float)


def compatibility(quantity_or_unit_1: QuantityOrUnit,
quantity_or_unit_2: QuantityOrUnit) -> bool:

return are_compatible(quantity_or_unit_1, quantity_or_unit_2)

def are_compatible(quantity_or_unit_1: QuantityOrUnit,
quantity_or_unit_2: QuantityOrUnit) -> bool:
""" Check whether two quantities or units are compatible.
This means that they have the same dimensionalities.
Expand Down Expand Up @@ -640,7 +705,7 @@ def get_standard_units(quantity_or_unit: Optional[QuantityOrUnit]=None,
raise NoStandardsError

for standard_unit, _ in kernel.adimensional_standards.items():
if compatibility(quantity_or_unit, standard_unit):
if are_compatible(quantity_or_unit, standard_unit):
output = standard_unit
break

Expand Down Expand Up @@ -760,7 +825,7 @@ def check(quantity_or_unit: Any,

if unit is not None:
aux_unit = get_unit(quantity_or_unit)
if not similarity(aux_unit, unit):
if not are_equal(aux_unit, unit):
return False
if value_type is not None:
aux_value = get_value(quantity_or_unit)
Expand All @@ -786,7 +851,7 @@ def check(quantity_or_unit: Any,
elif is_unit(quantity_or_unit):

if unit is not None:
if not similarity(quantity_or_unit, unit):
if not are_equal(quantity_or_unit, unit):
return False
if dimensionality is not None:
aux_dimensionality = get_dimensionality(quantity_or_unit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,97 +4,97 @@
import openmm.unit as openmm_unit
import unyt

def test_similarity_parsing_error():
def test_are_close_parsing_error():

with pytest.raises(LibraryWithoutParserError):
puw.configure.reset()
puw.configure.load_library(['openmm.unit'])
puw.similarity('1 meter', '1 meter')
puw.are_close('1 meter', '1 meter')

def test_similarity_string_quantity():
def test_are_close_string_quantity():
puw.configure.reset()
puw.configure.load_library(['pint', 'openmm.unit'])

similar = puw.similarity('1 meter', '1 kilometer')
assert not similar
close = puw.are_close('1 meter', '1 kilometer')
assert not close

similar = puw.similarity('1 meter', '1 cm')
assert not similar
close = puw.are_close('1 meter', '1 cm')
assert not close

similar = puw.similarity('1 meter', '100 cm')
assert similar
close = puw.are_close('1 meter', '100 cm')
assert close

def test_similarity_pint_quantity():
def test_are_close_pint_quantity():
puw.configure.reset()
puw.configure.load_library(['pint'])

quantity_1 = puw.quantity(0.4,'cm', form="pint")
quantity_2 = puw.quantity(0.4,'cm', form="pint")
similar = puw.similarity(quantity_1, quantity_2)
assert similar
close = puw.are_close(quantity_1, quantity_2)
assert close

quantity_1 = puw.quantity(4,'mm', form="pint")
quantity_2 = puw.quantity(0.4,'cm', form="pint")
similar = puw.similarity(quantity_1, quantity_2)
assert similar
close = puw.are_close(quantity_1, quantity_2)
assert close

quantity_1 = puw.quantity(4,'mm', form="pint")
quantity_2 = puw.quantity(0.41,'cm', form="pint")
similar = puw.similarity(quantity_1, quantity_2)
assert not similar
close = puw.are_close(quantity_1, quantity_2)
assert not close

quantity_1 = puw.quantity(4,'m', form="pint")
quantity_2 = puw.quantity(4,'cm', form="pint")
similar = puw.similarity(quantity_1, quantity_2)
assert not similar
close = puw.are_close(quantity_1, quantity_2)
assert not close

def test_similarity_openmm_quantity():
def test_are_close_openmm_quantity():
puw.configure.reset()
puw.configure.load_library(['openmm.unit'])

quantity_1 = puw.quantity(0.4, openmm_unit.centimeter, form="openmm.unit")
quantity_2 = puw.quantity(0.4, openmm_unit.centimeter, form="openmm.unit")
similar = puw.similarity(quantity_1, quantity_2)
assert similar
close = puw.are_close(quantity_1, quantity_2)
assert close

quantity_1 = puw.quantity(4, openmm_unit.millimeter, form="openmm.unit")
quantity_2 = puw.quantity(0.4, openmm_unit.centimeter, form="openmm.unit")
similar = puw.similarity(quantity_1, quantity_2)
assert similar
close = puw.are_close(quantity_1, quantity_2)
assert close

quantity_1 = puw.quantity(4, openmm_unit.millimeter, form="openmm.unit")
quantity_2 = puw.quantity(0.41, openmm_unit.centimeter, form="openmm.unit")
similar = puw.similarity(quantity_1, quantity_2)
assert not similar
close = puw.are_close(quantity_1, quantity_2)
assert not close

quantity_1 = puw.quantity(1, openmm_unit.meter, form="openmm.unit")
quantity_2 = puw.quantity(1, openmm_unit.centimeter, form="openmm.unit")
similar = puw.similarity(quantity_1, quantity_2)
assert not similar
close = puw.are_close(quantity_1, quantity_2)
assert not close

def test_similarity_unyt_quantity():
def test_are_close_unyt_quantity():
puw.configure.reset()
puw.configure.load_library(['unyt'])

quantity_1 = puw.quantity(0.4, unyt.cm, form="unyt")
quantity_2 = puw.quantity(0.4, unyt.cm, form="unyt")
similar = puw.similarity(quantity_1, quantity_2)
assert similar
close = puw.are_close(quantity_1, quantity_2)
assert close

quantity_1 = puw.quantity(4, unyt.mm, form="unyt")
quantity_2 = puw.quantity(0.4, unyt.cm, form="unyt")
similar = puw.similarity(quantity_1, quantity_2)
assert similar
close = puw.are_close(quantity_1, quantity_2)
assert close

quantity_1 = puw.quantity(4, unyt.mm, form="unyt")
quantity_2 = puw.quantity(0.41, unyt.cm, form="unyt")
similar = puw.similarity(quantity_1, quantity_2)
assert not similar
close = puw.are_close(quantity_1, quantity_2)
assert not close

quantity_1 = puw.quantity(4, unyt.m, form="unyt")
quantity_2 = puw.quantity(4, unyt.cm, form="unyt")
similar = puw.similarity(quantity_1, quantity_2)
assert not similar
close = puw.are_close(quantity_1, quantity_2)
assert not close



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,41 @@
import openmm.unit as openmm_unit
import unyt

def test_compatibility_pint():
def test_are_compatible_pint():

puw.configure.reset()
puw.configure.load_library(['pint'])

q1 = puw.quantity(2.5, 'nanometers/picoseconds', form="pint")
q2 = puw.convert(q1, to_unit='angstroms/picoseconds', to_form="pint")
assert puw.compatibility(q1, q2)
assert puw.are_compatible(q1, q2)

q1 = puw.quantity(2.5, 'joules/picoseconds', form="pint")
q2 = puw.quantity(2.8, 'angstroms/picoseconds', form="pint")
assert not puw.compatibility(q1, q2)
assert not puw.are_compatible(q1, q2)

def test_are_compatible_openmm():

def test_compatibility_openmm():
puw.configure.reset()
puw.configure.load_library(['openmm.unit'])

q1 = puw.quantity(2.5, openmm_unit.nanometer/openmm_unit.picosecond, form="openmm.unit")
q2 = puw.quantity(3.0, openmm_unit.angstrom/openmm_unit.second, form="openmm.unit")
assert puw.compatibility(q1, q2)
assert puw.are_compatible(q1, q2)

q1 = puw.quantity(2.5, openmm_unit.joule/openmm_unit.second, form="openmm.unit")
q2 = puw.quantity(3.0, openmm_unit.angstrom/openmm_unit.second, form="openmm.unit")
assert not puw.compatibility(q1, q2)
assert not puw.are_compatible(q1, q2)

def test_are_compatible_unyt():

def test_compatibility_unyt():
puw.configure.reset()
puw.configure.load_library(['unyt'])

q1 = puw.quantity(2.5, unyt.nm/unyt.ps, form="unyt")
q2 = puw.quantity(3.0, unyt.m/unyt.second, form="unyt")
assert puw.compatibility(q1, q2)
assert puw.are_compatible(q1, q2)

q1 = puw.quantity(2.5, unyt.J/unyt.second, form="unyt")
q2 = puw.quantity(3.0, unyt.m/unyt.second, form="unyt")
assert not puw.compatibility(q1, q2)
assert not puw.are_compatible(q1, q2)
Loading

0 comments on commit 3102e38

Please sign in to comment.