From 29e20d2da4b959ae0e7cbe3b3e09cd42dc9854f3 Mon Sep 17 00:00:00 2001 From: au596283 Date: Fri, 15 Jul 2022 16:12:53 +0200 Subject: [PATCH 01/16] hostPzeillingerLex added --- hironaka/host_ZeillingerLex.py | 46 ++++++++++++++++++++++++++++++++++ test/testThom.py | 2 +- 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 hironaka/host_ZeillingerLex.py diff --git a/hironaka/host_ZeillingerLex.py b/hironaka/host_ZeillingerLex.py new file mode 100644 index 0000000..4c2f2b6 --- /dev/null +++ b/hironaka/host_ZeillingerLex.py @@ -0,0 +1,46 @@ +import abc +from itertools import combinations + +import numpy as np + + +def get_char_vector(vt): + """ + Character vector (L, S), + L: maximum coordinate - minimum coordinate + S: sum of the numbers of maximum coordinates and minimum coordinates + e.g., (1, 1, -1, -1) -> (L=2, S=4) + """ + mx = max(vt) + mn = min(vt) + L = mx - mn + S = sum([vt[i] == mx for i in range(len(vt))]) + \ + sum([vt[i] == mn for i in range(len(vt))]) + return L, S + + +def select_coord(self, points: list, debug=False): + dim = len(points[-1]) + result = [] + for pts in range(len(points)): + if len(pts) <= 1: + result.append([]) + continue + pairs = combinations(pts, 2) + char_vectors = [] + for pair in pairs: + vector = tuple([pair[0][i] - pair[1][i] for i in range(dim)]) + char_vectors.append((vector, self.get_char_vector(vector))) + char_vectors.sort(key=(lambda x: x[1])) + + if debug: + print(char_vectors) + + for char_vector in char_vectors: + r = [np.argmin(char_vector[0]), np.argmax(char_vector[0])] + if r[0] != r[1]: + result.append(r) + else: # if all coordinates are the same, return the first two. + result.append([0, 1]) + +return result \ No newline at end of file diff --git a/test/testThom.py b/test/testThom.py index 52426c0..71dda64 100644 --- a/test/testThom.py +++ b/test/testThom.py @@ -13,7 +13,7 @@ class TestThom(unittest.TestCase): def test_game(self): - N = 4 + N = 3 host = Zeillinger() agent = AgentMorin() points = thom_points_homogeneous(N) From a377b5d27ac376e67a3aae90c614366bda4264c5 Mon Sep 17 00:00:00 2001 From: au596283 Date: Fri, 15 Jul 2022 18:27:53 +0200 Subject: [PATCH 02/16] host ZeillingerLex added --- hironaka/host.py | 48 +++++++++++++++++++ hironaka/host_ZeillingerLex.py | 88 ++++++++++++++++++++-------------- test/testThom.py | 10 ++-- 3 files changed, 104 insertions(+), 42 deletions(-) diff --git a/hironaka/host.py b/hironaka/host.py index 20205c4..3846404 100644 --- a/hironaka/host.py +++ b/hironaka/host.py @@ -61,3 +61,51 @@ def select_coord(self, points: Points, debug=False): result.append([0, 1]) return result + +class ZeillingerLex(Host): + # noinspection PyPep8Naming + @staticmethod + def get_char_vector(vt): + """ + Character vector (L, S), + L: maximum coordinate - minimum coordinate + S: sum of the numbers of maximum coordinates and minimum coordinates + e.g., (1, 1, -1, -1) -> (L=2, S=4) + """ + mx = max(vt) + mn = min(vt) + L = mx - mn + S = sum([vt[i] == mx for i in range(len(vt))]) + \ + sum([vt[i] == mn for i in range(len(vt))]) + return L, S + + def select_coord(self, points: Points, debug=False): + assert not points.ended + dim = points.dimension + result = [] + for b in range(points.batch_size): + pts = points.get_batch(b) + if len(pts) <= 1: + result.append([]) + continue + pairs = combinations(pts, 2) + char_vectors = [] + for pair in pairs: + vector = tuple([pair[0][i] - pair[1][i] for i in range(dim)]) + char_vectors.append((vector, self.get_char_vector(vector))) + char_vectors.sort(key=(lambda x: x[1])) + + if debug: + print(char_vectors) + coords = [] + for char_vector in char_vectors: + if char_vector[1] == char_vectors[0][1]: + r = [np.argmin(char_vector[0]), np.argmax(char_vector[0])] + if r[0] != r[1]: + coords.append(r) + else: # if all coordinates are the same, return the first two. + coords.append([0, 1]) + coords.sort() + result.append(coords[0]) + return result + diff --git a/hironaka/host_ZeillingerLex.py b/hironaka/host_ZeillingerLex.py index 4c2f2b6..57d4330 100644 --- a/hironaka/host_ZeillingerLex.py +++ b/hironaka/host_ZeillingerLex.py @@ -1,46 +1,60 @@ import abc +import numpy as np from itertools import combinations -import numpy as np +class Host(metaclass=abc.ABCMeta): + @abc.abstractmethod + def selectCoord(self, points, debug=False): + pass + + +class RandomHost(Host): + def selectCoord(self, points, debug=False): + assert points + + DIM = len(points[0]) + return list(np.random.choice(list(range(DIM)), size=2)) -def get_char_vector(vt): - """ - Character vector (L, S), - L: maximum coordinate - minimum coordinate - S: sum of the numbers of maximum coordinates and minimum coordinates - e.g., (1, 1, -1, -1) -> (L=2, S=4) - """ - mx = max(vt) - mn = min(vt) - L = mx - mn - S = sum([vt[i] == mx for i in range(len(vt))]) + \ - sum([vt[i] == mn for i in range(len(vt))]) - return L, S - - -def select_coord(self, points: list, debug=False): - dim = len(points[-1]) - result = [] - for pts in range(len(points)): - if len(pts) <= 1: - result.append([]) - continue - pairs = combinations(pts, 2) - char_vectors = [] + +class ZeillingerLex(Host): + def getCharVector(self, vt): + mx = max(vt) + mn = min(vt) + L = mx - mn + S = sum([vt[i] == mx for i in range(len(vt))]) + \ + sum([vt[i] == mn for i in range(len(vt))]) + return (L, S) + + def selectCoord(self, points, debug=False): + assert points + + DIM = len(points[0]) + pairs = combinations(points, 2) + charVectors = [] for pair in pairs: - vector = tuple([pair[0][i] - pair[1][i] for i in range(dim)]) - char_vectors.append((vector, self.get_char_vector(vector))) - char_vectors.sort(key=(lambda x: x[1])) + vector = tuple([pair[0][i] - pair[1][i] for i in range(DIM)]) + charVectors.append((vector, self.getCharVector(vector))) + charVectors.sort(key=(lambda x: x[1])) if debug: - print(char_vectors) - - for char_vector in char_vectors: - r = [np.argmin(char_vector[0]), np.argmax(char_vector[0])] - if r[0] != r[1]: - result.append(r) - else: # if all coordinates are the same, return the first two. - result.append([0, 1]) + print(charVectors) + + coords = [] + for char_vector in charVectors: + if char_vector[1] == charVectors[0][1]: + r = [np.argmin(char_vector[0]), np.argmax(char_vector[0])] + if r[0] != r[1]: + coords.append(r) + else: # if all coordinates are the same, return the first two. + coords.append([0, 1]) + coords.sort() + return coords + +A = [(1,0,1,0), (1,0,0,1), (0,2,0,0), (0,1,0,1)] +host = ZeillingerLex() +print(host.selectCoord(A)) +B = [[3, 1]] +B.sort() +print(B) -return result \ No newline at end of file diff --git a/test/testThom.py b/test/testThom.py index 71dda64..933bffd 100644 --- a/test/testThom.py +++ b/test/testThom.py @@ -6,15 +6,15 @@ from hironaka.abs import Points from hironaka.agentThom import AgentMorin from hironaka.gameThom import GameMorin -from hironaka.host import Zeillinger +from hironaka.host import Zeillinger, ZeillingerLex from hironaka.util.geomThom import thom_points, thom_points_homogeneous, thom_monomial_ideal from hironaka.util.searchThom import search_tree_morin class TestThom(unittest.TestCase): def test_game(self): - N = 3 - host = Zeillinger() + N = 5 + host = ZeillingerLex() agent = AgentMorin() points = thom_points_homogeneous(N) opoints = thom_points(N) @@ -32,7 +32,7 @@ def test_game(self): print(game.move_history) def test_ThomTree(self): - points = thom_points_homogeneous(3) + points = thom_points_homogeneous(6) print(f"Points: {points}") dimension = len(points[0]) initial_points = Points([points], distinguished_points=[len(points) - 1]) @@ -41,7 +41,7 @@ def test_ThomTree(self): tree.create_node(0, 0, data=initial_points) MAX_SIZE = 10000 - host = Zeillinger() + host = ZeillingerLex() weights = [1] * dimension search_tree_morin(initial_points, tree, 0, weights, host, max_size=MAX_SIZE) tree.show(data_property="points", idhidden=False) From 4d92ab43e4319d962d4ce6b1f56ba2bce6d44c3c Mon Sep 17 00:00:00 2001 From: au596283 Date: Sat, 16 Jul 2022 14:40:56 +0200 Subject: [PATCH 03/16] host ZeillingerLex and WeakSpivakovsky are added --- hironaka/host.py | 23 +++++++++++++ hironaka/host_ZeillingerLex.py | 60 ---------------------------------- hironaka/util/searchThom.py | 1 + test/testThom.py | 8 ++--- 4 files changed, 28 insertions(+), 64 deletions(-) delete mode 100644 hironaka/host_ZeillingerLex.py diff --git a/hironaka/host.py b/hironaka/host.py index 3846404..10a71fa 100644 --- a/hironaka/host.py +++ b/hironaka/host.py @@ -109,3 +109,26 @@ def select_coord(self, points: Points, debug=False): result.append(coords[0]) return result +class WeakSpivakovsky(Host): + def select_coord(self, points:Points, debug=False): + assert points + dim = points.dimension + for b in range(points.batch_size): + pts = points.get_batch(b) + if len(pts) <= 1: + result.append([]) + continue + "For each point we store the subset of nonzero coordinates" + subsets = [set(np.nonzero(point)[0]) for point in pts] + "Find a minimal hitting set, brute-force" + U = set.union(*subsets) + result = [] + for i in range(1, len(U)+1): + combs = combinations(U, i) + for c in combs: + if all(set(c) & l for l in subsets): + result.append(c) + if result: + break + return result + diff --git a/hironaka/host_ZeillingerLex.py b/hironaka/host_ZeillingerLex.py deleted file mode 100644 index 57d4330..0000000 --- a/hironaka/host_ZeillingerLex.py +++ /dev/null @@ -1,60 +0,0 @@ -import abc -import numpy as np -from itertools import combinations - - -class Host(metaclass=abc.ABCMeta): - @abc.abstractmethod - def selectCoord(self, points, debug=False): - pass - - -class RandomHost(Host): - def selectCoord(self, points, debug=False): - assert points - - DIM = len(points[0]) - return list(np.random.choice(list(range(DIM)), size=2)) - - -class ZeillingerLex(Host): - def getCharVector(self, vt): - mx = max(vt) - mn = min(vt) - L = mx - mn - S = sum([vt[i] == mx for i in range(len(vt))]) + \ - sum([vt[i] == mn for i in range(len(vt))]) - return (L, S) - - def selectCoord(self, points, debug=False): - assert points - - DIM = len(points[0]) - pairs = combinations(points, 2) - charVectors = [] - for pair in pairs: - vector = tuple([pair[0][i] - pair[1][i] for i in range(DIM)]) - charVectors.append((vector, self.getCharVector(vector))) - charVectors.sort(key=(lambda x: x[1])) - - if debug: - print(charVectors) - - coords = [] - for char_vector in charVectors: - if char_vector[1] == charVectors[0][1]: - r = [np.argmin(char_vector[0]), np.argmax(char_vector[0])] - if r[0] != r[1]: - coords.append(r) - else: # if all coordinates are the same, return the first two. - coords.append([0, 1]) - coords.sort() - return coords - -A = [(1,0,1,0), (1,0,0,1), (0,2,0,0), (0,1,0,1)] -host = ZeillingerLex() -print(host.selectCoord(A)) -B = [[3, 1]] -B.sort() -print(B) - diff --git a/hironaka/util/searchThom.py b/hironaka/util/searchThom.py index 433bc20..c0a1a28 100644 --- a/hironaka/util/searchThom.py +++ b/hironaka/util/searchThom.py @@ -23,6 +23,7 @@ def search_tree_morin(points: Points, tree, curr_node, curr_weights, host, max_s tree.create_node(node_id, node_id, parent=curr_node, data=NoCont('...more...')) return tree coords = host.select_coord(points)[0] + print(coords) for action in coords: if curr_weights[action] > min([curr_weights[i] for i in coords]): diff --git a/test/testThom.py b/test/testThom.py index 933bffd..cb43ed8 100644 --- a/test/testThom.py +++ b/test/testThom.py @@ -6,14 +6,14 @@ from hironaka.abs import Points from hironaka.agentThom import AgentMorin from hironaka.gameThom import GameMorin -from hironaka.host import Zeillinger, ZeillingerLex +from hironaka.host import Zeillinger, ZeillingerLex, WeakSpivakovsky from hironaka.util.geomThom import thom_points, thom_points_homogeneous, thom_monomial_ideal from hironaka.util.searchThom import search_tree_morin class TestThom(unittest.TestCase): def test_game(self): - N = 5 + N = 3 host = ZeillingerLex() agent = AgentMorin() points = thom_points_homogeneous(N) @@ -32,7 +32,7 @@ def test_game(self): print(game.move_history) def test_ThomTree(self): - points = thom_points_homogeneous(6) + points = thom_points_homogeneous(5) print(f"Points: {points}") dimension = len(points[0]) initial_points = Points([points], distinguished_points=[len(points) - 1]) @@ -41,7 +41,7 @@ def test_ThomTree(self): tree.create_node(0, 0, data=initial_points) MAX_SIZE = 10000 - host = ZeillingerLex() + host = WeakSpivakovsky() weights = [1] * dimension search_tree_morin(initial_points, tree, 0, weights, host, max_size=MAX_SIZE) tree.show(data_property="points", idhidden=False) From cac68128f4d448476b0103a87a24fa03add6bcf6 Mon Sep 17 00:00:00 2001 From: au596283 Date: Sat, 16 Jul 2022 16:01:33 +0200 Subject: [PATCH 04/16] minor mathematical error fixed in quadratic_fixed_points in geomThom.py. Tests are fixed accordingly. --- hironaka/util/geomThom.py | 2 +- hironaka/util/searchThom.py | 1 - test/testThom.py | 10 ++++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/hironaka/util/geomThom.py b/hironaka/util/geomThom.py index fc5810e..533d0f4 100644 --- a/hironaka/util/geomThom.py +++ b/hironaka/util/geomThom.py @@ -33,7 +33,7 @@ def quadratic_fixed_points(order_of_jets: int): P = [] for s in range(2, order_of_jets + 1): for lin_set in combinations(list(range(order_of_jets)), s): - P = list(list(L) for L in list(combinations_with_replacement(list(lin_set), 2)) if L[0] + L[1] < order_of_jets - 1) + P = list(list(L) for L in list(combinations_with_replacement(list(range(order_of_jets)), 2)) if L[0] + L[1] < order_of_jets - 1) if len(P) >= order_of_jets-s: for comb in list(combinations(list(P), order_of_jets - s)): Q.append([list(lin_set), list(comb)]) diff --git a/hironaka/util/searchThom.py b/hironaka/util/searchThom.py index c0a1a28..433bc20 100644 --- a/hironaka/util/searchThom.py +++ b/hironaka/util/searchThom.py @@ -23,7 +23,6 @@ def search_tree_morin(points: Points, tree, curr_node, curr_weights, host, max_s tree.create_node(node_id, node_id, parent=curr_node, data=NoCont('...more...')) return tree coords = host.select_coord(points)[0] - print(coords) for action in coords: if curr_weights[action] > min([curr_weights[i] for i in coords]): diff --git a/test/testThom.py b/test/testThom.py index cb43ed8..af68aef 100644 --- a/test/testThom.py +++ b/test/testThom.py @@ -13,12 +13,13 @@ class TestThom(unittest.TestCase): def test_game(self): - N = 3 + N = 4 host = ZeillingerLex() agent = AgentMorin() points = thom_points_homogeneous(N) opoints = thom_points(N) game = GameMorin(Points([points], distinguished_points=[len(points) - 1]), host, agent) + print('Thom monomial ideal:', thom_monomial_ideal(N)) print('Original Points:', opoints) print('Homogeneous Points:', points) for i in range(100): @@ -32,7 +33,7 @@ def test_game(self): print(game.move_history) def test_ThomTree(self): - points = thom_points_homogeneous(5) + points = thom_points_homogeneous(4) print(f"Points: {points}") dimension = len(points[0]) initial_points = Points([points], distinguished_points=[len(points) - 1]) @@ -55,13 +56,14 @@ def test_thom_points(self): assert str(thom_points(2)) == thom_points_2 thom_monomial_idea_3 = \ - "[-b[0, 0]**3*b[1, 2], 2*b[0, 0]**2*b[1, 1]**2, -b[0, 0]**3*b[2, 2], b[0, 0]*b[1, 1]*b[2, 2]]" + "[-b[0, 0]**3*b[1, 2], 2*b[0, 0]**2*b[1, 1]**2, -b[0, 0]**3*b[2, 2], 0, 0, 0, b[0, 0]*b[1, 1]*b[2, 2]]" assert str(thom_monomial_ideal(3)) == thom_monomial_idea_3 thom_points_homogeneous_3 = "[[1, 0, 1, 0], [1, 0, 0, 1], [0, 2, 0, 0], [0, 1, 0, 1]]" assert str(thom_points_homogeneous(3)) == thom_points_homogeneous_3 - thom_points_homogeneous_4 = "[[2, 1, 0, 1, 0, 0, 0], [2, 0, 2, 0, 0, 0, 0], [2, 0, 0, 0, 2, 0, 0], [1, 2, 1, 0, 0, 0, 0], [2, 0, 1, 0, 0, 1, 0], [2, 0, 1, 0, 0, 0, 1], [2, 0, 0, 1, 1, 0, 0], [2, 0, 0, 0, 1, 0, 1], [0, 4, 0, 0, 0, 0, 0], [1, 2, 0, 0, 0, 1, 0], [1, 2, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 0, 0], [1, 1, 0, 0, 2, 0, 0], [0, 3, 0, 0, 1, 0, 0], [1, 1, 0, 0, 1, 0, 1]]" + thom_points_homogeneous_4 = \ + "[[2, 1, 0, 1, 0, 0, 0], [2, 1, 0, 0, 0, 1, 0], [2, 1, 0, 0, 0, 0, 1], [2, 0, 2, 0, 0, 0, 0], [2, 0, 1, 0, 1, 0, 0], [2, 0, 0, 0, 2, 0, 0], [1, 2, 1, 0, 0, 0, 0], [1, 2, 0, 0, 1, 0, 0], [2, 0, 1, 0, 0, 1, 0], [2, 0, 1, 0, 0, 0, 1], [2, 0, 0, 1, 1, 0, 0], [2, 0, 0, 0, 1, 0, 1], [0, 4, 0, 0, 0, 0, 0], [1, 2, 0, 0, 0, 1, 0], [1, 2, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 0, 0], [1, 1, 0, 0, 2, 0, 0], [0, 3, 0, 0, 1, 0, 0], [1, 1, 0, 0, 1, 0, 1]]" assert str(thom_points_homogeneous(4)) == thom_points_homogeneous_4 From e1bf065b4ee4f84b18adfefef2094c27b15ba844 Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 16 Jul 2022 20:01:44 -0400 Subject: [PATCH 05/16] Major fix. --- README.md | 28 ++++- hironaka/abs/PointsTensor.py | 31 +++--- hironaka/agent.py | 35 +++++- hironaka/agentThom.py | 54 --------- hironaka/envs/HironakaBase.py | 3 +- hironaka/game.py | 105 +++++++++++++++++- hironaka/gameHironaka.py | 60 ---------- hironaka/gameThom.py | 66 ----------- hironaka/host.py | 7 +- hironaka/policy/NNPolicy.py | 2 +- hironaka/policy/__init__.py | 2 +- hironaka/policy_players/PolicyAgent.py | 3 +- hironaka/policy_players/__init__.py | 2 +- hironaka/src/__init__.py | 2 + hironaka/src/_list_ops.py | 4 +- hironaka/src/_snippets.py | 8 +- .../geomThom.py => src/_thom_snippets.py} | 17 +-- hironaka/util/search.py | 44 +++++++- hironaka/util/searchThom.py | 46 -------- hironaka/validator/HironakaValidator.py | 4 +- hironaka/validator/__init__.py | 2 +- test/testGame.py | 2 +- test/testPoints.py | 2 +- test/testPolicy.py | 2 +- test/testThom.py | 32 ++++-- test/testTorchPoints.py | 1 - test/testUtil.py | 2 +- 27 files changed, 279 insertions(+), 287 deletions(-) delete mode 100644 hironaka/agentThom.py delete mode 100644 hironaka/gameHironaka.py delete mode 100644 hironaka/gameThom.py rename hironaka/{util/geomThom.py => src/_thom_snippets.py} (85%) delete mode 100644 hironaka/util/searchThom.py diff --git a/README.md b/README.md index e40ae9c..00404b7 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,22 @@ # Hironaka -A utility package for reinforcement learning study of Hironaka's game of local resolution of singularities and its variation problems. +A utility package for reinforcement learning study of Hironaka's game of local resolution of singularities and its +variation problems. For ML and RL specialists, the following two sections should give you an overview: + - [Rule of the game](#rule-of-the-game) - [The structure of the repo](#the-structure-of-the-repo) For math-oriented viewers or ML experts who are intrigued about the background story, please feel free to continue with: + - [What is a resolution of singularity](#what-is-a-resolution-of-singularity) - [What is Hironaka's polyhedral game](#what-is-hironakas-polyhedral-game) - [Variations of Hironaka's game](#variations-of-hironakas-game) - [Further topics](#further-topics) # Rule of the game + There are two players. To separate the roles, we name them 'host' and 'agent'. Game state: a set of integral points ....... @@ -24,29 +28,49 @@ Agent: ...... ...... # The structure of the repo + ...... --- -Now the big question is, how is this game related to some fundamental questions in pure math, or specifically, algebraic geometry. +Now the big question is, how is this game related to some fundamental questions in pure math, or specifically, algebraic +geometry. + # What is a resolution of singularity + [intro] + ## Smoothness + [definition] & [examples] + ## Singularities + [examples] + ## Blow-up: turning singularities into smooth points + [examples] + ## Hironaka's theorem + [statement] # What is Hironaka's polyhedral game + ## Monomial ideals + [definition] & [examples] + ## Rephrasing the local resolution of singularity problem + [definitions] # Variations of Hironaka's game + ## Thom game + ## Singularity theory and Thom polynomial + # Further topics + ... \ No newline at end of file diff --git a/hironaka/abs/PointsTensor.py b/hironaka/abs/PointsTensor.py index b65f142..82fde4d 100644 --- a/hironaka/abs/PointsTensor.py +++ b/hironaka/abs/PointsTensor.py @@ -4,39 +4,44 @@ import torch from hironaka.abs.PointsBase import PointsBase -from hironaka.src import shift_lst, get_newton_polytope_lst, get_shape, scale_points, get_batched_padded_array -from hironaka.src._torch_ops import shift_torch, get_newton_polytope_torch, reposition_torch +from hironaka.src import get_batched_padded_array, rescale_torch +from hironaka.src import shift_torch, get_newton_polytope_torch, reposition_torch class PointsTensor(PointsBase): - config_keys = ['value_threshold', 'device_key', 'padded_value'] + subcls_config_keys = ['value_threshold', 'device_key', 'padding_value'] + copied_attributes = ['distinguished_points'] def __init__(self, points: Union[torch.Tensor, List[List[List[int]]], np.ndarray], value_threshold: Optional[int] = 1e8, device_key: Optional[str] = 'cpu', - padded_value: Optional[float] = -1.0, + padding_value: Optional[float] = -1.0, + distinguished_points: Optional[List[int]] = None, config_kwargs: Optional[Dict[str, Any]] = None, **kwargs): config = kwargs if config_kwargs is None else {**config_kwargs, **kwargs} self.value_threshold = value_threshold - # It's better to require a fixed shape of the tensor implementation. + assert padding_value < 0, f"'padding_value' must be a negative number. Got {padding_value} instead." if isinstance(points, list): points = torch.FloatTensor( get_batched_padded_array(points, new_length=config['max_num_points'], - constant_value=config.get('padded_value', -1))) - elif isinstance(points, (torch.Tensor, np.ndarray)): + constant_value=padding_value)) + elif isinstance(points, np.ndarray): points = torch.FloatTensor(points) + elif isinstance(points, torch.Tensor): + points = points.type(torch.float32) else: raise Exception(f"Input must be a Tensor, a numpy array or a nested list. Got {type(points)}.") self.batch_size, self.max_num_points, self.dimension = points.shape self.device_key = device_key - self.padded_value = padded_value + self.padding_value = padding_value + self.distinguished_points = distinguished_points super().__init__(points, **config) self.device = torch.device(self.device_key) @@ -62,19 +67,19 @@ def _shift(self, coords: List[List[int]], axis: List[int], inplace: Optional[bool] = True): - return shift_torch(points, coords, axis, inplace=inplace) + return shift_torch(points, coords, axis, inplace=inplace, padding_value=self.padding_value) def _get_newton_polytope(self, points: torch.Tensor, inplace: Optional[bool] = True): - return get_newton_polytope_torch(points, inplace=inplace) + return get_newton_polytope_torch(points, inplace=inplace, padding_value=self.padding_value) def _get_shape(self, points: torch.Tensor): return points.shape def _reposition(self, points: torch.Tensor, inplace: Optional[bool] = True): - return reposition_torch(points, inplace=inplace) + return reposition_torch(points, inplace=inplace, padding_value=self.padding_value) def _rescale(self, points: torch.Tensor, inplace: Optional[bool] = True): - return points / torch.reshape(torch.amax(points, (1, 2)), (-1, 1, 1)) + r = rescale_torch(points, inplace=inplace, padding_value=self.padding_value) def _points_copy(self, points: torch.Tensor): return points.clone().detach() @@ -87,4 +92,4 @@ def _get_batch_ended(self, points: torch.Tensor): return num_points.le(1).cpu().detach().tolist() def __repr__(self): - return str(self.points) + return str(self.points) \ No newline at end of file diff --git a/hironaka/agent.py b/hironaka/agent.py index 6cddf1d..ef27e77 100644 --- a/hironaka/agent.py +++ b/hironaka/agent.py @@ -11,12 +11,14 @@ class Agent(abc.ABC): """ @abc.abstractmethod - def move(self, points: Points, coords, inplace=True): + def move(self, points: Points, coords, weights, inplace=True): pass class RandomAgent(Agent): - def move(self, points: Points, coords, inplace=True): + def move(self, points: Points, coords, weights=None, inplace=True): + assert weights is None, "Only support agents without weights." + actions = [np.random.choice(coord, size=1)[0] if len(coord) > 1 else None for coord in coords] if not inplace: return actions @@ -26,10 +28,37 @@ def move(self, points: Points, coords, inplace=True): class ChooseFirstAgent(Agent): - def move(self, points: Points, coords, inplace=True): + def move(self, points: Points, coords, weights=None, inplace=True): + assert weights is None, "Only support agents without weights." + actions = [min(coord) if len(coord) > 1 else None for coord in coords] if not inplace: return actions points.shift(coords, actions) points.get_newton_polytope() return actions + + +class AgentMorin(Agent): + def move(self, points: Points, coords, weights, inplace=True): + assert points.batch_size == 1, "Temporarily only support batch size 1." # TODO: generalize! + weights_unravelled = weights[0] + coords_unravelled = coords[0] + + if weights_unravelled[coords_unravelled[0]] == weights_unravelled[coords_unravelled[1]]: + action = np.random.choice(coords_unravelled, size=1)[0] + else: + action = coords_unravelled[np.argmin( + [weights_unravelled[coords_unravelled[0]], weights_unravelled[coords_unravelled[1]]] + )] + changing_coordinate = [coord for coord in coords_unravelled if coord != action] + next_weights = [weights_unravelled[i] if i not in changing_coordinate else 0 + for i in range(len(weights_unravelled))] + + if inplace: + points.shift([coords_unravelled], [action]) + points.reposition() + points.get_newton_polytope() + weights[:] = next_weights + return [action] + return [action], [next_weights] diff --git a/hironaka/agentThom.py b/hironaka/agentThom.py deleted file mode 100644 index 5d10495..0000000 --- a/hironaka/agentThom.py +++ /dev/null @@ -1,54 +0,0 @@ -import abc - -import numpy as np - -from hironaka.abs import Points -from hironaka.agent import Agent -from hironaka.src import shift_lst, get_newton_polytope_lst - - -class TAgent(abc.ABC): - @abc.abstractmethod - def move(self, points: Points, weights, coords): - pass - - -class AgentThom(TAgent): - def move(self, points: Points, weights, coords): - assert points.batch_size == 1 # Temporarily only support batch size 1. TODO: generalize! - weights = weights[0] - coords = coords[0] - - if weights[coords[0]] == weights[coords[1]]: - action = np.random.choice(coords, size=1)[0] - else: - action = coords[np.argmin([weights[coords[0]], weights[coords[1]]])] - changing_coordinate = [coord for coord in coords if coord != action] - next_weights = [weights[i] if i not in changing_coordinate else 0 for i in range(len(weights))] - points.shift([coords], [action]) - points.reposition() - points.get_newton_polytope() - return points, action, [next_weights] - - -class AgentMorin(TAgent): - def move(self, points, weights, coords): - assert points.batch_size == 1 # Temporarily only support batch size 1. TODO: generalize! - weights = weights[0] - coords = coords[0] - - if weights[coords[0]] == weights[coords[1]]: - action = np.random.choice(coords, size=1)[0] - else: - action = coords[np.argmin([weights[coords[0]], weights[coords[1]]])] - changing_coordinate = [coord for coord in coords if coord != action] - next_weights = [weights[i] if i not in changing_coordinate else 0 for i in range(len(weights))] - points.shift([coords], [action]) - points.reposition() - points.get_newton_polytope() - - ## NEED WORK - if points.distinguished_points[0] is not None: - return points, action, [next_weights] - else: - return False \ No newline at end of file diff --git a/hironaka/envs/HironakaBase.py b/hironaka/envs/HironakaBase.py index b29ddf2..2d97b53 100644 --- a/hironaka/envs/HironakaBase.py +++ b/hironaka/envs/HironakaBase.py @@ -66,7 +66,8 @@ def __init__(self, shape=(self.max_number_points, self.dimension), dtype=np.float32) else: - self.point_observation_space = spaces.Box(low=-1.0, high=1.0, shape=(self.max_number_points, self.dimension), + self.point_observation_space = spaces.Box(low=-1.0, high=1.0, + shape=(self.max_number_points, self.dimension), dtype=np.float32) # Configs to pass down to other functions diff --git a/hironaka/game.py b/hironaka/game.py index fc93dd3..be983da 100644 --- a/hironaka/game.py +++ b/hironaka/game.py @@ -1,6 +1,10 @@ import abc import logging -from typing import Optional +from typing import Optional, Union + +from hironaka.abs import Points +from hironaka.agent import Agent +from hironaka.host import Host class Game(abc.ABC): @@ -46,8 +50,107 @@ def step(self, verbose: int = 0) -> bool: """ pass + def _show(self, coords, action, weights, ended): + self.logger.info(f"Host move: {coords}") + self.logger.info(f"Agent move: {action}") + if weights is not None: + self.logger.info(f"Weights: {weights}") + self.logger.info(f"Game Ended: {ended}") + def print_history(self): self.logger.info("Coordinate history (host choices):") self.logger.info(self.coord_history) self.logger.info("Move history (agent choices):") self.logger.info(self.move_history) + + +class GameHironaka(Game): + def __init__(self, + state: Union[Points, None], + host: Host, + agent: Agent, + scale_observation: Optional[bool] = True, + **kwargs): + if self.logger is None: + self.logger = logging.getLogger(__class__.__name__) + + super().__init__(state, host, agent, **kwargs) + self.scale_observation = scale_observation + self.stopped = False + + def step(self, verbose: int = 0) -> bool: + """ + Make one move forward. + + In particular, + 1. the host selects coordinates + (use: Host.selectCoord(state)); + 2. the agent makes one move according to the selected coordinates + (use: Agent.move(state, coords)). + + Return: True if successful, False if unsuccessful + """ + if self.stopped: + return False + + if verbose: + self.logger.info(self.state) + + coords = self.host.select_coord(self.state) + action = self.agent.move(self.state, coords) + if self.scale_observation: + self.state.rescale() + + if verbose: + self._show(coords, action, None, self.state.ended) + + self.coord_history.append(coords) + self.move_history.append(action) + + if self.state.ended: + self.stopped = True + return False + return True + + +class GameMorin(Game): + """The agent is Thom (picks the action coordinate with smallest weight), but the game terminates with a + label 'NO CONTRIBUTION' if the distinguished Porteous point is not a vertex of the Newton polytope + after the shift""" + + def __init__(self, + state: Union[Points, None], + host: Host, + agent: Agent, + scale_observation: Optional[bool] = True, + **kwargs): + if self.logger is None: + self.logger = logging.getLogger(__class__.__name__) + + super().__init__(state, host, agent, **kwargs) + self.weights = [[1] * self.state.dimension for _ in range(self.state.batch_size)] + self.scale_observation = scale_observation + self.stopped = False + + def step(self, verbose: int = 0) -> bool: + if self.stopped: + return False + + if verbose: + self.logger.info(self.state) + + coords = self.host.select_coord(self.state) + action = self.agent.move(self.state, self.weights, coords, inplace=True) + + if verbose: + self._show(coords, action, None, self.state.ended) + + self.coord_history.append(coords) + self.move_history.append(action) + + if self.state.ended or self.state.distinguished_points[0] is None: + self.stopped = True + if self.state.distinguished_points[0] is None: + self.logger.info("No contribution.") + return False + return True diff --git a/hironaka/gameHironaka.py b/hironaka/gameHironaka.py deleted file mode 100644 index 9c77cdd..0000000 --- a/hironaka/gameHironaka.py +++ /dev/null @@ -1,60 +0,0 @@ -import logging -from typing import Optional, Union - -from .abs import Points -from .agent import Agent -from .game import Game -from .host import Host - - -class GameHironaka(Game): - def __init__(self, - state: Union[Points, None], - host: Host, - agent: Agent, - scale_observation: Optional[bool] = True, - **kwargs): - if self.logger is None: - self.logger = logging.getLogger(__class__.__name__) - - super().__init__(state, host, agent, **kwargs) - self.scale_observation = scale_observation - self.stopped = False - - def step(self, verbose: int = 0) -> bool: - """ - Make one move forward. - - In particular, - 1. the host selects coordinates - (use: Host.selectCoord(state)); - 2. the agent makes one move according to the selected coordinates - (use: Agent.move(state, coords)). - - Return: True if successful, False if unsuccessful - """ - if self.stopped: - return False - - if verbose: - self.logger.info(self.state) - - coords = self.host.select_coord(self.state) - action = self.agent.move(self.state, coords) - if self.scale_observation: - self.state.rescale() - - if verbose: - self.logger.info(f"Host move: {coords}") - self.logger.info(f"Agent move: {action}") - self.logger.info(f"Game Ended: {self.state.ended}") - - self.coord_history.append(coords) - self.move_history.append(action) - - if self.state.ended: - self.stopped = True - return False - return True - - diff --git a/hironaka/gameThom.py b/hironaka/gameThom.py deleted file mode 100644 index 0395386..0000000 --- a/hironaka/gameThom.py +++ /dev/null @@ -1,66 +0,0 @@ -from typing import List, Tuple - -import numpy as np - -from hironaka.abs import Points -from hironaka.agent import Agent -from hironaka.agentThom import TAgent -from hironaka.host import Host - - -class GameThom: # QUESTION: Usage??? - def __init__(self, points: Points, host: Host, agent: TAgent): - self.state = points - self.host = host - self.agent = agent - self.coord_history = [] - self.move_history = [] - self.weights = [[1] * points.dimension for _ in range(self.state.batch_size)] - self.stopped = False - - def step(self): - if self.stopped: - return - coords = self.host.select_coord(self.state) - new_state, action, new_weights = self.agent.move(self.state, self.weights, coords) - - self.state = new_state - self.weights = new_weights - self.coord_history.append(coords) - self.move_history.append(action) - - if len(self.state) == 1: - self.stopped = True - - -class GameMorin: - - """The agent is Thom (picks the action coordinate with smallest weight), but the game terminates with a - label 'NO CONTRIBUTION' if the distinguished Porteous point is not a vertex of the Newton polytope - after the shift""" - - def __init__(self, points: Points, host: Host, agent: TAgent): - self.state = points - self.host = host - self.agent = agent - self.coord_history = [] - self.move_history = [] - self.weights = [[1] * points.dimension for _ in range(self.state.batch_size)] - self.stopped = False - - def step(self): - if self.stopped: - return - coords = self.host.select_coord(self.state) - new_data = self.agent.move(self.state, self.weights, coords) - if not new_data: - print('No contribution') - self.stopped = True - else: - newState, action, new_weights = new_data - self.state = newState - self.weights = new_weights - self.coord_history.append(coords) - self.move_history.append(action) - if self.state.ended: - self.stopped = True diff --git a/hironaka/host.py b/hironaka/host.py index 10a71fa..757150b 100644 --- a/hironaka/host.py +++ b/hironaka/host.py @@ -62,6 +62,7 @@ def select_coord(self, points: Points, debug=False): return result + class ZeillingerLex(Host): # noinspection PyPep8Naming @staticmethod @@ -109,8 +110,9 @@ def select_coord(self, points: Points, debug=False): result.append(coords[0]) return result + class WeakSpivakovsky(Host): - def select_coord(self, points:Points, debug=False): + def select_coord(self, points: Points, debug=False): assert points dim = points.dimension for b in range(points.batch_size): @@ -123,7 +125,7 @@ def select_coord(self, points:Points, debug=False): "Find a minimal hitting set, brute-force" U = set.union(*subsets) result = [] - for i in range(1, len(U)+1): + for i in range(1, len(U) + 1): combs = combinations(U, i) for c in combs: if all(set(c) & l for l in subsets): @@ -131,4 +133,3 @@ def select_coord(self, points:Points, debug=False): if result: break return result - diff --git a/hironaka/policy/NNPolicy.py b/hironaka/policy/NNPolicy.py index 86d2738..060dacc 100644 --- a/hironaka/policy/NNPolicy.py +++ b/hironaka/policy/NNPolicy.py @@ -5,7 +5,7 @@ import torch from hironaka.policy.Policy import Policy -from hironaka.src import batched_coord_list_to_binary, decode_action, encode_action, mask_encoded_action +from hironaka.src import batched_coord_list_to_binary, decode_action, mask_encoded_action class NNPolicy(Policy): diff --git a/hironaka/policy/__init__.py b/hironaka/policy/__init__.py index ee558f7..f30d3cd 100644 --- a/hironaka/policy/__init__.py +++ b/hironaka/policy/__init__.py @@ -1,2 +1,2 @@ -from .Policy import Policy from .NNPolicy import NNPolicy +from .Policy import Policy diff --git a/hironaka/policy_players/PolicyAgent.py b/hironaka/policy_players/PolicyAgent.py index 0b42a4b..5914042 100644 --- a/hironaka/policy_players/PolicyAgent.py +++ b/hironaka/policy_players/PolicyAgent.py @@ -10,7 +10,8 @@ def __init__(self, policy: Policy): self._policy = policy def move(self, points: Points, coords: List[List[int]], inplace=True): - assert len(coords) == points.batch_size # TODO: wrap the move method for the abstract "Agent" with sanity checks? + assert len( + coords) == points.batch_size # TODO: wrap the move method for the abstract "Agent" with sanity checks? features = points.get_features() diff --git a/hironaka/policy_players/__init__.py b/hironaka/policy_players/__init__.py index d9a06dc..c7f96cd 100644 --- a/hironaka/policy_players/__init__.py +++ b/hironaka/policy_players/__init__.py @@ -1,2 +1,2 @@ -from .PolicyHost import PolicyHost from .PolicyAgent import PolicyAgent +from .PolicyHost import PolicyHost diff --git a/hironaka/src/__init__.py b/hironaka/src/__init__.py index dd730ee..8dc6d13 100644 --- a/hironaka/src/__init__.py +++ b/hironaka/src/__init__.py @@ -1,3 +1,5 @@ from ._list_ops import * from ._np_ops import * from ._snippets import * +from ._thom_snippets import * +from ._torch_ops import * \ No newline at end of file diff --git a/hironaka/src/_list_ops.py b/hironaka/src/_list_ops.py index eb3cd61..d85a1d1 100644 --- a/hironaka/src/_list_ops.py +++ b/hironaka/src/_list_ops.py @@ -1,9 +1,9 @@ from typing import List import numpy as np +from scipy.spatial import ConvexHull from ._snippets import get_shape -from scipy.spatial import ConvexHull def get_newton_polytope_approx_lst(points: List[List[List[float]]], inplace=True, get_ended=False): @@ -81,7 +81,7 @@ def get_newton_polytope_lst(points: List[List[List[float]]], inplace=True): points[:, :, :] = result else: return result - #return get_newton_polytope_approx_lst(points, inplace=inplace, get_ended=get_ended) + # return get_newton_polytope_approx_lst(points, inplace=inplace, get_ended=get_ended) def shift_lst(points: List[List[List[float]]], coords: List[List[int]], axis: List[int], inplace=True): diff --git a/hironaka/src/_snippets.py b/hironaka/src/_snippets.py index d1df7d3..c39a8ee 100644 --- a/hironaka/src/_snippets.py +++ b/hironaka/src/_snippets.py @@ -1,7 +1,7 @@ +import numbers from typing import List, Union import numpy as np -import numbers def get_shape(o): @@ -131,9 +131,9 @@ def scale_points(points: List[List[List[int]]], inplace=True): for point in points[b]: if inplace: - point[:] = [x/m for x in point] + point[:] = [x / m for x in point] else: - new_points[b].append([x/m for x in point]) + new_points[b].append([x / m for x in point]) if not inplace: return new_points @@ -160,7 +160,7 @@ def decode_action(code: int, dimension: int): def mask_encoded_action(dimension: int): assert isinstance(dimension, int), f"Got {type(dimension)}." - result = np.ones(2**dimension) + result = np.ones(2 ** dimension) result[0] = 0 for i in range(dimension): result[1 << i] = 0 diff --git a/hironaka/util/geomThom.py b/hironaka/src/_thom_snippets.py similarity index 85% rename from hironaka/util/geomThom.py rename to hironaka/src/_thom_snippets.py index 533d0f4..0ece645 100644 --- a/hironaka/util/geomThom.py +++ b/hironaka/src/_thom_snippets.py @@ -2,7 +2,7 @@ import numpy as np import sympy as sp -from sympy import MatrixSymbol, Matrix, Poly +from sympy import Matrix, MatrixSymbol, Poly def quadratic_part(order_of_jets: int): @@ -19,9 +19,9 @@ def quadratic_part(order_of_jets: int): blower = Matrix(blower) for s in range(order_of_jets - 1): V = Matrix(np.zeros((order_of_jets, order_of_jets))) - for t in range(s+1): - V = V + Matrix((blower.row(t).transpose()*blower.row(s-t))) - W.append(Matrix(np.triu(V+V.transpose()-Matrix(np.diag(np.array(V.diagonal())[0]))))) + for t in range(s + 1): + V = V + Matrix((blower.row(t).transpose() * blower.row(s - t))) + W.append(Matrix(np.triu(V + V.transpose() - Matrix(np.diag(np.array(V.diagonal())[0]))))) return [blower, W] @@ -33,8 +33,9 @@ def quadratic_fixed_points(order_of_jets: int): P = [] for s in range(2, order_of_jets + 1): for lin_set in combinations(list(range(order_of_jets)), s): - P = list(list(L) for L in list(combinations_with_replacement(list(range(order_of_jets)), 2)) if L[0] + L[1] < order_of_jets - 1) - if len(P) >= order_of_jets-s: + P = list(list(L) for L in list(combinations_with_replacement(list(range(order_of_jets)), 2)) if + L[0] + L[1] < order_of_jets - 1) + if len(P) >= order_of_jets - s: for comb in list(combinations(list(P), order_of_jets - s)): Q.append([list(lin_set), list(comb)]) return Q @@ -69,7 +70,7 @@ def thom_points(order_of_jets: int): else: P = Q[0] # QUESTION: Really starts at 1????? for i in range(0, len(Q)): - P = P+Q[i] + P = P + Q[i] return Poly(P).monoms() @@ -82,6 +83,6 @@ def thom_points_homogeneous(order_of_jets: int): max_deg = np.amax([np.sum(TP[i][1:]) for i in range(len(TP))]) for point in TP: pt = np.array(point) - pt[0] = max_deg-np.sum(point[1:]) + pt[0] = max_deg - np.sum(point[1:]) P.append(pt.tolist()) return P diff --git a/hironaka/util/search.py b/hironaka/util/search.py index b8605d3..4c40de4 100644 --- a/hironaka/util/search.py +++ b/hironaka/util/search.py @@ -1,4 +1,6 @@ -from collections import deque +from collections import deque, namedtuple + +import numpy as np from hironaka.abs import Points from hironaka.host import Host @@ -50,3 +52,43 @@ def search_tree(points, tree, curr_node, host, max_size=100): tree.create_node(node_id, node_id, parent=curr_node, data=shifts[-1]) search_tree(shifts[-1], tree, node_id, host, max_size=max_size) return tree + + +def search_tree_morin(points: Points, tree, curr_node, curr_weights, host, max_size=100): + """ + Perform a full tree search and store the full result in a Tree object. + """ + Node = namedtuple('Node', ['points']) + + if isinstance(curr_weights, np.ndarray): + curr_weights = curr_weights.tolist() + + if points.ended or tree.size() > max_size: + if tree.size() > max_size: + node_id = tree.size() + tree.create_node(node_id, node_id, parent=curr_node, data=Node('...more...')) + return tree + coords = host.select_coord(points)[0] + + for action in coords: + if curr_weights[action] > min([curr_weights[i] for i in coords]): + continue + + changing_coordinate = [coord for coord in coords if coord != action] + next_weights = [curr_weights[i] if i not in changing_coordinate else curr_weights[i] - curr_weights[action] for + i in range(len(curr_weights))] + + new_points = points.shift([coords], [action], inplace=False) + + new_points.reposition() + new_points.get_newton_polytope() + + if new_points.distinguished_points[0] is None: + node_id = tree.size() + tree.create_node(node_id, node_id, parent=curr_node, data=Node('No contribution')) + continue + node_id = tree.size() + tree.create_node(node_id, node_id, parent=curr_node, + data=Node(str(new_points) + f", {new_points.distinguished_points}")) + search_tree_morin(new_points, tree, node_id, next_weights, host, max_size) + return tree diff --git a/hironaka/util/searchThom.py b/hironaka/util/searchThom.py deleted file mode 100644 index 433bc20..0000000 --- a/hironaka/util/searchThom.py +++ /dev/null @@ -1,46 +0,0 @@ -from collections import namedtuple -from dataclasses import dataclass -from typing import List, Tuple - -import numpy as np - -from hironaka.abs import Points -from hironaka.src import shift_lst, get_newton_polytope_lst, reposition_lst - - -def search_tree_morin(points: Points, tree, curr_node, curr_weights, host, max_size=100): - """ - Perform a full tree search and store the full result in a Tree object. - """ - NoCont = namedtuple('NoCont', ['points']) - - if isinstance(curr_weights, np.ndarray): - curr_weights = curr_weights.tolist() - - if points.ended or tree.size() > max_size: - if tree.size() > max_size: - node_id = tree.size() - tree.create_node(node_id, node_id, parent=curr_node, data=NoCont('...more...')) - return tree - coords = host.select_coord(points)[0] - - for action in coords: - if curr_weights[action] > min([curr_weights[i] for i in coords]): - continue - - changing_coordinate = [coord for coord in coords if coord != action] - next_weights = [curr_weights[i] if i not in changing_coordinate else curr_weights[i]-curr_weights[action] for i in range(len(curr_weights))] - - new_points = points.shift([coords], [action], inplace=False) - - new_points.reposition() - new_points.get_newton_polytope() - - if new_points.distinguished_points[0] is None: - node_id = tree.size() - tree.create_node(node_id, node_id, parent=curr_node, data=NoCont('No contribution')) - continue - node_id = tree.size() - tree.create_node(node_id, node_id, parent=curr_node, data=NoCont(str(new_points) + f", {new_points.distinguished_points}")) - search_tree_morin(new_points, tree, node_id, next_weights, host, max_size) - return tree diff --git a/hironaka/validator/HironakaValidator.py b/hironaka/validator/HironakaValidator.py index ef7e7f7..14a122f 100644 --- a/hironaka/validator/HironakaValidator.py +++ b/hironaka/validator/HironakaValidator.py @@ -1,8 +1,8 @@ -from typing import Optional, Dict, Any import logging +from typing import Optional, Dict, Any from hironaka.abs import Points -from hironaka.gameHironaka import GameHironaka +from hironaka.game import GameHironaka from hironaka.util import generate_batch_points diff --git a/hironaka/validator/__init__.py b/hironaka/validator/__init__.py index c22110f..00579ed 100644 --- a/hironaka/validator/__init__.py +++ b/hironaka/validator/__init__.py @@ -1 +1 @@ -from .HironakaValidator import HironakaValidator \ No newline at end of file +from .HironakaValidator import HironakaValidator diff --git a/test/testGame.py b/test/testGame.py index ddb44f8..1ae126b 100644 --- a/test/testGame.py +++ b/test/testGame.py @@ -1,7 +1,7 @@ import unittest from hironaka.agent import RandomAgent -from hironaka.gameHironaka import GameHironaka +from hironaka.game import GameHironaka from hironaka.host import Zeillinger from hironaka.util import * diff --git a/test/testPoints.py b/test/testPoints.py index ed73207..76be03f 100644 --- a/test/testPoints.py +++ b/test/testPoints.py @@ -124,7 +124,7 @@ def test_scale(self): # print(p2) def test_value_threshold(self): - points = Points([[[5e7, 5e7+1, 1e7], [1, 1, 2]]], value_threshold=int(1e8)) + points = Points([[[5e7, 5e7 + 1, 1e7], [1, 1, 2]]], value_threshold=int(1e8)) assert not points.exceed_threshold() points.shift([[0, 1]], [0]) assert points.exceed_threshold() diff --git a/test/testPolicy.py b/test/testPolicy.py index 7e9ecd3..4b537f0 100644 --- a/test/testPolicy.py +++ b/test/testPolicy.py @@ -3,7 +3,7 @@ from torch import nn from hironaka.abs import Points -from hironaka.gameHironaka import GameHironaka +from hironaka.game import GameHironaka from hironaka.policy.NNPolicy import NNPolicy from hironaka.policy_players.PolicyAgent import PolicyAgent from hironaka.policy_players.PolicyHost import PolicyHost diff --git a/test/testThom.py b/test/testThom.py index af68aef..c361128 100644 --- a/test/testThom.py +++ b/test/testThom.py @@ -1,14 +1,13 @@ import unittest -import numpy as np from treelib import Tree from hironaka.abs import Points -from hironaka.agentThom import AgentMorin -from hironaka.gameThom import GameMorin -from hironaka.host import Zeillinger, ZeillingerLex, WeakSpivakovsky -from hironaka.util.geomThom import thom_points, thom_points_homogeneous, thom_monomial_ideal -from hironaka.util.searchThom import search_tree_morin +from hironaka.agent import AgentMorin +from hironaka.game import GameMorin +from hironaka.host import ZeillingerLex, WeakSpivakovsky +from hironaka.src._thom_snippets import thom_monomial_ideal, thom_points, thom_points_homogeneous +from hironaka.util import search_tree_morin class TestThom(unittest.TestCase): @@ -19,9 +18,22 @@ def test_game(self): points = thom_points_homogeneous(N) opoints = thom_points(N) game = GameMorin(Points([points], distinguished_points=[len(points) - 1]), host, agent) - print('Thom monomial ideal:', thom_monomial_ideal(N)) - print('Original Points:', opoints) - print('Homogeneous Points:', points) + + r1 = "[2*b[0, 0]**4*b[1, 1]*b[1, 3] - 2*b[0, 0]**4*b[1, 2]**2, -2*b[0, 0]**4*b[1, 2]*b[2, 2], -b[0, 0]**3*b[1, 1]**2*b[1, 2], 4*b[0, 0]**3*b[1, 1]**2*b[2, 2], 2*b[0, 0]**2*b[1, 1]**4, 0, 2*b[0, 0]**4*b[1, 1]*b[2, 3] - 2*b[0, 0]**4*b[1, 2]*b[2, 2], -2*b[0, 0]**4*b[2, 2]**2, -b[0, 0]**3*b[1, 1]**2*b[2, 2], 0, 0, 0, 2*b[0, 0]**4*b[1, 1]*b[3, 3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b[0, 0]**3*b[1, 2]*b[2, 3] - b[0, 0]**3*b[1, 3]*b[2, 2], -2*b[0, 0]**2*b[1, 1]**2*b[2, 3] + 2*b[0, 0]**2*b[1, 1]*b[1, 2]*b[2, 2], 2*b[0, 0]**2*b[1, 1]*b[2, 2]**2, b[0, 0]*b[1, 1]**3*b[2, 2], b[0, 0]**3*b[1, 2]*b[3, 3], -2*b[0, 0]**2*b[1, 1]**2*b[3, 3], 0, 0, b[0, 0]**3*b[2, 2]*b[3, 3], 0, 0, 0, 0, 0, 0, 0, b[0, 0]*b[1, 1]*b[2, 2]*b[3, 3]]" + ro = [(4, 1, 0, 1, 0, 0, 0), (4, 1, 0, 0, 0, 1, 0), (4, 1, 0, 0, 0, 0, 1), (4, 0, 2, 0, 0, 0, 0), + (4, 0, 1, 0, 1, 0, 0), (4, 0, 0, 0, 2, 0, 0), (3, 2, 1, 0, 0, 0, 0), (3, 2, 0, 0, 1, 0, 0), + (3, 0, 1, 0, 0, 1, 0), (3, 0, 1, 0, 0, 0, 1), (3, 0, 0, 1, 1, 0, 0), (3, 0, 0, 0, 1, 0, 1), + (2, 4, 0, 0, 0, 0, 0), (2, 2, 0, 0, 0, 1, 0), (2, 2, 0, 0, 0, 0, 1), (2, 1, 1, 0, 1, 0, 0), + (2, 1, 0, 0, 2, 0, 0), (1, 3, 0, 0, 1, 0, 0), (1, 1, 0, 0, 1, 0, 1)] + rhom = [[2, 1, 0, 1, 0, 0, 0], [2, 1, 0, 0, 0, 1, 0], [2, 1, 0, 0, 0, 0, 1], [2, 0, 2, 0, 0, 0, 0], + [2, 0, 1, 0, 1, 0, 0], [2, 0, 0, 0, 2, 0, 0], [1, 2, 1, 0, 0, 0, 0], [1, 2, 0, 0, 1, 0, 0], + [2, 0, 1, 0, 0, 1, 0], [2, 0, 1, 0, 0, 0, 1], [2, 0, 0, 1, 1, 0, 0], [2, 0, 0, 0, 1, 0, 1], + [0, 4, 0, 0, 0, 0, 0], [1, 2, 0, 0, 0, 1, 0], [1, 2, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 0, 0], + [1, 1, 0, 0, 2, 0, 0], [0, 3, 0, 0, 1, 0, 0], [1, 1, 0, 0, 1, 0, 1]] + assert str(thom_monomial_ideal(4)) == r1 + assert str(opoints) == str(ro) + assert str(rhom) == str(points) + for i in range(100): print('Game state:', game.state) game.step() @@ -65,5 +77,3 @@ def test_thom_points(self): thom_points_homogeneous_4 = \ "[[2, 1, 0, 1, 0, 0, 0], [2, 1, 0, 0, 0, 1, 0], [2, 1, 0, 0, 0, 0, 1], [2, 0, 2, 0, 0, 0, 0], [2, 0, 1, 0, 1, 0, 0], [2, 0, 0, 0, 2, 0, 0], [1, 2, 1, 0, 0, 0, 0], [1, 2, 0, 0, 1, 0, 0], [2, 0, 1, 0, 0, 1, 0], [2, 0, 1, 0, 0, 0, 1], [2, 0, 0, 1, 1, 0, 0], [2, 0, 0, 0, 1, 0, 1], [0, 4, 0, 0, 0, 0, 0], [1, 2, 0, 0, 0, 1, 0], [1, 2, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 0, 0], [1, 1, 0, 0, 2, 0, 0], [0, 3, 0, 0, 1, 0, 0], [1, 1, 0, 0, 1, 0, 1]]" assert str(thom_points_homogeneous(4)) == thom_points_homogeneous_4 - - diff --git a/test/testTorchPoints.py b/test/testTorchPoints.py index 93cf629..869f6b6 100644 --- a/test/testTorchPoints.py +++ b/test/testTorchPoints.py @@ -63,7 +63,6 @@ def test_pointstensor(self): ] ) - pts = PointsTensor(p) pts.get_newton_polytope() diff --git a/test/testUtil.py b/test/testUtil.py index 6023063..31ebb8c 100644 --- a/test/testUtil.py +++ b/test/testUtil.py @@ -73,4 +73,4 @@ def test_true_newton_polytope(self): def test_get_shape_extra_character(self): p = [[1, 2, 3, 'd'], [2, 3, 4]] - assert get_shape(p) == (2,3) \ No newline at end of file + assert get_shape(p) == (2, 3) From a01dcb3e56b9eb8e49ecbdddc3372fb49ec800be Mon Sep 17 00:00:00 2001 From: Void Date: Sat, 16 Jul 2022 20:02:42 -0400 Subject: [PATCH 06/16] Major fix. --- hironaka/src/_torch_ops.py | 45 +++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/hironaka/src/_torch_ops.py b/hironaka/src/_torch_ops.py index 472d8d8..5a3d798 100644 --- a/hironaka/src/_torch_ops.py +++ b/hironaka/src/_torch_ops.py @@ -5,7 +5,9 @@ from hironaka.src import batched_coord_list_to_binary -def get_newton_polytope_approx_torch(points: torch.Tensor, inplace: Optional[bool] = True): +def get_newton_polytope_approx_torch(points: torch.Tensor, + inplace: Optional[bool] = True, + padding_value: Optional[float] = -1.): assert len(points.shape) == 3 batch_size, max_num_points, dimension = points.shape @@ -27,21 +29,26 @@ def get_newton_polytope_approx_torch(points: torch.Tensor, inplace: Optional[boo points_to_remove = ((difference >= 0) & diag_filter & filter_matrix).all(3).any(2) points_to_remove = points_to_remove.unsqueeze(2).repeat(1, 1, dimension) + r = points * ~points_to_remove + torch.full(points.shape, padding_value) * points_to_remove + if inplace: - points[:, :, :] = points * ~points_to_remove + torch.full(points.shape, -1.0) * points_to_remove + points[:, :, :] = r return None else: - return points * ~points_to_remove + torch.full(points.shape, -1.0) * points_to_remove + return r -def get_newton_polytope_torch(points: torch.Tensor, inplace: Optional[bool] = True): - return get_newton_polytope_approx_torch(points, inplace=inplace) +def get_newton_polytope_torch(points: torch.Tensor, + inplace: Optional[bool] = True, + padding_value: Optional[float] = -1.): + return get_newton_polytope_approx_torch(points, inplace=inplace, padding_value=padding_value) def shift_torch(points: torch.Tensor, coord: Union[torch.Tensor, List[List[int]]], axis: Union[torch.Tensor, List[int]], - inplace=True): + inplace: Optional[bool] = True, + padding_value: Optional[float] = -1.): """ note: If coord is a list, it is assumed to be lists of chosen coordinates. @@ -85,25 +92,37 @@ def shift_torch(points: torch.Tensor, trans_matrix = trans_matrix.unsqueeze(1).repeat(1, max_num_points, 1, 1) transformed_points = torch.matmul(trans_matrix, points.unsqueeze(3)).squeeze(3) - result = (transformed_points * available_points) + torch.full(points.shape, -1.0) * ~available_points + r = (transformed_points * available_points) + torch.full(points.shape, padding_value) * ~available_points if inplace: - points[:, :, :] = result + points[:, :, :] = r return None else: - return result + return r -def reposition_torch(points: torch.Tensor, inplace: Optional[bool] = True): +def reposition_torch(points: torch.Tensor, + inplace: Optional[bool] = True, + padding_value: Optional[float] = -1.): available_points = points.ge(0) maximum = torch.max(points) preprocessed = points * available_points + torch.full(points.shape, maximum + 1) * ~available_points coordinate_minimum = torch.amin(preprocessed, 1) unfiltered_result = points - coordinate_minimum.unsqueeze(1).repeat(1, points.shape[1], 1) - result = unfiltered_result * available_points + torch.full(points.shape, -1.0) * ~available_points + r = unfiltered_result * available_points + torch.full(points.shape, padding_value) * ~available_points if inplace: - points[:, :, :] = result + points[:, :, :] = r return None else: - return result + return r + + +def rescale_torch(points: torch.Tensor, inplace: Optional[bool] = True, padding_value: Optional[float] = -1.): + available_points = points.ge(0) + r = points * available_points / torch.reshape(torch.amax(points, (1, 2)), (-1, 1, 1)) + \ + padding_value * ~available_points + if inplace: + points[:, :, :] = r + else: + return r \ No newline at end of file From 4621aed181241fc5831e1728520a4550fe591e08 Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Sun, 17 Jul 2022 11:02:24 +0200 Subject: [PATCH 07/16] Update host.py Fix --- hironaka/host.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hironaka/host.py b/hironaka/host.py index 757150b..37cbde3 100644 --- a/hironaka/host.py +++ b/hironaka/host.py @@ -114,7 +114,7 @@ def select_coord(self, points: Points, debug=False): class WeakSpivakovsky(Host): def select_coord(self, points: Points, debug=False): assert points - dim = points.dimension + result = [] for b in range(points.batch_size): pts = points.get_batch(b) if len(pts) <= 1: From 7aa7259f0a9f094ac6c53e53480386545cbcaa85 Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Sun, 17 Jul 2022 11:03:08 +0200 Subject: [PATCH 08/16] Update host.py Small fix --- hironaka/host.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hironaka/host.py b/hironaka/host.py index 37cbde3..71d0074 100644 --- a/hironaka/host.py +++ b/hironaka/host.py @@ -113,7 +113,7 @@ def select_coord(self, points: Points, debug=False): class WeakSpivakovsky(Host): def select_coord(self, points: Points, debug=False): - assert points + assert not points.ended result = [] for b in range(points.batch_size): pts = points.get_batch(b) From f3c1b77818b8d8a68e008b9e977b79c9617e9273 Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Sun, 17 Jul 2022 12:58:24 +0200 Subject: [PATCH 09/16] thom_monimial_ideal cannot be checked against a string due to unstable output --- hironaka/src/__init__.py | 1 - test/testThom.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/hironaka/src/__init__.py b/hironaka/src/__init__.py index 8dc6d13..6021aee 100644 --- a/hironaka/src/__init__.py +++ b/hironaka/src/__init__.py @@ -1,5 +1,4 @@ from ._list_ops import * -from ._np_ops import * from ._snippets import * from ._thom_snippets import * from ._torch_ops import * \ No newline at end of file diff --git a/test/testThom.py b/test/testThom.py index c361128..3ba044b 100644 --- a/test/testThom.py +++ b/test/testThom.py @@ -19,7 +19,6 @@ def test_game(self): opoints = thom_points(N) game = GameMorin(Points([points], distinguished_points=[len(points) - 1]), host, agent) - r1 = "[2*b[0, 0]**4*b[1, 1]*b[1, 3] - 2*b[0, 0]**4*b[1, 2]**2, -2*b[0, 0]**4*b[1, 2]*b[2, 2], -b[0, 0]**3*b[1, 1]**2*b[1, 2], 4*b[0, 0]**3*b[1, 1]**2*b[2, 2], 2*b[0, 0]**2*b[1, 1]**4, 0, 2*b[0, 0]**4*b[1, 1]*b[2, 3] - 2*b[0, 0]**4*b[1, 2]*b[2, 2], -2*b[0, 0]**4*b[2, 2]**2, -b[0, 0]**3*b[1, 1]**2*b[2, 2], 0, 0, 0, 2*b[0, 0]**4*b[1, 1]*b[3, 3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, b[0, 0]**3*b[1, 2]*b[2, 3] - b[0, 0]**3*b[1, 3]*b[2, 2], -2*b[0, 0]**2*b[1, 1]**2*b[2, 3] + 2*b[0, 0]**2*b[1, 1]*b[1, 2]*b[2, 2], 2*b[0, 0]**2*b[1, 1]*b[2, 2]**2, b[0, 0]*b[1, 1]**3*b[2, 2], b[0, 0]**3*b[1, 2]*b[3, 3], -2*b[0, 0]**2*b[1, 1]**2*b[3, 3], 0, 0, b[0, 0]**3*b[2, 2]*b[3, 3], 0, 0, 0, 0, 0, 0, 0, b[0, 0]*b[1, 1]*b[2, 2]*b[3, 3]]" ro = [(4, 1, 0, 1, 0, 0, 0), (4, 1, 0, 0, 0, 1, 0), (4, 1, 0, 0, 0, 0, 1), (4, 0, 2, 0, 0, 0, 0), (4, 0, 1, 0, 1, 0, 0), (4, 0, 0, 0, 2, 0, 0), (3, 2, 1, 0, 0, 0, 0), (3, 2, 0, 0, 1, 0, 0), (3, 0, 1, 0, 0, 1, 0), (3, 0, 1, 0, 0, 0, 1), (3, 0, 0, 1, 1, 0, 0), (3, 0, 0, 0, 1, 0, 1), @@ -30,7 +29,6 @@ def test_game(self): [2, 0, 1, 0, 0, 1, 0], [2, 0, 1, 0, 0, 0, 1], [2, 0, 0, 1, 1, 0, 0], [2, 0, 0, 0, 1, 0, 1], [0, 4, 0, 0, 0, 0, 0], [1, 2, 0, 0, 0, 1, 0], [1, 2, 0, 0, 0, 0, 1], [1, 1, 1, 0, 1, 0, 0], [1, 1, 0, 0, 2, 0, 0], [0, 3, 0, 0, 1, 0, 0], [1, 1, 0, 0, 1, 0, 1]] - assert str(thom_monomial_ideal(4)) == r1 assert str(opoints) == str(ro) assert str(rhom) == str(points) From 1f093a81cc19a30c0086197a981c002af7b19002 Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Sun, 17 Jul 2022 23:33:22 +0200 Subject: [PATCH 10/16] make it closer to the main branch --- hironaka/agent.py | 22 ++++- hironaka/{abs => core}/Points.py | 0 hironaka/{abs => core}/PointsBase.py | 0 hironaka/{abs => core}/PointsNumpy.py | 0 hironaka/{abs => core}/PointsTensor.py | 0 hironaka/{abs => core}/__init__.py | 0 hironaka/game.py | 2 +- .../{envs => gym_env}/HironakaAgentEnv.py | 0 hironaka/{envs => gym_env}/HironakaBase.py | 0 hironaka/{envs => gym_env}/HironakaHostEnv.py | 0 hironaka/{envs => gym_env}/__init__.py | 0 hironaka/host.py | 86 ++++++++----------- hironaka/policy_players/PolicyAgent.py | 24 ------ hironaka/policy_players/PolicyHost.py | 25 ------ hironaka/src/_snippets.py | 7 ++ hironaka/util/__init__.py | 1 - hironaka/util/geom.py | 9 -- hironaka/util/search.py | 2 +- hironaka/validator/HironakaValidator.py | 4 +- test/testGame.py | 2 +- test/testGymEnv.py | 2 +- test/testPoints.py | 2 +- test/testPolicy.py | 8 +- test/testSearch.py | 2 +- test/testThom.py | 4 +- test/testTorchPoints.py | 4 +- test/testZeillinger.py | 2 +- 27 files changed, 82 insertions(+), 126 deletions(-) rename hironaka/{abs => core}/Points.py (100%) rename hironaka/{abs => core}/PointsBase.py (100%) rename hironaka/{abs => core}/PointsNumpy.py (100%) rename hironaka/{abs => core}/PointsTensor.py (100%) rename hironaka/{abs => core}/__init__.py (100%) rename hironaka/{envs => gym_env}/HironakaAgentEnv.py (100%) rename hironaka/{envs => gym_env}/HironakaBase.py (100%) rename hironaka/{envs => gym_env}/HironakaHostEnv.py (100%) rename hironaka/{envs => gym_env}/__init__.py (100%) delete mode 100644 hironaka/policy_players/PolicyAgent.py delete mode 100644 hironaka/policy_players/PolicyHost.py delete mode 100644 hironaka/util/geom.py diff --git a/hironaka/agent.py b/hironaka/agent.py index ef27e77..651216e 100644 --- a/hironaka/agent.py +++ b/hironaka/agent.py @@ -2,7 +2,8 @@ import numpy as np -from .abs import Points +from hironaka.core import Points +from hironaka.policy.Policy import Policy class Agent(abc.ABC): @@ -39,6 +40,25 @@ def move(self, points: Points, coords, weights=None, inplace=True): return actions +class PolicyAgent(Agent): + def __init__(self, policy: Policy): + self._policy = policy + + def move(self, points: Points, coords: List[List[int]], inplace=True): + assert len( + coords) == points.batch_size # TODO: wrap the move method for the abstract "Agent" with sanity checks? + + features = points.get_features() + + actions = self._policy.predict((features, coords)) + + if inplace: + points.shift(coords, actions) + points.get_newton_polytope() + + return actions + + class AgentMorin(Agent): def move(self, points: Points, coords, weights, inplace=True): assert points.batch_size == 1, "Temporarily only support batch size 1." # TODO: generalize! diff --git a/hironaka/abs/Points.py b/hironaka/core/Points.py similarity index 100% rename from hironaka/abs/Points.py rename to hironaka/core/Points.py diff --git a/hironaka/abs/PointsBase.py b/hironaka/core/PointsBase.py similarity index 100% rename from hironaka/abs/PointsBase.py rename to hironaka/core/PointsBase.py diff --git a/hironaka/abs/PointsNumpy.py b/hironaka/core/PointsNumpy.py similarity index 100% rename from hironaka/abs/PointsNumpy.py rename to hironaka/core/PointsNumpy.py diff --git a/hironaka/abs/PointsTensor.py b/hironaka/core/PointsTensor.py similarity index 100% rename from hironaka/abs/PointsTensor.py rename to hironaka/core/PointsTensor.py diff --git a/hironaka/abs/__init__.py b/hironaka/core/__init__.py similarity index 100% rename from hironaka/abs/__init__.py rename to hironaka/core/__init__.py diff --git a/hironaka/game.py b/hironaka/game.py index be983da..64ab46d 100644 --- a/hironaka/game.py +++ b/hironaka/game.py @@ -2,7 +2,7 @@ import logging from typing import Optional, Union -from hironaka.abs import Points +from hironaka.core import Points from hironaka.agent import Agent from hironaka.host import Host diff --git a/hironaka/envs/HironakaAgentEnv.py b/hironaka/gym_env/HironakaAgentEnv.py similarity index 100% rename from hironaka/envs/HironakaAgentEnv.py rename to hironaka/gym_env/HironakaAgentEnv.py diff --git a/hironaka/envs/HironakaBase.py b/hironaka/gym_env/HironakaBase.py similarity index 100% rename from hironaka/envs/HironakaBase.py rename to hironaka/gym_env/HironakaBase.py diff --git a/hironaka/envs/HironakaHostEnv.py b/hironaka/gym_env/HironakaHostEnv.py similarity index 100% rename from hironaka/envs/HironakaHostEnv.py rename to hironaka/gym_env/HironakaHostEnv.py diff --git a/hironaka/envs/__init__.py b/hironaka/gym_env/__init__.py similarity index 100% rename from hironaka/envs/__init__.py rename to hironaka/gym_env/__init__.py diff --git a/hironaka/host.py b/hironaka/host.py index 71d0074..a04981e 100644 --- a/hironaka/host.py +++ b/hironaka/host.py @@ -1,9 +1,11 @@ import abc from itertools import combinations +from typing import Optional import numpy as np -from .abs import Points +from hironaka.core import Points +from hironaka.policy.Policy import Policy class Host(abc.ABC): @@ -54,63 +56,49 @@ def select_coord(self, points: Points, debug=False): if debug: print(char_vectors) - r = [np.argmin(char_vectors[0][0]), np.argmax(char_vectors[0][0])] - if r[0] != r[1]: - result.append(r) - else: # if all coordinates are the same, return the first two. - result.append([0, 1]) - + result.append(self._get_coord(char_vectors)) return result + def _get_coord(self, char_vectors): + r = [np.argmin(char_vectors[0][0]), np.argmax(char_vectors[0][0])] + if r[0] != r[1]: + return r + else: # if all coordinates are the same, return the first two. + return [0, 1] -class ZeillingerLex(Host): - # noinspection PyPep8Naming - @staticmethod - def get_char_vector(vt): - """ - Character vector (L, S), - L: maximum coordinate - minimum coordinate - S: sum of the numbers of maximum coordinates and minimum coordinates - e.g., (1, 1, -1, -1) -> (L=2, S=4) - """ - mx = max(vt) - mn = min(vt) - L = mx - mn - S = sum([vt[i] == mx for i in range(len(vt))]) + \ - sum([vt[i] == mn for i in range(len(vt))]) - return L, S + +class PolicyHost(Host): + def __init__(self, + policy: Policy, + use_discrete_actions_for_host: Optional[bool] = False, + **kwargs): + self._policy = policy + self.use_discrete_actions_for_host = kwargs.get('use_discrete_actions_for_host', use_discrete_actions_for_host) def select_coord(self, points: Points, debug=False): - assert not points.ended - dim = points.dimension - result = [] - for b in range(points.batch_size): - pts = points.get_batch(b) - if len(pts) <= 1: - result.append([]) - continue - pairs = combinations(pts, 2) - char_vectors = [] - for pair in pairs: - vector = tuple([pair[0][i] - pair[1][i] for i in range(dim)]) - char_vectors.append((vector, self.get_char_vector(vector))) - char_vectors.sort(key=(lambda x: x[1])) + features = points.get_features() - if debug: - print(char_vectors) - coords = [] - for char_vector in char_vectors: - if char_vector[1] == char_vectors[0][1]: - r = [np.argmin(char_vector[0]), np.argmax(char_vector[0])] - if r[0] != r[1]: - coords.append(r) - else: # if all coordinates are the same, return the first two. - coords.append([0, 1]) - coords.sort() - result.append(coords[0]) + coords = self._policy.predict(features) # return multi-binary array + result = [] + for b in range(coords.shape[0]): + result.append(np.where(coords[b] == 1)[0]) return result +class ZeillingerLex(Zeillinger): + def _get_coord(self, char_vectors): # TODO: efficiency can be improved + coords = [] + for char_vector in char_vectors: + if char_vector[1] == char_vectors[0][1]: + r = [np.argmin(char_vector[0]), np.argmax(char_vector[0])] + if r[0] != r[1]: + coords.append(r) + else: # if all coordinates are the same, return the first two. + coords.append([0, 1]) + coords.sort() + return coords[0] + + class WeakSpivakovsky(Host): def select_coord(self, points: Points, debug=False): assert not points.ended diff --git a/hironaka/policy_players/PolicyAgent.py b/hironaka/policy_players/PolicyAgent.py deleted file mode 100644 index 5914042..0000000 --- a/hironaka/policy_players/PolicyAgent.py +++ /dev/null @@ -1,24 +0,0 @@ -from typing import List - -from hironaka.abs import Points -from hironaka.agent import Agent -from hironaka.policy.Policy import Policy - - -class PolicyAgent(Agent): - def __init__(self, policy: Policy): - self._policy = policy - - def move(self, points: Points, coords: List[List[int]], inplace=True): - assert len( - coords) == points.batch_size # TODO: wrap the move method for the abstract "Agent" with sanity checks? - - features = points.get_features() - - actions = self._policy.predict((features, coords)) - - if inplace: - points.shift(coords, actions) - points.get_newton_polytope() - - return actions diff --git a/hironaka/policy_players/PolicyHost.py b/hironaka/policy_players/PolicyHost.py deleted file mode 100644 index 27b9b50..0000000 --- a/hironaka/policy_players/PolicyHost.py +++ /dev/null @@ -1,25 +0,0 @@ -from typing import Optional - -import numpy as np - -from hironaka.abs import Points -from hironaka.host import Host -from hironaka.policy.Policy import Policy - - -class PolicyHost(Host): - def __init__(self, - policy: Policy, - use_discrete_actions_for_host: Optional[bool] = False, - **kwargs): - self._policy = policy - self.use_discrete_actions_for_host = kwargs.get('use_discrete_actions_for_host', use_discrete_actions_for_host) - - def select_coord(self, points: Points, debug=False): - features = points.get_features() - - coords = self._policy.predict(features) # return multi-binary array - result = [] - for b in range(coords.shape[0]): - result.append(np.where(coords[b] == 1)[0]) - return result diff --git a/hironaka/src/_snippets.py b/hironaka/src/_snippets.py index c39a8ee..0826e61 100644 --- a/hironaka/src/_snippets.py +++ b/hironaka/src/_snippets.py @@ -166,3 +166,10 @@ def mask_encoded_action(dimension: int): result[1 << i] = 0 return result + +def generate_points(n: int, dimension=3, max_value=50): + return [[np.random.randint(max_value) for _ in range(dimension)] for _ in range(n)] + + +def generate_batch_points(n: int, batch_num=1, dimension=3, max_value=50): + return [[[np.random.randint(max_value) for _ in range(dimension)] for _ in range(n)] for _ in range(batch_num)] diff --git a/hironaka/util/__init__.py b/hironaka/util/__init__.py index 6331a7f..114d7ee 100644 --- a/hironaka/util/__init__.py +++ b/hironaka/util/__init__.py @@ -1,2 +1 @@ -from .geom import * from .search import * diff --git a/hironaka/util/geom.py b/hironaka/util/geom.py deleted file mode 100644 index 051bbb7..0000000 --- a/hironaka/util/geom.py +++ /dev/null @@ -1,9 +0,0 @@ -import numpy as np - - -def generate_points(n: int, dimension=3, max_value=50): - return [[np.random.randint(max_value) for _ in range(dimension)] for _ in range(n)] - - -def generate_batch_points(n: int, batch_num=1, dimension=3, max_value=50): - return [[[np.random.randint(max_value) for _ in range(dimension)] for _ in range(n)] for _ in range(batch_num)] diff --git a/hironaka/util/search.py b/hironaka/util/search.py index 4c40de4..d969951 100644 --- a/hironaka/util/search.py +++ b/hironaka/util/search.py @@ -2,7 +2,7 @@ import numpy as np -from hironaka.abs import Points +from hironaka.core import Points from hironaka.host import Host diff --git a/hironaka/validator/HironakaValidator.py b/hironaka/validator/HironakaValidator.py index 14a122f..5255dbd 100644 --- a/hironaka/validator/HironakaValidator.py +++ b/hironaka/validator/HironakaValidator.py @@ -1,9 +1,9 @@ import logging from typing import Optional, Dict, Any -from hironaka.abs import Points +from hironaka.core import Points from hironaka.game import GameHironaka -from hironaka.util import generate_batch_points +from hironaka.src import generate_batch_points class HironakaValidator(GameHironaka): diff --git a/test/testGame.py b/test/testGame.py index 1ae126b..7a048c3 100644 --- a/test/testGame.py +++ b/test/testGame.py @@ -3,7 +3,7 @@ from hironaka.agent import RandomAgent from hironaka.game import GameHironaka from hironaka.host import Zeillinger -from hironaka.util import * +from hironaka.src import * class TestGame(unittest.TestCase): diff --git a/test/testGymEnv.py b/test/testGymEnv.py index 7d9b12a..960ea3d 100644 --- a/test/testGymEnv.py +++ b/test/testGymEnv.py @@ -4,7 +4,7 @@ import numpy as np from gym.envs.registration import register -from hironaka.abs import Points +from hironaka.core import Points from hironaka.agent import RandomAgent from hironaka.host import Zeillinger, RandomHost diff --git a/test/testPoints.py b/test/testPoints.py index 76be03f..b9bfe01 100644 --- a/test/testPoints.py +++ b/test/testPoints.py @@ -4,7 +4,7 @@ from hironaka.abs.Points import Points from hironaka.src import make_nested_list -from hironaka.util import generate_points +from hironaka.src import generate_points class TestPoints(unittest.TestCase): diff --git a/test/testPolicy.py b/test/testPolicy.py index 4b537f0..7d29cc0 100644 --- a/test/testPolicy.py +++ b/test/testPolicy.py @@ -2,12 +2,12 @@ from torch import nn -from hironaka.abs import Points +from hironaka.core import Points from hironaka.game import GameHironaka from hironaka.policy.NNPolicy import NNPolicy -from hironaka.policy_players.PolicyAgent import PolicyAgent -from hironaka.policy_players.PolicyHost import PolicyHost -from hironaka.util import generate_batch_points +from hironaka.agent import PolicyAgent +from hironaka.host import PolicyHost +from hironaka.src import generate_batch_points class NN(nn.Module): diff --git a/test/testSearch.py b/test/testSearch.py index f9004ba..f3ff050 100644 --- a/test/testSearch.py +++ b/test/testSearch.py @@ -2,7 +2,7 @@ from treelib import Tree -from hironaka.abs import Points +from hironaka.core import Points from hironaka.host import Zeillinger from hironaka.src import make_nested_list from hironaka.util import search_depth, search_tree diff --git a/test/testThom.py b/test/testThom.py index 3ba044b..5db855b 100644 --- a/test/testThom.py +++ b/test/testThom.py @@ -2,11 +2,11 @@ from treelib import Tree -from hironaka.abs import Points +from hironaka.core import Points from hironaka.agent import AgentMorin from hironaka.game import GameMorin from hironaka.host import ZeillingerLex, WeakSpivakovsky -from hironaka.src._thom_snippets import thom_monomial_ideal, thom_points, thom_points_homogeneous +from hironaka.src import thom_monomial_ideal, thom_points, thom_points_homogeneous from hironaka.util import search_tree_morin diff --git a/test/testTorchPoints.py b/test/testTorchPoints.py index 869f6b6..609ad9b 100644 --- a/test/testTorchPoints.py +++ b/test/testTorchPoints.py @@ -2,8 +2,8 @@ import torch -from hironaka.abs.PointsTensor import PointsTensor -from hironaka.src._torch_ops import get_newton_polytope_torch, shift_torch, reposition_torch +from hironaka.core import PointsTensor +from hironaka.src import get_newton_polytope_torch, shift_torch, reposition_torch class testTorchPoints(unittest.TestCase): diff --git a/test/testZeillinger.py b/test/testZeillinger.py index 73075a7..3347752 100644 --- a/test/testZeillinger.py +++ b/test/testZeillinger.py @@ -1,6 +1,6 @@ import unittest -from hironaka.abs import Points +from hironaka.core import Points from hironaka.host import Zeillinger from hironaka.src import make_nested_list From abf0de08bf86de72e84c1011f2ff74b3d35830e0 Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Sun, 17 Jul 2022 23:36:47 +0200 Subject: [PATCH 11/16] minor fix --- hironaka/agent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hironaka/agent.py b/hironaka/agent.py index 651216e..8800394 100644 --- a/hironaka/agent.py +++ b/hironaka/agent.py @@ -4,7 +4,7 @@ from hironaka.core import Points from hironaka.policy.Policy import Policy - +from typing import List class Agent(abc.ABC): """ From d00b092be0a2f8a5a0a8bc57b527344ea8fee367 Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Sun, 17 Jul 2022 23:40:28 +0200 Subject: [PATCH 12/16] minor fix --- hironaka/core/Points.py | 2 +- hironaka/core/PointsNumpy.py | 2 +- hironaka/core/PointsTensor.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hironaka/core/Points.py b/hironaka/core/Points.py index b24b1a2..e05ec71 100644 --- a/hironaka/core/Points.py +++ b/hironaka/core/Points.py @@ -2,7 +2,7 @@ import numpy as np -from hironaka.abs.PointsBase import PointsBase +from .PointsBase import PointsBase from hironaka.src import shift_lst, get_newton_polytope_lst, get_shape, scale_points, reposition_lst, \ get_newton_polytope_approx_lst diff --git a/hironaka/core/PointsNumpy.py b/hironaka/core/PointsNumpy.py index 555db24..a04168e 100644 --- a/hironaka/core/PointsNumpy.py +++ b/hironaka/core/PointsNumpy.py @@ -1,4 +1,4 @@ -from hironaka.abs.PointsBase import PointsBase +from .PointsBase import PointsBase class PointsNumpy(PointsBase): # INCOMPLETE diff --git a/hironaka/core/PointsTensor.py b/hironaka/core/PointsTensor.py index 82fde4d..ea3d8db 100644 --- a/hironaka/core/PointsTensor.py +++ b/hironaka/core/PointsTensor.py @@ -3,7 +3,7 @@ import numpy as np import torch -from hironaka.abs.PointsBase import PointsBase +from .PointsBase import PointsBase from hironaka.src import get_batched_padded_array, rescale_torch from hironaka.src import shift_torch, get_newton_polytope_torch, reposition_torch From e940e937a4c68b75dd56e751c8bba4b09bac43c9 Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Sun, 17 Jul 2022 23:44:32 +0200 Subject: [PATCH 13/16] minor fix --- hironaka/core/__init__.py | 1 + test/testPoints.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hironaka/core/__init__.py b/hironaka/core/__init__.py index 2dabb6e..683db19 100644 --- a/hironaka/core/__init__.py +++ b/hironaka/core/__init__.py @@ -1 +1,2 @@ from .Points import * +from .PointsTensor import * \ No newline at end of file diff --git a/test/testPoints.py b/test/testPoints.py index b9bfe01..c1a173d 100644 --- a/test/testPoints.py +++ b/test/testPoints.py @@ -2,7 +2,7 @@ import numpy as np -from hironaka.abs.Points import Points +from hironaka.core import Points from hironaka.src import make_nested_list from hironaka.src import generate_points From d26b3c7065d8434027787b5bc783c412cd37f18f Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Sun, 17 Jul 2022 23:49:29 +0200 Subject: [PATCH 14/16] minor fix --- test/testGame.py | 1 + test/testGymEnv.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/testGame.py b/test/testGame.py index 7a048c3..c1c1021 100644 --- a/test/testGame.py +++ b/test/testGame.py @@ -4,6 +4,7 @@ from hironaka.game import GameHironaka from hironaka.host import Zeillinger from hironaka.src import * +from hironaka.core import Points class TestGame(unittest.TestCase): diff --git a/test/testGymEnv.py b/test/testGymEnv.py index 960ea3d..19000bb 100644 --- a/test/testGymEnv.py +++ b/test/testGymEnv.py @@ -10,13 +10,13 @@ register( id='hironaka/HironakaHost-v0', - entry_point='hironaka.envs:HironakaHostEnv', + entry_point='hironaka.gym_env:HironakaHostEnv', max_episode_steps=10000, ) register( id='hironaka/HironakaAgent-v0', - entry_point='hironaka.envs:HironakaAgentEnv', + entry_point='hironaka.gym_env:HironakaAgentEnv', max_episode_steps=10000, ) From 9e79c84f626c26cbd1a313d675ce4e9394146fa4 Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Mon, 18 Jul 2022 00:06:33 +0200 Subject: [PATCH 15/16] minor fix --- hironaka/gym_env/HironakaAgentEnv.py | 2 +- hironaka/gym_env/HironakaBase.py | 4 ++-- hironaka/gym_env/HironakaHostEnv.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hironaka/gym_env/HironakaAgentEnv.py b/hironaka/gym_env/HironakaAgentEnv.py index 586a915..01acc6f 100644 --- a/hironaka/gym_env/HironakaAgentEnv.py +++ b/hironaka/gym_env/HironakaAgentEnv.py @@ -4,7 +4,7 @@ from gym import spaces from hironaka.agent import Agent -from hironaka.envs.HironakaBase import HironakaBase +from .HironakaBase import HironakaBase from hironaka.src import decode_action diff --git a/hironaka/gym_env/HironakaBase.py b/hironaka/gym_env/HironakaBase.py index 2d97b53..c024b5d 100644 --- a/hironaka/gym_env/HironakaBase.py +++ b/hironaka/gym_env/HironakaBase.py @@ -5,9 +5,9 @@ import numpy as np from gym import spaces -from hironaka.abs import Points +from hironaka.core import Points from hironaka.src import get_padded_array, get_gym_version_in_float -from hironaka.util import generate_points +from hironaka.src import generate_points ObsType = TypeVar("ObsType") ActType = TypeVar("ActType") diff --git a/hironaka/gym_env/HironakaHostEnv.py b/hironaka/gym_env/HironakaHostEnv.py index 6de4846..96fe594 100644 --- a/hironaka/gym_env/HironakaHostEnv.py +++ b/hironaka/gym_env/HironakaHostEnv.py @@ -3,7 +3,7 @@ import numpy as np from gym import spaces -from hironaka.envs.HironakaBase import HironakaBase +from .HironakaBase import HironakaBase from hironaka.host import Host From fdb4cb82a379db2a72497e434f60768cd3462c96 Mon Sep 17 00:00:00 2001 From: Honglu Fan <64070721+honglu2875@users.noreply.github.com> Date: Mon, 18 Jul 2022 07:52:47 +0200 Subject: [PATCH 16/16] Update requirements.txt Fix requirements --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1edb69b..1a70d4a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ treelib==1.6.1 gym==0.22 torch~=1.12.0 scipy~=1.8.1 -PyYAML~=6.0 \ No newline at end of file +PyYAML~=6.0 +sympy~=1.10.1