Skip to content

Commit

Permalink
Find integers and floats of equivalent values (#169)
Browse files Browse the repository at this point in the history
* Finds integers and floats of equivalent values.

Integers stored as floats or ints can be found using equivalent float or
int values
	e.g. signac find a 1.0 returns jobs with sp.a = 1 or sp.a =
1.0
	signac find a 1 returns jobs with sp.a = 1 or sp.a = 1.0

Modifies unit tests to accommodate change.

Fixes #164

* Make type more explicit in collection.find_expression

This was causing tests to fail on python2.7 where round() returns
floating point values, whereas round() returns ints in python3.

Change hash(round(value)) == hash(value) to round(value) == value to
check if a value is an integer value (but may be a floating point data
type).

* Simplify type-checking logic and add comments

Change made per request from code review
(#169 (comment)).
I also added a comment block to explain why we added the check there.

* Tidy up imports in collection.py

* Add Kelly to list of contributors

* Use is_integer() to check if floats are integer-valued

* Minor syntactic update.

* Update changelog.
  • Loading branch information
tcmoore3 authored and csadorf committed Apr 12, 2019
1 parent 8da8757 commit e55cc68
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 9 deletions.
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ next
----

- Add command line options ``--sp`` and ``--doc`` for ``signac find`` that allow users to display key-value pairs of the state point and document in combination with the job id (#97, #146).
- Fix: Searches for whole numbers will match all numerically matching integers regardless of whether they are stored as decimals or whole numbers (#169).


[1.0.0] -- 2019-02-28
Expand Down
1 change: 1 addition & 0 deletions contributors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ Bradley Dice
Tim Moore
Pengji Zhou
Eric Harper
Kelly Wang
24 changes: 19 additions & 5 deletions signac/contrib/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@
# on files on the local file system instead of a MongoDB database.
#
# [1]: https://github.com/mongodb/mongo-python-driver
import sys
import argparse
import io
import re
import logging
import argparse
import operator
import re
import sys
from itertools import islice
from numbers import Number

from ..core import json
from ..common import six
Expand All @@ -30,7 +31,6 @@
from collections.abc import Mapping

if six.PY2 or (six.PY3 and sys.version_info.minor < 5):

def isclose(a, b, rel_tol=1e-9, abs_tol=0.0):
return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
else:
Expand Down Expand Up @@ -620,7 +620,21 @@ def _find_expression(self, key, value):
raise KeyError("Unknown expression-operator '{}'.".format(op))
else:
index = self.index(key, build=True)
return index.get(value, set())
# Check to see if 'value' is a floating point type but an
# integer value (e.g., 4.0), and search for both the int and float
# values. This allows the user to find statepoints that have
# integer-valued keys that are stored as floating point types.
# Note that this both cases: 1) user searches for an int and hopes
# to find values that are stored as integer-valued floats and 2) user
# searches for a integer-valued float and hopes to find ints.
# This way, both `signac find x 4.0` and `signac find x 4` would
# return jobs where `sp.x` is stored as either 4.0 or 4.
if isinstance(value, Number) and float(value).is_integer():
result_float = index.get(_float(value), set())
result_int = index.get(int(value), set())
return result_int.union(result_float)
else:
return index.get(value, set())

def _find_result(self, expr):
if not len(expr):
Expand Down
10 changes: 6 additions & 4 deletions tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,14 @@ def test_insert_multiple(self):
def test_int_float_equality(self):
self.c.insert_one(dict(a=1))
self.c.insert_one(dict(a=1.0))
self.assertEqual(len(self.c.find(dict(a=1))), 1)
self.assertEqual(len(self.c.find(dict(a=1.0))), 1)
self.assertEqual(len(self.c.find(dict(a=1))), 2)
self.assertEqual(len(self.c.find(dict(a=1.0))), 2)
"""
for doc in self.c.find(dict(a=1)):
self.assertEqual(type(doc['a']), int)
for doc in self.c.find(dict(a=1.0)):
self.assertEqual(type(doc['a']), float)
"""

def test_copy(self):
docs = [dict(_id=str(i)) for i in range(10)]
Expand Down Expand Up @@ -259,7 +261,7 @@ def test_find_integer(self):
self.c.update(docs)
self.assertEqual(len(self.c.find()), len(docs))
self.assertEqual(len(self.c.find({'a': 0})), 1)
self.assertEqual(len(self.c.find({'a': 0.0})), 0)
self.assertEqual(len(self.c.find({'a': 0.0})), 1)
self.assertEqual(list(self.c.find({'a': 0}))[0], docs[0])
self.assertEqual(len(self.c.find({'a': -1})), 0)
self.assertEqual(len(self.c.find({'a.b': 0})), 0)
Expand All @@ -276,7 +278,7 @@ def test_find_float(self):
docs = [dict(a=float(i)) for i in range(10)]
self.c.update(docs)
self.assertEqual(len(self.c.find()), len(docs))
self.assertEqual(len(self.c.find({'a': 0})), 0)
self.assertEqual(len(self.c.find({'a': 0})), 1)
self.assertEqual(len(self.c.find({'a': 0.0})), 1)
self.assertEqual(list(self.c.find({'a': 0.0}))[0], docs[0])
self.assertEqual(len(self.c.find({'a': -1})), 0)
Expand Down

0 comments on commit e55cc68

Please sign in to comment.