Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add base tests for core.HARKobject, core.distanceMetric, core.AgentType #401

Merged
merged 15 commits into from
Oct 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 20 additions & 15 deletions HARK/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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__:
Expand Down Expand Up @@ -174,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.
Expand All @@ -192,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
Expand Down Expand Up @@ -259,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):
Expand Down Expand Up @@ -839,8 +840,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

Expand All @@ -851,13 +853,15 @@ 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 + '}')
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 = []
Expand All @@ -871,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
Expand Down
110 changes: 110 additions & 0 deletions HARK/tests/test_core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""
This file implements unit tests for interpolation methods
"""
from HARK.core import HARKobject, distanceMetric, AgentType

import numpy as np
import unittest


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):
# 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_b), 0.0)


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, 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)
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))