From 6a136034b492fbdb99aab7dc314361b1aa74da72 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Mon, 12 Jan 2015 16:49:25 +0100 Subject: [PATCH 1/3] Implement union simplification. --- ppp_libmodule/simplification.py | 36 +++++++++++++++++++++++++++++++++ setup.py | 2 +- tests/test_simplification.py | 26 ++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 ppp_libmodule/simplification.py create mode 100644 tests/test_simplification.py diff --git a/ppp_libmodule/simplification.py b/ppp_libmodule/simplification.py new file mode 100644 index 0000000..f73af21 --- /dev/null +++ b/ppp_libmodule/simplification.py @@ -0,0 +1,36 @@ +import itertools + +from ppp_datamodel.nodes import Resource +from ppp_datamodel.nodes.list_operators import * + +__all__ = ['simplify'] + +def simplify_union(tree): + if len(tree.list) == 0: + return List([]) + elif len(tree.list) == 1: + return tree.list[0] + list_ = [x for x in tree.list if x] + lists = (x.list for x in list_ if isinstance(x, List)) + not_lists = [x for x in list_ if not isinstance(x, List)] + lists = list(set(itertools.chain(*lists))) + if not_lists: + all_ = not_lists + all_.append(List(lists)) + return Union(all_) + else: + return List(lists) + + +predicates = { + Union: simplify_union + } + +def predicate(tree): + for (cls, f) in predicates.items(): + if isinstance(tree, cls): + return f(tree) + return tree + +def simplify(tree): + return tree.traverse(predicate) diff --git a/setup.py b/setup.py index d860652..77f1be6 100755 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ 'Topic :: Software Development :: Libraries', ], install_requires=[ - 'ppp_datamodel>=0.5.2,<0.6', + 'ppp_datamodel>=0.5.22,<0.6', ], packages=[ 'ppp_libmodule', diff --git a/tests/test_simplification.py b/tests/test_simplification.py new file mode 100644 index 0000000..a61881a --- /dev/null +++ b/tests/test_simplification.py @@ -0,0 +1,26 @@ +import unittest + +from ppp_datamodel.nodes import Triple as T +from ppp_datamodel.nodes import Missing as M +from ppp_datamodel.nodes import Resource as R +from ppp_datamodel.nodes.list_operators import * +from ppp_libmodule.simplification import simplify + + +class SimplificationTestCase(unittest.TestCase): + def testUnionTrivial(self): + self.assertEqual(simplify(Union([])), List([])) + self.assertEqual(simplify(Union([List([R('a')])])), + List([R('a')])) + def testUnionResourceLists(self): + t = Union([List([R('a'), R('b')]), List([R('c')])]) + t = simplify(t) + self.assertIsInstance(t, List) + self.assertEqual(set(t.list), {R('a'), R('b'), R('c')}) + def testUnionMixed(self): + t = Union([List([R('a'), R('b')]), T(M(), M(), M())]) + t = simplify(t) + self.assertIsInstance(t, Union) + self.assertEqual(t.list[0], T(M(), M(), M())) + self.assertIsInstance(t.list[1], List) + self.assertEqual(set(t.list[1].list), {R('a'), R('b')}) From 90c2099872fd37c6ca6be952bae66faacb2059e4 Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Mon, 12 Jan 2015 16:59:38 +0100 Subject: [PATCH 2/3] Comments. --- ppp_libmodule/simplification.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ppp_libmodule/simplification.py b/ppp_libmodule/simplification.py index f73af21..b7d77e0 100644 --- a/ppp_libmodule/simplification.py +++ b/ppp_libmodule/simplification.py @@ -6,19 +6,25 @@ __all__ = ['simplify'] def simplify_union(tree): + # Trivial cases if len(tree.list) == 0: return List([]) elif len(tree.list) == 1: return tree.list[0] + + # Partition the items in lists / non-lists list_ = [x for x in tree.list if x] lists = (x.list for x in list_ if isinstance(x, List)) not_lists = [x for x in list_ if not isinstance(x, List)] + + # Make union of lists lists = list(set(itertools.chain(*lists))) - if not_lists: + + if not_lists: # If there are non-lists (eg. triples) all_ = not_lists all_.append(List(lists)) return Union(all_) - else: + else: # If there are only lists return List(lists) From 8a287269a83cfe84b1e98522c050e6d888b13cfc Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Mon, 12 Jan 2015 17:27:32 +0100 Subject: [PATCH 3/3] Implement intersection simplification. --- ppp_libmodule/simplification.py | 43 +++++++++++++++++++++++++++------ tests/test_simplification.py | 16 ++++++++++++ 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/ppp_libmodule/simplification.py b/ppp_libmodule/simplification.py index b7d77e0..f6b0045 100644 --- a/ppp_libmodule/simplification.py +++ b/ppp_libmodule/simplification.py @@ -1,3 +1,4 @@ +import operator import itertools from ppp_datamodel.nodes import Resource @@ -5,6 +6,13 @@ __all__ = ['simplify'] +def partition(pred, list_): + # Partition the items in lists / non-lists + list_ = [x for x in list_ if x] + lists = filter(pred, list_) + not_lists = itertools.filterfalse(pred, list_) + return (lists, not_lists) + def simplify_union(tree): # Trivial cases if len(tree.list) == 0: @@ -12,24 +20,43 @@ def simplify_union(tree): elif len(tree.list) == 1: return tree.list[0] - # Partition the items in lists / non-lists - list_ = [x for x in tree.list if x] - lists = (x.list for x in list_ if isinstance(x, List)) - not_lists = [x for x in list_ if not isinstance(x, List)] - + (lists, non_lists) = partition(lambda x:isinstance(x, List), tree.list) # Make union of lists + lists = map(operator.attrgetter('list'), lists) lists = list(set(itertools.chain(*lists))) - if not_lists: # If there are non-lists (eg. triples) - all_ = not_lists + non_lists = list(non_lists) + if non_lists: # If there are non-lists (eg. triples) + all_ = non_lists all_.append(List(lists)) return Union(all_) else: # If there are only lists return List(lists) +def simplify_intersection(tree): + # Trivial cases + if len(tree.list) == 0: + return tree + elif len(tree.list) == 1: + return tree.list[0] + + (lists, non_lists) = partition(lambda x:isinstance(x, List), tree.list) + # Make intersection of lists + lists = list(map(set, map(operator.attrgetter('list'), lists))) + lists = list(lists[0].intersection(*lists[1:])) + + non_lists = list(non_lists) + if non_lists: # If there are non-lists (eg. triples) + all_ = non_lists + all_.append(List(lists)) + return Intersection(all_) + else: # If there are only lists + return List(lists) + predicates = { - Union: simplify_union + Union: simplify_union, + Intersection: simplify_intersection, } def predicate(tree): diff --git a/tests/test_simplification.py b/tests/test_simplification.py index a61881a..be18ebf 100644 --- a/tests/test_simplification.py +++ b/tests/test_simplification.py @@ -24,3 +24,19 @@ def testUnionMixed(self): self.assertEqual(t.list[0], T(M(), M(), M())) self.assertIsInstance(t.list[1], List) self.assertEqual(set(t.list[1].list), {R('a'), R('b')}) + + def testIntersectionTrivial(self): + self.assertEqual(simplify(Intersection([])), Intersection([])) + self.assertEqual(simplify(Intersection([List([R('a')])])), + List([R('a')])) + def testIntersectionResourceLists(self): + t = Intersection([List([R('a'), R('b')]), List([R('c'), R('a')])]) + t = simplify(t) + self.assertEqual(t, List([R('a')])) + def testIntersectionMixed(self): + t = Intersection([List([R('a'), R('b')]), List([R('c'), R('a')]), + T(M(), M(), M())]) + t = simplify(t) + self.assertIsInstance(t, Intersection) + self.assertEqual(t.list[0], T(M(), M(), M())) + self.assertEqual(t.list[1], List([R('a')]))