def new_values(self): """ Helper method that allows a user to enter new values for an existing object and automatically runs those values through the saved formulas. """ variable_dict = {} for variable_key in self.inputs.keys(): print(f"Please enter new value for {variable_key}") variable_value = float(input(f"{variable_key} = ")) globals()[variable_key] = Variable(variable_key, variable_value) variable_dict[variable_key] = Variable(variable_key, variable_value) print("Thank you, we recorded these values:") for key, value in variable_dict.items(): print(f"{key} = {value}") functions = [eval(formula) for formula in self.formulas] inputs = {} for key, value in variable_dict.items(): inputs[key] = value.val self.inputs = inputs self.outputs = [function.val for function in functions] self.derivatives = [function.der for function in functions] self.second_derivatives = [function.der2 for function in functions] if len(self.formulas) == 1: print( f"The final derivative of formula {self.formulas} with your chosen values is {self.derivatives}. Thanks for playing!" ) else: print( f"The final derivatives of formulas {self.formulas} with your chosen values are {self.derivatives}. Thanks for playing!" )
def test_eq_results(): x1 = Variable('x', 1) x2 = Variable('y', 1) x3 = 1 assert (x1 == x2) assert (x1 == x3) assert (not (x1 != x2)) assert (not (x1 != x3))
def test_le_results(): x1 = Variable('x', 2) x2 = Variable('y', 2) x3 = 2 assert (x1 <= x2) assert (x1 <= x3) assert (not (x1 < x2)) assert (not (x1 < x3))
def test_gt_results(): x1 = Variable('x', 4) x2 = Variable('y', 2) x3 = 2 assert (x1 > x2) assert (x1 > x3) assert (not (x1 < x2)) assert (not (x1 < x3))
def test_ge_results(): x1 = Variable('x', 4) x2 = Variable('y', 4) x3 = 4 assert (x1 >= x2) assert (x1 >= x3) assert (not (x1 > x2)) assert (not (x1 > x3))
def test_exp_result_var(): val = 1 x = Variable('x', val) obj_generate = elem.exp(x) obj_wanted = Variable('x', np.exp(val), np.exp(val), np.exp(val)) assert np.isclose(obj_generate.val, obj_wanted.val) ## ensure same element assert list(obj_generate.der) == list(obj_wanted.der) ## ensure same value assert sum( list( map( lambda x: not np.isclose(obj_generate.der[x], obj_wanted.der[x] ), obj_generate.der))) == 0
def test_arctan_result_var(): val = 1 x = Variable('x', val) obj_generate = elem.arctan(x) obj_wanted = Variable('x', np.arctan(val), 1 / (val**2 + 1), -2 * val / (val**2 + 1)**2) assert np.isclose(obj_generate.val, obj_wanted.val) ## ensure same element assert list(obj_generate.der) == list(obj_wanted.der) ## ensure same value assert sum( list( map( lambda x: not np.isclose(obj_generate.der[x], obj_wanted.der[x] ), obj_generate.der))) == 0
def test_log_result_var(): val = 1 x = Variable('x', val) obj_generate = elem.log(x) obj_wanted = Variable(name='x', value=np.log(val), derivative=1 / (val), second_derivative=-1 / (val)**2) assert np.isclose(obj_generate.val, obj_wanted.val) assert list(obj_generate.der) == list(obj_wanted.der) assert sum( list( map( lambda x: not np.isclose(obj_generate.der[x], obj_wanted.der[x] ), obj_generate.der))) == 0
def test_sqrt_result_var(): val = 4 x = Variable('x', val) obj_generate = elem.sqrt(x) obj_wanted = Variable(name='x', value=np.sqrt(val), derivative=1 / (2 * np.sqrt(x.val)), second_derivative=-1 / (4 * val**(3 / 2))) assert np.isclose(obj_generate.val, obj_wanted.val) assert list(obj_generate.der) == list(obj_wanted.der) assert sum( list( map( lambda x: not np.isclose(obj_generate.der[x], obj_wanted.der[x] ), obj_generate.der))) == 0
def test_arccos_result_var(): val = 0 x = Variable('x', val) obj_generate = elem.arccos(x) obj_wanted = Variable('x', np.arccos(val), -1 / np.sqrt(1 - val**2), -val / (1 - val**2)**(3 / 2)) assert np.isclose(obj_generate.val, obj_wanted.val) ## ensure same element assert list(obj_generate.der) == list(obj_wanted.der) ## ensure same value assert sum( list( map( lambda x: not np.isclose(obj_generate.der[x], obj_wanted.der[x] ), obj_generate.der))) == 0
def test_tan_result_var(): val = 1 x = Variable('x', val) obj_generate = elem.tan(x) obj_wanted = Variable(name='x', value=np.tan(val), derivative=1 / (np.cos(val))**2, second_derivative=2 * np.tan(val) * (1 / np.cos(val))**2) assert np.isclose(obj_generate.val, obj_wanted.val) assert list(obj_generate.der) == list(obj_wanted.der) assert sum( list( map( lambda x: not np.isclose(obj_generate.der[x], obj_wanted.der[x] ), obj_generate.der))) == 0
def arctan(x): """Defines what happens when arctan operations are performed on a Variable() object or a constant value. Includes calculation of first and second derivative. """ try: new_x = Variable(name=x.name, value=np.arctan(x.val), derivative=x.der, second_derivative=x.der2) for key in x.der: new_x.der[key] = 1 / (1 + x.val**2) * x.der.get(key) for key in x.der2: new_x.der2[key] = (x.val**2 + 1) * x.der2.get( key) - 2 * x.val * x.der.get(key)**2 / (x.val**2 + 1)**2 return new_x except AttributeError: # constant return np.arctan(x)
def test_cosh_result_var(): val = 1 x = Variable('x', val) obj_generate = elem.cosh(x) obj_wanted = Variable(name='x', value=np.cosh(val), derivative=np.sinh(val), second_derivative=np.cosh(val)) assert np.isclose(obj_generate.val, obj_wanted.val) ## ensure same element assert list(obj_generate.der) == list(obj_wanted.der) ## ensure same value assert sum( list( map( lambda x: not np.isclose(obj_generate.der[x], obj_wanted.der[x] ), obj_generate.der))) == 0
def sin(x): """Defines what happens when sin operations performed on a Variable() object or a constant value. Includes calculation of first and second derivative. """ try: new_x = Variable(name=x.name, value=np.sin(x.val), derivative=x.der, second_derivative=x.der2) for key in x.der: new_x.der[key] = x.der.get(key) * np.cos(x.val) for key in x.der2: new_x.der2[key] = x.der2.get(key) * np.cos( x.val) - (x.der.get(key))**2 * np.sin(x.val) return new_x except AttributeError: # constant return np.sin(x)
def tan(x): #--> 1/cos^2(x) """Defines what happens when tangent operations performed on a Variable() object or a constant value. Includes calculation of first and second derivative. """ try: new_x = Variable(name=x.name, value=np.tan(x.val), derivative=x.der, second_derivative=x.der2) for key in x.der: new_x.der[key] = x.der.get(key) * (1 / (np.cos(x.val)**2)) for key in x.der2: new_x.der2[key] = (1 / np.cos(x.val))**2 * ( x.der2.get(key) + 2 * (x.der.get(key)**2) * np.tan(x.val)) return new_x except AttributeError: return np.tan(x)
def exp(x): """Defines what happens when exponential operations are performed on a Variable() object or a constant value. Includes calculation of first and second derivative. """ try: new_x = Variable(name=x.name, value=np.exp(x.val), derivative=x.der, second_derivative=x.der2) for key in x.der: new_x.der[key] = x.der.get(key) * new_x.val for key in x.der2: new_x.der2[key] = np.exp( x.val) * (x.der2.get(key) + x.der.get(key)**2) return new_x except AttributeError: # constant return np.exp(x)
def test_add_results(): x1 = Variable('x', 1) x2 = x1 + 4 x3 = 4 + x1 # test __radd__ x3 = -x1 + 5 # test __neg__ x4 = Variable('y', 4) x5 = x1 + x4 assert x2.val == 5 assert x2.der['x'] == 1 assert x2.der2['x'] == 0 assert x3.val == 4 assert x3.der['x'] == -1 assert x3.der2['x'] == 0 assert x5.val == 5 assert x5.der['x'] == 1 assert x5.der['y'] == 1 assert x5.der2['x'] == 0 assert x5.der2['y'] == 0
def cosh(x): """Defines what happens when hyperbolic cosine operations performed on a Variable() object or a constant value. Includes calculation of first and second derivative. """ try: # Create a Variable instance new_x = Variable(name=x.name, value=np.cosh(x.val), derivative=x.der, second_derivative=x.der2) for key in x.der: new_x.der[key] = x.der.get(key) * np.sinh(x.val) for key in x.der2: new_x.der2[key] = x.der2.get(key) * np.sinh( x.val) + x.der.get(key)**2 * np.cosh(x.val) return new_x except AttributeError: # if x = constant return math.cosh(x)
def test_pow_results(): x1 = Variable('x', 2) x2 = x1**2 x3 = 4**x1 # test __rpow__ assert x2.val == 4 assert x2.der['x'] == 4 assert x2.der2['x'] == 2 assert x3.val == 16 assert np.isclose(x3.der['x'], np.log(4) * 4**2) assert np.isclose(x3.der2['x'], 4**2 * (np.log(4)**2))
def log(x, base=math.e): """Defines what happens when log operations are performed on a Variable() object or a constant value. Includes calculation of first and second derivative. """ try: #Ensure input domain valid if x.val <= 0: raise ValueError( 'Cannot evaluate the log of a non-positive number') new_x = Variable(name=x.name, value=math.log(x.val, base), derivative=x.der, second_derivative=x.der2) for key in x.der: new_x.der[key] = x.der.get(key) / (x.val * math.log(base)) for key in x.der2: new_x.der2[ key] = x.val * x.der2.get(key) - x.der.get(key)**2 / x.val**2 return new_x except AttributeError: return math.log(x, base)
def sqrt(x): """Defines what happens when square root operations performed on a Variable() object or a constant value. Includes calculation of first and second derivative. """ try: #Ensure input domain valid if x.val < 0: raise ValueError( 'Cannot evaluate the square root of a negative number') new_x = Variable(name=x.name, value=np.sqrt(x.val), derivative=x.der, second_derivative=x.der2) for key in x.der: new_x.der[key] = x.der.get(key) * (1 / (2 * np.sqrt(x.val))) for key in x.der2: new_x.der2[key] = (2 * x.val * x.der2.get(key) - x.der.get(key)**2) / (4 * x.val**(3 / 2)) return new_x except AttributeError: return np.sqrt(x)
def test_mul_results(): x1 = Variable('x', 5) x2 = 3 * x1 x3 = x1 * x2 x4 = x3 * 5 # test __rmul__ x5 = Variable('y', 4) x6 = x1 * x5 assert x2.val == 15 assert x2.der['x'] == 3 assert x2.der2['x'] == 0 assert x3.val == 75 assert x3.der['x'] == 30 assert x3.der2['x'] == 6 assert x4.val == 375 assert x4.der['x'] == 150 assert x4.der2['x'] == 30 assert x6.val == 20 assert x6.der['x'] == 4 assert x6.der['y'] == 5 assert x6.der2['x'] == 0 assert x6.der2['y'] == 0
def test_div_results(): x1 = Variable('x', 1) x2 = x1 / 4 x3 = x1 / x2 x4 = 2 / x1 # test __rtruediv__ x5 = Variable('y', 1) x6 = x1 / x5 assert x2.val == 1 / 4 assert x2.der['x'] == 1 / 4 assert x2.der2['x'] == 0 assert x3.val == 4 assert x3.der['x'] == 0 assert x3.der2['x'] == 0 assert x4.val == 2 assert x4.der['x'] == -2 assert x4.der2['x'] == 4 assert x6.val == 1 assert x6.der['x'] == 1 assert x6.der['y'] == -1 assert x6.der2['x'] == 0 assert x6.der2['y'] == 2
def test_sub_results(): x1 = Variable('x', 4) x2 = x1 - 3 x3 = x2 - x1 x4 = 5 - x1 # test __rsub__ x5 = Variable('y', 4) x6 = x1 + x5 assert x2.val == 1 assert x2.der['x'] == 1 assert x2.der2['x'] == 0 assert x3.val == -3 assert x3.der['x'] == 0 assert x3.der2['x'] == 0 assert x4.val == 1 assert x4.der['x'] == -1 assert x4.der2['x'] == 0 assert x6.val == 8 assert x6.der['x'] == 1 assert x6.der['y'] == 1 assert x6.der2['x'] == 0 assert x6.der2['y'] == 0
def arcsin(x): """Defines what happens when arcsin operations are performed on a Variable() object or a constant value. Includes calculation of first and second derivative. """ try: if x.val < -1.0 or x.val > 1.0: raise ValueError("input of arcsin should within (-1, 1)") new_x = Variable(name=x.name, value=np.arcsin(x.val), derivative=x.der, second_derivative=x.der2) for key in x.der: new_x.der[key] = 1 / np.sqrt(1 - x.val**2) * x.der.get(key) for key in x.der2: new_x.der2[key] = x.val * x.der.get(key)**2 - ( x.val**2 - 1) * x.der2.get(key) / ((1 - x.val**2)**3 / 2) return new_x except AttributeError: # constant if x < -1.0 or x > 1.0: raise ValueError("input of arcsin should within (-1, 1)") return np.arcsin(x)
def new_formulas(self): """ Helper method that allows a user to enter new formulas for an existing object and automatically runs the saved values through the new formulas. """ for variable_key, variable_value in self.inputs.items(): globals()[variable_key] = Variable(variable_key, variable_value) print( """What new formulas would you like to derive? Please use the same saved variables. You may enter as many formulas as you'd like, either writing them separated by commas or copying a list.""" ) formulas = input() # If a list of formulas is entered if formulas[0] == '[': formulas = formulas[1:-1].split( ',' ) # Saves string of formula minus first/last spots representing [] # If multiple formulas are manually written else: formulas = formulas.split( ',') # Saves string of formula split on comma to make list functions = [eval(formula) for formula in formulas] # Save final outputs to AD class object self.outputs = [function.val for function in functions] self.derivatives = [function.der for function in functions] self.second_derivatives = [function.der2 for function in functions] self.formulas = formulas if len(formulas) == 1: print( f"The final derivative of formula {self.formulas} with your chosen values is {self.derivatives}. Thanks for playing!" ) else: print( f"The final derivatives of formulas {self.formulas} with your chosen values are {self.derivatives}. Thanks for playing!" )
def funct_vect(values): var1 = Variable(name='x', value=values[0]) var2 = Variable(name='y', value=values[1]) f = (1. - var1)**2. + (var2 - var1 * var1)**2. return f
def funct(values): var = Variable(name='x', value=values) f = (var - 5)**2 return f
def test_start_results(): x = Variable('x', 5) assert x.name == 'x' assert x.val == 5 assert x.der['x'] == 1 assert x.der2['x'] == 0
def test_pow_types(): with pytest.raises(TypeError): Variable('x', "hi") / 5 assert Variable('x', 5)**"hi" is None assert "hi"**Variable('x', 5) is None