From 35b1fc5e43f553e95ad4c8a42c37ca66639d9120 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Mon, 23 Sep 2019 18:55:32 -0400 Subject: [PATCH 01/12] add test for core.py --- HARK/tests/test_core.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 HARK/tests/test_core.py diff --git a/HARK/tests/test_core.py b/HARK/tests/test_core.py new file mode 100644 index 000000000..34199294d --- /dev/null +++ b/HARK/tests/test_core.py @@ -0,0 +1,15 @@ +""" +This file implements unit tests for interpolation methods +""" +from HARK.core import HARKobject + +import numpy as np +import unittest + +class testHARKobject(unittest.TestCase): + def setUp(self): + self.obj_a = HARKobject() + self.obj_b = HARKobject() + + def test_distance(self): + self.assertRaises(AttributeError, self.obj_a.distance(self.obj_b)) From 9563bcf546cb8ff6fea152b381c68a957b7566d9 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Wed, 25 Sep 2019 14:41:55 -0400 Subject: [PATCH 02/12] fix logic bug while comparing types --- HARK/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HARK/core.py b/HARK/core.py index 4ca8331ff..119db0143 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -53,7 +53,7 @@ def distanceMetric(thing_A, thing_B): else: distance = float(abs(lenA - lenB)) # If both inputs are numbers, return their difference - elif (typeA is int or typeB is float) and (typeB is int or typeB is float): + elif isinstance(thing_A, (int, float)) and isinstance(thing_B, (int, float)): distance = float(abs(thing_A - thing_B)) # If both inputs are array-like, return the maximum absolute difference b/w # corresponding elements (if same shape); return largest difference in dimensions From 24dea3eccd022663de221724a6d4f031244d64a5 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Thu, 26 Sep 2019 16:24:57 -0500 Subject: [PATCH 03/12] flatten dimensions when calculating distance b/w 2 --- HARK/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/HARK/core.py b/HARK/core.py index 119db0143..941ead370 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -62,7 +62,8 @@ def distanceMetric(thing_A, thing_B): if thing_A.shape == thing_B.shape: distance = np.max(abs(thing_A - thing_B)) else: - distance = np.max(abs(thing_A.shape - thing_B.shape)) + # Flatten arrays so there are in the same dimensions + distance = np.max(abs(thing_A.flatten().shape[0] - thing_B.flatten().shape[0])) # If none of the above cases, but the objects are of the same class, call # the distance method of one on the other elif thing_A.__class__.__name__ == thing_B.__class__.__name__: From 3258fe1c1771ba521d1fd5e9317ad8e6b1c7ef36 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Thu, 26 Sep 2019 16:27:56 -0500 Subject: [PATCH 04/12] add base tests for core.distanceMetric --- HARK/tests/test_core.py | 75 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/HARK/tests/test_core.py b/HARK/tests/test_core.py index 34199294d..246499ea7 100644 --- a/HARK/tests/test_core.py +++ b/HARK/tests/test_core.py @@ -1,15 +1,82 @@ """ This file implements unit tests for interpolation methods """ -from HARK.core import HARKobject +from HARK.core import HARKobject, distanceMetric, AgentType import numpy as np import unittest -class testHARKobject(unittest.TestCase): + +class testdistanceMetric(unittest.TestCase): def setUp(self): + self.list_a = [1.0, 2.1, 3] + self.list_b = [3.1, 4, -1.4] + self.list_c = [8.6, 9] self.obj_a = HARKobject() self.obj_b = HARKobject() + self.obj_c = HARKobject() + + def test_list(self): + # same length + self.assertEqual(distanceMetric(self.list_a, self.list_b), 4.4) + # different length + self.assertEqual(distanceMetric(self.list_b, self.list_c), 1.0) + # sanity check, same objects + self.assertEqual(distanceMetric(self.list_b, self.list_b), 0.0) + + def test_array(self): + # same length + self.assertEqual( + distanceMetric(np.array(self.list_a), np.array(self.list_b)), 4.4 + ) + # different length + self.assertEqual( + distanceMetric(np.array(self.list_b).reshape(1, 3), np.array(self.list_c)), + 1.0, + ) + # sanity check, same objects + self.assertEqual( + distanceMetric(np.array(self.list_b), np.array(self.list_b)), 0.0 + ) + + def test_hark_object_distance(self): + self.obj_a.distance_criteria = ["var_1", "var_2", "var_3"] + self.obj_b.distance_criteria = ["var_1", "var_2", "var_3"] + self.obj_c.distance_criteria = ["var_5"] + # if attributes don't exist or don't match + self.assertEqual(distanceMetric(self.obj_a, self.obj_b), 1000.0) + self.assertEqual(distanceMetric(self.obj_a, self.obj_c), 1000.0) + # add single numbers to attributes + self.obj_a.var_1, self.obj_a.var_2, self.obj_a.var_3 = 0.1, 1, 2.1 + self.obj_b.var_1, self.obj_b.var_2, self.obj_b.var_3 = 1.8, -1, 0.1 + self.assertEqual(distanceMetric(self.obj_a, self.obj_b), 2.0) + + # sanity check - same objects + self.assertEqual(distanceMetric(self.obj_a, self.obj_a), 0.0) + + +# class testHARKobject(unittest.TestCase): +# def setUp(self): +# self.obj_a = HARKobject() +# self.obj_b = HARKobject() +# self.obj_c = HARKobject() + +# def test_distance(self): +# self.assertRaises(AttributeError, self.obj_a.distance(self.obj_b)) + +# class testAgentType(unittest.TestCase): +# def setUp(self): +# self.agent = AgentType() - def test_distance(self): - self.assertRaises(AttributeError, self.obj_a.distance(self.obj_b)) +# def test_time(self): +# self.agent.time_vary = ['var_1', 'var_2'] +# self.agent.var_1 = [4.3, 2, 1] +# self.agent.var_2 = [1, 2, 3, 4, 5] +# self.agent.timeFlip() +# self.assertEqual(self.agent.var_1, [1, 2, 4.3]) +# self.assertEqual(self.agent.var_2, [5, 4, 3, 2, 1]) +# self.assertEqual(self.agent.time_flow, True) +# self.agent.timeFlip() +# self.assertEqual(self.agent.var_1, [4.3, 2, 1]) +# self.assertEqual(self.agent.var_2, [1, 2, 3, 4, 5]) +# self.assertEqual(self.agent.time_flow, False) From b85cb8ac50d2af5050b63a81b8763b4ffde4457b Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Thu, 26 Sep 2019 16:48:39 -0500 Subject: [PATCH 05/12] add base tests for core.HARKobject --- HARK/tests/test_core.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/HARK/tests/test_core.py b/HARK/tests/test_core.py index 246499ea7..2334736d8 100644 --- a/HARK/tests/test_core.py +++ b/HARK/tests/test_core.py @@ -55,14 +55,26 @@ def test_hark_object_distance(self): self.assertEqual(distanceMetric(self.obj_a, self.obj_a), 0.0) -# class testHARKobject(unittest.TestCase): -# def setUp(self): -# self.obj_a = HARKobject() -# self.obj_b = HARKobject() -# self.obj_c = HARKobject() +class testHARKobject(unittest.TestCase): + def setUp(self): + # similar test to distanceMetric + self.obj_a = HARKobject() + self.obj_b = HARKobject() + self.obj_c = HARKobject() + + def test_distance(self): + self.obj_a.distance_criteria = ["var_1", "var_2", "var_3"] + self.obj_b.distance_criteria = ["var_1", "var_2", "var_3"] + self.obj_c.distance_criteria = ["var_5"] + self.obj_a.var_1, self.obj_a.var_2, self.obj_a.var_3 = [0.1], [1, 2], [2.1] + self.obj_b.var_1, self.obj_b.var_2, self.obj_b.var_3 = [1.8], [0, 0.1], [1.1] + self.assertEqual(self.obj_a.distance(self.obj_b), 1.9) + # change the length of a attribute list + self.obj_b.var_1, self.obj_b.var_2, self.obj_b.var_3 = [1.8], [0, 0, 0.1], [1.1] + self.assertEqual(self.obj_a.distance(self.obj_b), 1.7) + # sanity check + self.assertEqual(self.obj_b.distance(self.obj_a), 0.0) -# def test_distance(self): -# self.assertRaises(AttributeError, self.obj_a.distance(self.obj_b)) # class testAgentType(unittest.TestCase): # def setUp(self): From aed142e53eb1f536dfc41cd5a5722b49231f29ae Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Thu, 26 Sep 2019 17:12:01 -0500 Subject: [PATCH 06/12] change default behaviour of AgentType time flow to forward --- HARK/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HARK/core.py b/HARK/core.py index 941ead370..8e84690f0 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -175,7 +175,7 @@ class AgentType(HARKobject): 'solveOnePeriod' should appear in exactly one of these lists, depending on whether the same solution method is used in all periods of the model. ''' - def __init__(self, solution_terminal=None, cycles=1, time_flow=False, pseudo_terminal=True, + def __init__(self, solution_terminal=None, cycles=1, time_flow=True, pseudo_terminal=True, tolerance=0.000001, seed=0, **kwds): ''' Initialize an instance of AgentType by setting attributes. @@ -193,7 +193,7 @@ def __init__(self, solution_terminal=None, cycles=1, time_flow=False, pseudo_ter once before terminating. cycles=0 corresponds to an infinite horizon model, with a sequence of one period problems repeating indefinitely. time_flow : boolean - Whether time is currently "flowing" forward or backward for this + Whether time is currently "flowing" forward(True) or backward(False) for this instance. Used to flip between solving (using backward iteration) and simulating (etc). pseudo_terminal : boolean From 0984247dc5b98c66015b77db0fd9b40f3cda88ce Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Thu, 26 Sep 2019 18:02:01 -0500 Subject: [PATCH 07/12] lets not use exec in code unless absolutely required --- HARK/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HARK/core.py b/HARK/core.py index 8e84690f0..99cdbaf4a 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -260,7 +260,7 @@ def timeFlip(self): none ''' for name in self.time_vary: - exec('self.' + name + '.reverse()') + self.__dict__[name].reverse() self.time_flow = not self.time_flow def timeFwd(self): From e3f224e100027be19cddb07d3157bdf6414a0e5c Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Thu, 26 Sep 2019 18:28:10 -0500 Subject: [PATCH 08/12] add base tests for AgentType --- HARK/core.py | 5 +++-- HARK/tests/test_core.py | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/HARK/core.py b/HARK/core.py index 99cdbaf4a..1ede73d88 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -839,8 +839,9 @@ def solveOneCycle(agent, solution_last): ''' # Calculate number of periods per cycle, defaults to 1 if all variables are time invariant if len(agent.time_vary) > 0: - name = agent.time_vary[0] - T = len(eval('agent.' + name)) + # name = agent.time_vary[0] + # T = len(eval('agent.' + name)) + T = len(agent.__dict__[agent.time_vary[0]]) else: T = 1 diff --git a/HARK/tests/test_core.py b/HARK/tests/test_core.py index 2334736d8..6b931a8e2 100644 --- a/HARK/tests/test_core.py +++ b/HARK/tests/test_core.py @@ -73,22 +73,22 @@ def test_distance(self): self.obj_b.var_1, self.obj_b.var_2, self.obj_b.var_3 = [1.8], [0, 0, 0.1], [1.1] self.assertEqual(self.obj_a.distance(self.obj_b), 1.7) # sanity check - self.assertEqual(self.obj_b.distance(self.obj_a), 0.0) + self.assertEqual(self.obj_b.distance(self.obj_b), 0.0) -# class testAgentType(unittest.TestCase): -# def setUp(self): -# self.agent = AgentType() +class testAgentType(unittest.TestCase): + def setUp(self): + self.agent = AgentType() -# def test_time(self): -# self.agent.time_vary = ['var_1', 'var_2'] -# self.agent.var_1 = [4.3, 2, 1] -# self.agent.var_2 = [1, 2, 3, 4, 5] -# self.agent.timeFlip() -# self.assertEqual(self.agent.var_1, [1, 2, 4.3]) -# self.assertEqual(self.agent.var_2, [5, 4, 3, 2, 1]) -# self.assertEqual(self.agent.time_flow, True) -# self.agent.timeFlip() -# self.assertEqual(self.agent.var_1, [4.3, 2, 1]) -# self.assertEqual(self.agent.var_2, [1, 2, 3, 4, 5]) -# self.assertEqual(self.agent.time_flow, False) + def test_time(self): + self.agent.time_vary = ['var_1', 'var_2'] + self.agent.var_1 = [4.3, 2, 1] + self.agent.var_2 = [1, 2, 3, 4, 5] + self.agent.timeFlip() + self.assertEqual(self.agent.var_1, [1, 2, 4.3]) + self.assertEqual(self.agent.var_2, [5, 4, 3, 2, 1]) + self.assertEqual(self.agent.time_flow, False) + self.agent.timeFlip() + self.assertEqual(self.agent.var_1, [4.3, 2, 1]) + self.assertEqual(self.agent.var_2, [1, 2, 3, 4, 5]) + self.assertEqual(self.agent.time_flow, True) From 0e77e795c637e65bf66928122b211485e5d26156 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Sat, 28 Sep 2019 13:10:35 -0500 Subject: [PATCH 09/12] use eval() only when necessary --- HARK/core.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/HARK/core.py b/HARK/core.py index 1ede73d88..390c3e8b8 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -852,13 +852,16 @@ def solveOneCycle(agent, solution_last): these_args = getArgNames(solveOnePeriod) # Construct a dictionary to be passed to the solver - time_inv_string = '' - for name in agent.time_inv: - time_inv_string += ' \'' + name + '\' : agent.' + name + ',' - time_vary_string = '' - for name in agent.time_vary: - time_vary_string += ' \'' + name + '\' : None,' - solve_dict = eval('{' + time_inv_string + time_vary_string + '}') + # time_inv_string = '' + # for name in agent.time_inv: + # time_inv_string += ' \'' + name + '\' : agent.' + name + ',' + # time_vary_string = '' + # for name in agent.time_vary: + # time_vary_string += ' \'' + name + '\' : None,' + # solve_dict = eval('{' + time_inv_string + time_vary_string + '}') + time_inv_dict = {parameter: agent.__dict__[parameter] for parameter in agent.time_inv} + time_vary_dict = {parameter: None for parameter in agent.time_vary} + solve_dict = {**time_inv_dict, **time_vary_dict} # Initialize the solution for this cycle, then iterate on periods solution_cycle = [] From 2aebd7f56795f4d7901527fd08dc57150aedc13e Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Sat, 28 Sep 2019 13:58:20 -0500 Subject: [PATCH 10/12] add base tests for core.AgentType --- HARK/tests/test_core.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/HARK/tests/test_core.py b/HARK/tests/test_core.py index 6b931a8e2..e32fc0517 100644 --- a/HARK/tests/test_core.py +++ b/HARK/tests/test_core.py @@ -92,3 +92,19 @@ def test_time(self): self.assertEqual(self.agent.var_1, [4.3, 2, 1]) self.assertEqual(self.agent.var_2, [1, 2, 3, 4, 5]) self.assertEqual(self.agent.time_flow, True) + self.agent.timeRev() + self.assertEqual(self.agent.time_flow, False) + self.agent.timeFwd() + self.assertEqual(self.agent.time_flow, True) + + def test_solve(self): + self.agent.time_vary = ['vary_1'] + self.agent.time_inv = ['inv_1'] + self.agent.vary_1 = [1.1, 1.2, 1.3, 1.4] + self.agent.inv_1 = 1.05 + # to test the superclass we create a dummy solveOnePeriod function + # for our agent, which doesn't do anything, instead of using a NullFunc + self.agent.solveOnePeriod = lambda vary_1: HARKobject() + self.agent.solve() + self.assertEqual(len(self.agent.solution), 4) + self.assertTrue(isinstance(self.agent.solution[0], HARKobject)) \ No newline at end of file From 409ee0cc15313694abae9b7c7157ee4f2d934da3 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Sat, 28 Sep 2019 13:59:04 -0500 Subject: [PATCH 11/12] move from eval to __dict__ to access attributes --- HARK/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/HARK/core.py b/HARK/core.py index 390c3e8b8..08e7edcca 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -875,7 +875,8 @@ def solveOneCycle(agent, solution_last): # Update time-varying single period inputs for name in agent.time_vary: if name in these_args: - solve_dict[name] = eval('agent.' + name + '[t]') + # solve_dict[name] = eval('agent.' + name + '[t]') + solve_dict[name] = agent.__dict__[name][t] solve_dict['solution_next'] = solution_next # Make a temporary dictionary for this period From 79f41d1e274e81dae30d7515032839ec01283b65 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Sun, 29 Sep 2019 15:52:25 -0500 Subject: [PATCH 12/12] add python version agnostic logic --- HARK/core.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/HARK/core.py b/HARK/core.py index 08e7edcca..328fa3868 100644 --- a/HARK/core.py +++ b/HARK/core.py @@ -859,9 +859,8 @@ def solveOneCycle(agent, solution_last): # for name in agent.time_vary: # time_vary_string += ' \'' + name + '\' : None,' # solve_dict = eval('{' + time_inv_string + time_vary_string + '}') - time_inv_dict = {parameter: agent.__dict__[parameter] for parameter in agent.time_inv} - time_vary_dict = {parameter: None for parameter in agent.time_vary} - solve_dict = {**time_inv_dict, **time_vary_dict} + solve_dict = {parameter: agent.__dict__[parameter] for parameter in agent.time_inv} + solve_dict.update({parameter: None for parameter in agent.time_vary}) # Initialize the solution for this cycle, then iterate on periods solution_cycle = []