diff --git a/AD20/.DS_Store b/AD20/.DS_Store index 21b3e92..ab01a2f 100644 Binary files a/AD20/.DS_Store and b/AD20/.DS_Store differ diff --git a/AD20/AD20.egg-info/PKG-INFO b/AD20/AD20.egg-info/PKG-INFO new file mode 100644 index 0000000..e9b54a5 --- /dev/null +++ b/AD20/AD20.egg-info/PKG-INFO @@ -0,0 +1,20 @@ +Metadata-Version: 2.1 +Name: AD20 +Version: 0.0.6 +Summary: Automatic Differentiation package +Home-page: https://github.com/CS207-AD20/cs207-FinalProject +Author: Lindsey Brown, Xinyue Wang, Kevin Yoon +Author-email: +License: UNKNOWN +Description: AD20 package by Group 20: + Lindsey Brown + Xinyue Wang + Kevin Yoon + + For documentation, see https://github.com/CS207-AD20/cs207-FinalProject. + +Platform: UNKNOWN +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: MIT License +Classifier: Operating System :: OS Independent +Description-Content-Type: text/markdown diff --git a/AD20/AD20.egg-info/SOURCES.txt b/AD20/AD20.egg-info/SOURCES.txt new file mode 100644 index 0000000..953356b --- /dev/null +++ b/AD20/AD20.egg-info/SOURCES.txt @@ -0,0 +1,13 @@ +README.md +setup.py +AD20/ADgraph.py +AD20/ADmath.py +AD20/ADnum.py +AD20/__init__.py +AD20.egg-info/PKG-INFO +AD20.egg-info/SOURCES.txt +AD20.egg-info/dependency_links.txt +AD20.egg-info/requires.txt +AD20.egg-info/top_level.txt +Tests/__init__.py +Tests/test_AD20.py \ No newline at end of file diff --git a/AD20/AD20.egg-info/dependency_links.txt b/AD20/AD20.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/AD20/AD20.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/AD20/AD20.egg-info/requires.txt b/AD20/AD20.egg-info/requires.txt new file mode 100644 index 0000000..72e3e6b --- /dev/null +++ b/AD20/AD20.egg-info/requires.txt @@ -0,0 +1,10 @@ +numpy==1.15.2 +pandas==0.23.4 +networkx==2.2 +matplotlib==3.0.2 +pandastable==0.11.0 +scipy==1.1.0 +pytest-timeout==1.2.1 +pytest==3.4.2 +pytest-cov==2.5.1 +pytest-dependency==0.2 diff --git a/AD20/AD20.egg-info/top_level.txt b/AD20/AD20.egg-info/top_level.txt new file mode 100644 index 0000000..56d036e --- /dev/null +++ b/AD20/AD20.egg-info/top_level.txt @@ -0,0 +1,2 @@ +AD20 +Tests diff --git a/AD20/AD20/ADgraph.py b/AD20/AD20/ADgraph.py index 9e3998b..71c4ef0 100644 --- a/AD20/AD20/ADgraph.py +++ b/AD20/AD20/ADgraph.py @@ -1,4 +1,6 @@ import networkx as nx +import matplotlib +matplotlib.use('TkAgg') import matplotlib.pyplot as plt import matplotlib.patches as mpatches import numpy as np diff --git a/AD20/AD20/__pycache__/ADmath.cpython-35.pyc b/AD20/AD20/__pycache__/ADmath.cpython-35.pyc index 1785c31..db88a76 100644 Binary files a/AD20/AD20/__pycache__/ADmath.cpython-35.pyc and b/AD20/AD20/__pycache__/ADmath.cpython-35.pyc differ diff --git a/AD20/build/lib/AD20/ADgraph.py b/AD20/build/lib/AD20/ADgraph.py new file mode 100644 index 0000000..71c4ef0 --- /dev/null +++ b/AD20/build/lib/AD20/ADgraph.py @@ -0,0 +1,228 @@ +import networkx as nx +import matplotlib +matplotlib.use('TkAgg') +import matplotlib.pyplot as plt +import matplotlib.patches as mpatches +import numpy as np +import pandas as pd +from AD20.ADnum import ADnum + + +def gen_graph(y): + """ Function to create a directed graph from an ADnum. + + INPUTS + ====== + y : ADnum + + OUTPUTS + ======= + A networkx digraph + """ + G = nx.DiGraph() + d = y.graph + if len(d)== 0: + G.add_node(y) + for key in d: + G.add_node(key) + neighbors = d[key] + for neighbor in neighbors: + G.add_edge(key, neighbor[0], label = neighbor[1]) + return G + +def reverse_graph(y): + """ Function to create a dictionary containing edges of y reversed. + + INPUTS + ====== + y : ADnum + + OUTPUTS + ======= + A dictionary + """ + d = y.graph + parents = {} + for key in d: + neighbors = d[key] + for neighbor in neighbors: + if neighbor[0] not in parents: + parents[neighbor[0]] = [] + parents[neighbor[0]].append((key, neighbor[1])) + return parents + +def get_labels(y): + """ Function to generate labels for plotting networkx graph. + + INPUTS + ====== + y : ADnum + + OUTPUTS + ======= + A dictionary of ADnum objects mapped to string labels + """ + parents = reverse_graph(y) + total = len(y.graph) - sum([entry.constant for entry in y.graph.keys()]) + new_names = {} + nodes = [y] + while len(nodes)>0: + node = nodes.pop() + if node not in new_names: + if node.constant: + new_names[node] = str(np.round(node.val, decimals=1)) + else: + new_names[node] = 'X' + str(total) + total = total - 1 + if node in parents: + neighbors = parents[node] + for neighbor in neighbors: + nodes.append(neighbor[0]) + return new_names + +def get_colors(G, y): + """ Function to assign colors to nodes in the graph. + + INPUTS + ====== + G : networkx digraph + y : ADnum + + OUTPUTS + ======= + A list of colors for the graph + """ + colors = [] + parents = reverse_graph(y) + for node in G: + if node.constant: + colors.append('blue') + else: + if node == y: + colors.append('green') + else: + if node in parents: + colors.append('red') + else: + colors.append('magenta') + return colors + +def get_sizes(G, y, labs): + """ Function to assign sizes to nodes in the graph. + + INPUTS + ====== + G : networkx digraph + y : ADnum + labs : dictionary of graph labels + + OUTPUTS + ======= + A list of sizes for the graph + """ + sizes = [] + for node in G: + label = labs[node] + sizes.append(len(label)*200) + return sizes + +def draw_graph(y): + """ Function to draw the graph. + + INPUTS + ====== + y : ADnum + + OUTPUTS + ======= + A plot of the graph + """ + fig = plt.figure() + G = gen_graph(y) + edge_labs = nx.get_edge_attributes(G, 'label') + pos = nx.spring_layout(G) + labs = get_labels(y) + nx.draw_networkx(G, pos, labels = labs, node_color = get_colors(G, y), node_size = get_sizes(G, y, labs), font_color= 'white') + nx.draw_networkx_edge_labels(G, pos, edge_labels = edge_labs) + limits = plt.axis('off') + mag_patch = mpatches.Patch(color = 'magenta', label = 'input') + red_patch = mpatches.Patch(color = 'red', label = 'intermediate') + blue_patch = mpatches.Patch(color = 'blue', label = 'constant') + green_patch = mpatches.Patch(color = 'green', label = 'output') + plt.legend(handles = [mag_patch, red_patch, blue_patch, green_patch]) + plt.show() + return fig + +def gen_table(y): + """ Function to generate tables for the ADnum. + + INPUTS + ====== + y : ADnum + + OUTPUTS + ======= + A pandas data frame of the computational traces + """ + parents = reverse_graph(y) + labs = get_labels(y) + visited = [] + data = {} + data['Trace'] = [] + data['Operation']=[] + data['Value']= [] + data['Derivative']=[] + nodes = [y] + while len(nodes)>0: + node = nodes.pop() + if node not in visited: + if node.constant: + visited.append(node) + else: + visited.append(node) + data['Trace'].append(labs[node]) + data['Value'].append(node.val) + data['Derivative'].append(node.der) + if node in parents: + if len(parents[node]) == 1: + link = parents[node][0][1]+'('+labs[parents[node][0][0]]+')' + else: + link = parents[node][0][1]+'(' +labs[parents[node][0][0]]+ ' , ' + labs[parents[node][1][0]] + ')' + neighbors = parents[node] + for neighbor in neighbors: + nodes.append(neighbor[0]) + else: + link = 'input' + data['Operation'].append(link) + result = pd.DataFrame.from_dict(data) + result2 = result.sort_values('Trace') + resultorder = result2[['Trace', 'Operation', 'Value', 'Derivative']] + return resultorder + +def plot_ADnum(x, xmin = -10, xmax = 10): + '''Function to plot f and its derivative for single variable input + + INPUTS + ====== + x : ADnum + xmin : starting value of input + xmax : ending value of input + + OUTPUTS + ======= + A plot of x evaluated from xmin to xmax and its derivative + ''' + vals = np.linspace(xmin, xmax, 100) + evals = [x(ADnum(value, der=1)).val for value in vals] + ders = [x(ADnum(value, der=1)).der for value in vals] + fig = plt.figure() + plt.plot(vals, evals, label = 'f', linewidth = 2) + plt.plot(vals, ders, label = 'df/dx', linewidth = 2) + plt.legend(fontsize = 20) + plt.xlabel('x', fontsize = 20) + plt.ylabel('f', fontsize = 20) + plt.xticks(fontsize = 12) + plt.yticks(fontsize = 12) + return fig + + diff --git a/AD20/build/lib/AD20/ADmath.py b/AD20/build/lib/AD20/ADmath.py new file mode 100644 index 0000000..451d43c --- /dev/null +++ b/AD20/build/lib/AD20/ADmath.py @@ -0,0 +1,179 @@ +"""Module for performing special functions on ADnum objects. +Take an ADnum object as input and return an ADnum object as output. +For real number inputs, returns a real number. +""" +import numpy as np +from AD20.ADnum import ADnum + +#TRIGONOMETRIC FUNCTIONS +def sin(X): + try: + y = ADnum(np.sin(X.val), der = np.cos(X.val)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'sin')) + return y + except AttributeError: + return np.sin(X) + +def cos(X): + try: + y = ADnum(np.cos(X.val), der = -np.sin(X.val)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'cos')) + return y + except AttributeError: + return np.cos(X) + +def tan(X): + try: + y = ADnum(np.tan(X.val), der = (1/np.cos(X.val)**2)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'tan')) + return y + except AttributeError: + return np.tan(X) + +def csc(X): + try: + y = ADnum(1/np.sin(X.val), der = (-1/np.tan(X.val))*(1/np.sin(X.val))*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'csc')) + return y + except: + return 1/np.sin(X) + +def sec(X): + try: + y = ADnum(1/np.cos(X.val), der = np.tan(X.val)/np.cos(X.val)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'sec')) + return y + except AttributeError: + return 1/np.cos(X) + +def cot(X): + try: + y = ADnum(1/np.tan(X.val), der = -1/(np.sin(X.val)**2)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'cot')) + return y + except AttributeError: + return 1/np.tan(X) + +#INVERSE TRIGONOMETRIC FUNCTIONS +def arcsin(X): + try: + y = ADnum(np.arcsin(X.val), der = 1/np.sqrt(1-X.val**2)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'arcsin')) + return y + except AttributeError: + return np.arcsin(X) + +def arccos(X): + try: + y = ADnum(np.arccos(X.val), der = -1/np.sqrt(1-X.val**2)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'arccos')) + return y + except AttributeError: + return np.arccos(X) + +def arctan(X): + try: + y = ADnum(np.arctan(X.val), der = 1/(1+X.val**2)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'arctan')) + return y + except AttributeError: + return np.arctan(X) + +#HYPERBOLIC TRIG FUNCTIONS +def sinh(X): + try: + y = ADnum(np.sinh(X.val), der = np.cosh(X.val)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'sinh')) + return y + except AttributeError: + return np.sinh(X) + +def cosh(X): + try: + y = ADnum(np.cosh(X.val), der = np.sinh(X.val)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'cosh')) + return y + except AttributeError: + return np.cosh(X) + +def tanh(X): + try: + y = ADnum(np.tanh(X.val), der = 1/(np.cosh(X.val)**2)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'tanh')) + return y + except AttributeError: + return np.tanh(X) + +#NATURAL EXPONENTIAL AND NATURAL LOGARITHM +def exp(X): + try: + y = ADnum(np.exp(X.val), der = np.exp(X.val)*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'exp')) + return y + except AttributeError: + return np.exp(X) + +def log(X): + try: + y = ADnum(np.log(X.val), der = 1/X.val*X.der) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'log')) + return y + except AttributeError: + return np.log(X) + +def logistic(X): + return 1/(1+exp(-1*X)) + + +def sqrt(X): + try: + y = ADnum(np.sqrt(X.val), der = X.der/(2*np.sqrt(X.val))) + y.graph = X.graph + if X not in y.graph: + y.graph[X] = [] + y.graph[X].append((y, 'sqrt')) + return y + except AttributeError: + return np.sqrt(X) diff --git a/AD20/build/lib/AD20/ADnum.py b/AD20/build/lib/AD20/ADnum.py new file mode 100644 index 0000000..b3be551 --- /dev/null +++ b/AD20/build/lib/AD20/ADnum.py @@ -0,0 +1,198 @@ +"""Module to wrap numbers into ADnum object and do basic calculations. +Take value and specified derivative as given, wrap up as ADnum object, and return ADnum object for each basic calculation function. +""" +import numpy as np +class ADnum: + """ Class to create ADnum objects on which to perform differentiation. + + ATTRIBUTES + ========== + val : scalar for scalar valued quantities or numpy array for vector valued functions, the value of the ADnum object for a set input value + der : scalar for sclar functions of a single variable or numpy array for functions of multiple variables the derivative + graph : dictionary containing the edges of the computational graph + constant : 0 or 1 indicating whether the ADnum object is constant + + METHODS + ======= + This class overloads the methods for basic arithmetic operations. + + EXAMPLES + ======== + # >>> x = ADnum(2, der = 1) + # >>> f = 2*x+3 + # >>> print(f.val) + # 7.0 + # >>> print(f.der) + # 2.0 + """ + def __init__(self, value, **kwargs): + try: + scalarinput = (isinstance(value, int) or isinstance(value, float)) + value = np.array(value) + value = value.astype(float) + if 'der' not in kwargs: + try: + ins = kwargs['ins'] + ind = kwargs['ind'] + if scalarinput: + der = np.zeros(ins) + der[ind] = 1.0 + else: + if ins>1: + der = np.zeros((ins, len(value))) + der[ind, :] = 1.0 #np.ones(len(value)) + else: + der = np.ones(len(value)) + except: + raise KeyError('Must provide ins and ind if der not provided.') + else: + der = kwargs['der'] + der = np.array(der) + der = der.astype(float) + if 'ins' in kwargs: + ins = kwargs['ins'] + if len(der) != ins: + raise ValueError('Shape of derivative does not match number of inputs.') + except: + raise ValueError('Value and derivative of ADnum object must be numeric.') + self.val = value + self.der = der + if 'graph' not in kwargs: + self.graph = {} + else: + self.graph = kwargs['graph'] + if 'constant' not in kwargs: + self.constant = 0 + else: + self.constant = kwargs['constant'] + + def __mul__(self,other): + try: + graph = merge_dicts(self.graph, other.graph) + y = ADnum(self.val*other.val, der = self.val*other.der+self.der*other.val) + y.graph = graph + if self not in y.graph: + y.graph[self] = [] + y.graph[self].append((y, 'multiply')) + if other not in y.graph: + y.graph[other] = [] + y.graph[other].append((y, 'multiply')) + return y + except AttributeError: + other = ADnum(other*np.ones(np.shape(self.val)), der = np.zeros(np.shape(self.der)), constant = 1) + return self*other + + def __rmul__(self,other): + return self.__mul__(other) + + def __add__(self,other): + try: + graph = merge_dicts(self.graph, other.graph) + y = ADnum(self.val+other.val, der = self.der+other.der) + y.graph = graph + if self not in y.graph: + y.graph[self] = [] + y.graph[self].append((y, 'add')) + if other not in y.graph: + y.graph[other] = [] + y.graph[other].append((y, 'add')) + return y + except AttributeError: + other = ADnum(other*np.ones(np.shape(self.val)), der = np.zeros(np.shape(self.der)), constant = 1) + return self + other + + def __radd__(self,other): + return self.__add__(other) + + def __sub__(self,other): + try: + graph = merge_dicts(self.graph, other.graph) + y = ADnum(self.val-other.val,der = self.der-other.der) + y.graph = graph + if self not in y.graph: + y.graph[self] = [] + y.graph[self].append((y, 'subtract')) + if other not in y.graph: + y.graph[other] = [] + y.graph[other].append((y, 'subtract')) + return y + except AttributeError: + other = ADnum(other*np.ones(np.shape(self.val)), der = np.zeros(np.shape(self.der)), constant = 1) + return self-other + + def __rsub__(self, other): + try: + return ADnum(other.val-self.val, der = other.der-self.der) + except AttributeError: + other = ADnum(other*np.ones(np.shape(self.val)), der = np.zeros(np.shape(self.der)), constant = 1) + return other-self + + def __truediv__(self, other): + try: + graph = merge_dicts(self.graph, other.graph) + y = ADnum(self.val/other.val, der = (other.val*self.der-self.val*other.der)/(other.val**2)) + y.graph = graph + if self not in y.graph: + y.graph[self] = [] + y.graph[self].append((y, 'divide')) + if other not in y.graph: + y.graph[other] = [] + y.graph[other].append((y, 'divide')) + return y + except AttributeError: + other = ADnum(other*np.ones(np.shape(self.val)), der = np.zeros(np.shape(self.der)), constant = 1) + return self/other + + def __rtruediv__(self, other): + try: + return ADnum(other.val/self.val, der = (self.val*other.der-other.val*self.der)/(self.val**2)) + except AttributeError: + other = ADnum(other*np.ones(np.shape(self.val)), der = np.zeros(np.shape(self.der)), constant = 1) + return other/self + + def __pow__(self, other, modulo=None): + try: + graph = merge_dicts(self.graph, other.graph) + if self.val == 0: + y = ADnum(self.val**other.val, der = other.val*(self.val**(other.val-1))*self.der+(self.val**other.val)) + else: + y = ADnum(self.val**other.val, der = other.val*(self.val**(other.val-1))*self.der+(self.val**other.val)*np.log(np.abs(self.val))*other.der) + y.graph = graph + if self not in y.graph: + y.graph[self] = [] + y.graph[self].append((y, 'pow')) + if other not in y.graph: + y.graph[other] = [] + y.graph[other].append((y, 'pow')) + return y + except AttributeError: + other = ADnum(other*np.ones(np.shape(self.val)), der = np.zeros(np.shape(self.der)), constant = 1) + return self**other + + def __rpow__(self, other): + try: + return ADnum(other.val**self.val, der = self.val*(other.val**(self.val-1))*other.der+(other.val**self.val)*np.log(other.val)*self.der) + except AttributeError: + other = ADnum(other*np.ones(np.shape(self.val)), der = np.zeros(np.shape(self.der)), constant = 1) + return other**self + +def merge_dicts(d1, d2): + ''' Function to merge two dictionaries. + + INPUTS + ====== + d1 : dictionary + d2 : dictionary + + OUTPUTS + ======= + dnew : new dictionary that is a combination of d1 and d2 + + ''' + dnew = d1.copy() + for key in d2: + if key in dnew: + dnew[key] = dnew[key]+d2[key] + else: + dnew[key] = d2[key] + return dnew diff --git a/AD20/build/lib/AD20/__init__.py b/AD20/build/lib/AD20/__init__.py new file mode 100644 index 0000000..2c33c00 --- /dev/null +++ b/AD20/build/lib/AD20/__init__.py @@ -0,0 +1 @@ +name = "AD20" \ No newline at end of file diff --git a/AD20/build/lib/Tests/__init__.py b/AD20/build/lib/Tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/AD20/build/lib/Tests/test_AD20.py b/AD20/build/lib/Tests/test_AD20.py new file mode 100644 index 0000000..3f85271 --- /dev/null +++ b/AD20/build/lib/Tests/test_AD20.py @@ -0,0 +1,380 @@ +import pytest +import AD20 +import numpy as np +import networkx as nx +import matplotlib +import pandas +from AD20.ADnum import ADnum +from AD20 import ADmath as ADmath +from AD20 import ADgraph + +#ADnum unit tests +def test_ADnum_init(): + x = ADnum(100, der = -2) + assert x.val == 100 + assert x.der == -2 + assert len(x.graph)==0 + +def test_ADnum_valtype(): + with pytest.raises(ValueError): + z = ADnum('zoo', der = 1) + +def test_ADnum_dertype(): + with pytest.raises(ValueError): + z = ADnum(3.0, der = 'zebra') + +def test_ADnum_nodernoins(): + with pytest.raises(Exception): + z = ADnum(4, ind = 0) + + +def test_ADnum_nodernoind(): + with pytest.raises(Exception): + z = ADnum(4, ins = 3) + +def test_ADnum_nodernothing(): + with pytest.raises(Exception): + z = ADnum(3) + +def test_ADnum_derconsistent(): + with pytest.raises(ValueError): + z = ADnum(3, der = np.array([1, 3]), ins = 5) + +def test_graphiput(): + z = ADnum(1, der = 1, graph = {'a': 1}) + assert z.graph == {'a':1} + +def test_ADnum_mul(): + x = ADnum(3.0, der = 1) + f = x*2.0 + assert f.val == 6.0 + assert f.der == 2.0 + assert len(f.graph) == 2 + +def test_ADnum_rmul(): + x = ADnum(3.0, der = 1) + f = 2.0*x + assert f.val == 6.0 + assert f.der == 2.0 + assert len(f.graph) == 2 + +def test_ADnum_add(): + x = ADnum(3.0, der = 1) + f = x+2.0 + assert f.val == 5.0 + assert f.der == 1.0 + assert len(f.graph) == 2 + +def test_ADnum_radd(): + x = ADnum(3.0, der = 1) + f = 2.0+x + assert f.val == 5.0 + assert f.der == 1.0 + assert len(f.graph) == 2 + +def test_ADnum_sub(): + x = ADnum(5.0, der = 1) + f = x-2.0 + assert f.val == 3.0 + assert f.der == 1.0 + assert len(f.graph) == 2 + +def test_ADnum_rsub(): + x = ADnum(3.0, der = 1) + f = 5.0-x + assert f.val == 2.0 + assert f.der == -1.0 + assert len(f.graph) == 2 + +def test_ADnum_div(): + x = ADnum(3.0, der = 1) + f = x/1.5 + assert f.val == 2.0 + assert f.der == 1/1.5 + assert len(f.graph) == 2 + +def test_ADnum_rdiv(): + x = ADnum(3.0, der = 1) + f = 6/x + assert f.val == 2.0 + assert f.der == -2/3 + assert len(f.graph) == 2 + +def test_ADnum_pow(): + x = ADnum(3.0, der = 1) + f = x**2.0 + assert f.val == 9.0 + assert f.der == 6.0 + assert len(f.graph) == 2 + +def test_ADnum_rpow(): + x = ADnum(3.0, der = 1) + f = 4**x + assert f.val == 64 + assert f.der == 64*np.log(4.0) + assert len(f.graph) == 2 + +# ADmath unit tests + +def test_ADmath_sin(): + X = ADnum(np.pi, der = 1) + Y = ADmath.sin(X) + assert Y.val == np.sin(np.pi) + assert Y.der == np.cos(np.pi) + assert len(Y.graph) == 1 + +def test_ADmath_cos(): + f = ADmath.cos(ADnum(4, der = 1)) + assert f.val == np.cos(4) + assert f.der == -np.sin(4) + assert len(f.graph) == 1 + +def test_ADmath_tan(): + f = ADmath.tan(ADnum(4, der = 1)) + assert f.val == np.tan(4) + assert f.der == (1/np.cos(4))**2 + assert len(f.graph) == 1 + +def test_ADmath_csc(): + f = ADmath.csc(ADnum(5, der = 1)) + assert f.val == 1/np.sin(5) + assert f.der == (-1/np.tan(5))*(1/np.sin(5)) + assert len(f.graph) == 1 + +def test_ADmath_sec(): + f = ADmath.sec(ADnum(6, der = 1)) + assert f.val == 1/np.cos(6) + assert f.der == np.tan(6)/np.cos(6) + assert len(f.graph) == 1 + +def test_ADmath_cot(): + f = ADmath.cot(ADnum(1, der = 1)) + assert f.val == 1/np.tan(1) + assert f.der == -1/(np.sin(1)**2) + assert len(f.graph) == 1 + +def test_ADmath_arcsin(): + f = ADmath.arcsin(ADnum(.2, der = 1)) + assert f.val == np.arcsin(.2) + assert f.der == 1/(np.sqrt(1-.2**2)) + assert len(f.graph) == 1 + +def test_ADmath_arccos(): + f = ADmath.arccos(ADnum(.3, der = 1)) + assert f.val == np.arccos(.3) + assert f.der == -1/(np.sqrt(1-.3**2)) + assert len(f.graph) == 1 + +def test_ADmath_arctan(): + f = ADmath.arctan(ADnum(1, der = 1)) + assert f.val == np.arctan(1) + assert f.der == .5 + assert len(f.graph) == 1 + +def test_ADmath_sinh(): + f = ADmath.sinh(ADnum(2, der = 1)) + assert f.val == np.sinh(2) + assert f.der == np.cosh(2) + assert len(f.graph) == 1 + +def test_ADmath_cosh(): + f = ADmath.cosh(ADnum(3, der = 1)) + assert f.val == np.cosh(3) + assert f.der == np.sinh(3) + assert len(f.graph) == 1 + +def test_ADmath_tanh(): + f = ADmath.tanh(ADnum(-5, der = 1)) + assert f.val == np.tanh(-5) + assert f.der == 1/(np.cosh(-5)**2) + assert len(f.graph) == 1 + +def test_ADmath_exp(): + f = ADmath.exp(ADnum(-3, der = 1)) + assert f.val == np.exp(-3) + assert f.der == np.exp(-3) + assert len(f.graph) == 1 + +def test_ADmath_log(): + f = ADmath.log(ADnum(72, der = 1)) + assert f.val == np.log(72) + assert f.der == 1/72 + assert len(f.graph) == 1 + +def test_ADmath_logistic(): + f = ADmath.logistic(ADnum(0, der=1)) + assert f.val == .5 + assert f.der == .25 + +def test_ADmath_sqrt(): + f = ADmath.sqrt(ADnum(40, der = 1)) + assert f.val == np.sqrt(40) + assert f.der == 1/(2*np.sqrt(40)) + assert len(f.graph) == 1 + +def test_ADmath_sinr(): + X = np.pi + Y = ADmath.sin(X) + assert Y == np.sin(np.pi) + + +def test_ADmath_cosr(): + f = ADmath.cos(4) + assert f == np.cos(4) + +def test_ADmath_tanr(): + f = ADmath.tan(4) + assert f == np.tan(4) + +def test_ADmath_cscr(): + f = ADmath.csc(5) + assert f == 1/np.sin(5) + +def test_ADmath_secr(): + f = ADmath.sec(6) + assert f == 1/np.cos(6) + +def test_ADmath_cotr(): + f = ADmath.cot(1) + assert f == 1/np.tan(1) + +def test_ADmath_arcsinr(): + f = ADmath.arcsin(.2) + assert f == np.arcsin(.2) + +def test_ADmath_arccosr(): + f = ADmath.arccos(.3) + assert f == np.arccos(.3) + +def test_ADmath_arctanr(): + f = ADmath.arctan(1) + assert f == np.arctan(1) + +def test_ADmath_sinhr(): + f = ADmath.sinh(2) + assert f == np.sinh(2) + +def test_ADmath_coshr(): + f = ADmath.cosh(3) + assert f == np.cosh(3) + +def test_ADmath_tanhr(): + f = ADmath.tanh(-5) + assert f == np.tanh(-5) + +def test_ADmath_expr(): + f = ADmath.exp(-3) + assert f == np.exp(-3) + +def test_ADmath_logr(): + f = ADmath.log(72) + assert f == np.log(72) + +def test_ADmath_sqrtr(): + f = ADmath.sqrt(40) + assert f == np.sqrt(40) + +# More advanced tests +def test_12x(): + x = ADnum(1, der = 1) + f = 1/(1-2*x) + assert f.val == 1/(1-2) + assert f.der == 2/(1-2)**2 + +def test_xex(): + x = ADnum(2, der = 1) + f = x * ADmath.exp(x) + assert f.val == 2.0 * np.exp(2.0) + assert f.der == np.exp(2.0) + 2.0*np.exp(2.0) + +def test_5x2lnx(): + x = ADnum(1, der = 1) + f = 5 * x**2 * ADmath.log(x) + assert f.val == 0.0 + assert f.der == 10 * 1.0 * np.log(1.0) + 5*1.0 + + +def test_sinxcosx(): + x = ADnum(0, der = 1) + f = ADmath.sin(x) * ADmath.cos(x) + assert f.val == np.sin(0) * np.cos(0) + assert f.der == -(np.sin(0) ** 2) + np.cos(0) **2 + +def test_2xe2x(): + x = ADnum(2, der = 1) + f = 2 * x * ADmath.exp(2*x) + assert f.val == 4 * np.exp(4) + assert f.der == 2 * np.exp(4.0) + 8 * np.exp(4) + +def test_multivar(): + x = ADnum(3, ins = 2, ind = 0) + y = ADnum(4, ins = 2, ind= 1) + f = 2 * y + 2*x**2 + assert f.val == 2 * 4 + 2 * 3**2 + assert f.der.all() == np.all(np.array([12, 2])) + +def test_vecinput(): + x = ADnum([1, 2, 3], ins = 1, ind = 0) + assert np.array_equal(x.val, np.array([1., 2., 3.])) + assert np.array_equal(x.der, np.array([1., 1., 1.])) + +def test_vecinput_multi(): + x = ADnum([1, 2, 3], ins =2 , ind =0) + assert np.array_equal(x.der, np.array([[1., 1., 1.],[0., 0., 0.]])) + +#Graph testing +def test_gen_graph(): + d = {'y': [('x', 'test')]} + Y = ADnum(1, der = 1, graph = d) + G= ADgraph.gen_graph(Y) + assert 'y' in G + assert 'x' in G + assert type(G) == nx.classes.digraph.DiGraph + assert G.number_of_nodes()==2 + assert G.number_of_edges()==1 + +def test_reverse_graph(): + d = {'y': [('x', 'test')]} + rd = {'x': [('y', 'test')]} + Y = ADnum(1, der =1, graph = d) + rg = ADgraph.reverse_graph(Y) + assert rd == rg + +def test_get_labels(): + X = ADnum(1, der =1) + Y = ADmath.sin(X)+3 + labs = ADgraph.get_labels(Y) + assert labs[X] == 'X0' + assert labs[Y] == 'X2' + assert len(labs) == 4 + +def test_get_colorsandsizes(): + X = ADnum(1, der =1) + Y = ADmath.sin(X)+3 + labs = ADgraph.get_labels(Y) + G = ADgraph.gen_graph(Y) + cols = ADgraph.get_colors(G, Y) + sizes = ADgraph.get_sizes(G, Y, labs) + assert len(cols)== 4 + assert len(sizes) == 4 + +def test_draw_graph(): + X = ADnum(1, der =1) + Y = ADmath.sin(X)+3 + fig = ADgraph.draw_graph(Y) + assert type(fig) == matplotlib.figure.Figure + +def test_gen_table(): + X = ADnum(1, der =1) + Y = ADmath.sin(X)+3 + dat = ADgraph.gen_table(Y) + assert type(dat) == pandas.core.frame.DataFrame + + +def test_plot_ADnum(): + X = ADnum(1, der =1) + def Y(x): + return ADmath.sin(x) + fig = ADgraph.plot_ADnum(Y) + assert type(fig)==matplotlib.figure.Figure + diff --git a/AD20/dist/AD20-0.0.6-py3-none-any.whl b/AD20/dist/AD20-0.0.6-py3-none-any.whl new file mode 100644 index 0000000..2b4d806 Binary files /dev/null and b/AD20/dist/AD20-0.0.6-py3-none-any.whl differ diff --git a/AD20/dist/AD20-0.0.6.tar.gz b/AD20/dist/AD20-0.0.6.tar.gz new file mode 100644 index 0000000..c062665 Binary files /dev/null and b/AD20/dist/AD20-0.0.6.tar.gz differ diff --git a/AD20/setup.py b/AD20/setup.py index 818487a..8022dc7 100644 --- a/AD20/setup.py +++ b/AD20/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="AD20", - version="0.0.3", + version="0.0.6", author="Lindsey Brown, Xinyue Wang, Kevin Yoon", author_email=" ", description="Automatic Differentiation package", @@ -13,6 +13,17 @@ long_description_content_type="text/markdown", url="https://github.com/CS207-AD20/cs207-FinalProject", packages=setuptools.find_packages(), + install_requires=['numpy==1.15.2', + 'pandas==0.23.4', + 'networkx==2.2', + 'matplotlib==3.0.2', + 'pandastable==0.11.0', + 'scipy==1.1.0', + 'pytest-timeout==1.2.1', + 'pytest==3.4.2', + 'pytest-cov==2.5.1', + 'pytest-dependency==0.2' + ], classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License",