I've been playing around with sympy and decided to make an arbitrary equations solver since my finance class was getting a little dreary. I wrote a basic framework and started playing with some examples, but some work and some don't for some reason.
from sympy import *
import sympy.mpmath as const
OUT_OF_BOUNDS = "Integer out of bounds."
INVALID_INTEGER = "Invalid Integer."
INVALID_FLOAT = "Invalid Float."
CANT_SOLVE_VARIABLES = "Unable to Solve for More than One Variable."
CANT_SOLVE_DONE = "Already Solved. Nothing to do."
# time value of money equation: FV = PV(1 + i)**n
# FV = future value
# PV = present value
# i = growth rate per perioid
# n = number of periods
FV, PV, i, n = symbols('FV PV i n')
time_value_money_discrete = Eq(FV, PV*(1+i)**n)
time_value_money_continuous = Eq(FV, PV*const.e**(i*n))
def get_sym_num(prompt, fail_prompt):
while(True):
try:
s = input(prompt)
if s == "":
return None
f = sympify(s)
return f
except:
print(fail_prompt)
continue
equations_supported = [['Time Value of Money (discrete)', [FV, PV, i, n], time_value_money_discrete],
['Time Value of Money (continuous)',[FV, PV, i, n], time_value_money_continuous]]
EQUATION_NAME = 0
EQUATION_PARAMS = 1
EQUATION_EXPR = 2
if __name__ == "__main__":
while(True):
print()
for i, v in enumerate(equations_supported):
print("{}: {}".format(i, v[EQUATION_NAME]))
try:
process = input("What equation do you want to solve? ")
if process == "" or process == "exit":
break
process = int(process)
except:
print(INVALID_INTEGER)
continue
if process < 0 or process >= len(equations_supported):
print(OUT_OF_BOUNDS)
continue
params = [None]*len(equations_supported[process][EQUATION_PARAMS])
for i, p in enumerate(equations_supported[process][EQUATION_PARAMS]):
params[i] = get_sym_num("What is {}? ".format(p), INVALID_FLOAT)
if params.count(None) > 1:
print(CANT_SOLVE_VARIABLES)
continue
if params.count(None) == 0:
print(CANT_SOLVE_DONE)
continue
curr_expr = equations_supported[process][EQUATION_EXPR]
for i, p in enumerate(params):
if p != None:
curr_expr = curr_expr.subs(equations_supported[process][EQUATION_PARAMS][i], params[i])
print(solve(curr_expr, equations_supported[process][EQUATION_PARAMS][params.index(None)]))
This is the code I have so far. I guess I can strip it down to a basic example if need be, but I was also wondering if there was a better way to implement this sort of system. After I have this down, I want to be able to add arbitrary equations and solve them after inputting all but one parameter.
For example, if I put in (for equation 0), FV = 1000, PV = 500, i = .02, n is empty I get 35.0027887811465 which is the correct answer. If I redo it and change FV to 4000, it returns an empty list as the answer.
Another example, when I input an FV, PV, and an n, the program seems to hang. When I input small numbers, I got RootOf() answers instead of a simple decimal.
Can anyone help me?
Side note: I'm using SymPy 0.7.6 and Python 3.5.1 which I'm pretty sure are the latest
This is a floating point accuracy issue. solve by default plugs solutions into the original equation and evaluates them (using floating point arithmetic) in order to sort out false solutions. You can disable this by setting check=False. For example, for Hugh Bothwell's code
for fv in range(1870, 1875, 1):
sols = sp.solve(eq.subs({FV:fv}), check=False)
print("{}: {}".format(fv, sols))
which gives
1870: [66.6116466112007]
1871: [66.6386438584579]
1872: [66.6656266802551]
1873: [66.6925950919998]
1874: [66.7195491090752]
I don't have an answer, but I do have a much simpler demonstration case ;-)
import sympy as sp
FV, n = sp.symbols("FV n")
eq = sp.Eq(FV, sp.S("500 * 1.02 ** n"))
# see where it breaks
for fv in range(1870, 1875, 1):
sols = sp.solve(eq.subs({FV:fv}))
print("{}: {}".format(fv, sols))
which produces
1870: [66.6116466112007]
1871: [66.6386438584579]
1872: []
1873: []
1874: []
At a guess this is where the accuracy breaks down enough that it can't find a verifiable solution for n?
Also, while poking at this I did a fairly extensive rewrite which you may find useful. It does pretty much the same as your code but in a much more loosely-coupled fashion.
import sympy as sp
class Equation:
def __init__(self, label, equality_str, eq="=="):
self.label = label
# parse the equality
lhs, rhs = equality_str.split(eq)
self.equality = sp.Eq(sp.sympify(lhs), sp.sympify(rhs))
# index free variables by name
self.vars = {var.name: var for var in self.equality.free_symbols}
def prompt_for_values(self):
# show variables to be entered
var_names = sorted(self.vars, key=str.lower)
print("\nFree variables are: " + ", ".join(var_names))
print("Enter a value for all but one (press Enter to skip):")
# prompt for values by name
var_values = {}
for name in var_names:
value = input("Value of {}: ".format(name)).strip()
if value:
var_values[name] = sp.sympify(value)
# convert names to Sympy variable references
return {self.vars[name]:value for name,value in var_values.items()}
def solve(self):
values = self.prompt_for_values()
solutions = sp.solve(self.equality.subs(values))
# remove complex answers
solutions = [sol.evalf() for sol in solutions if sol.is_real]
return solutions
def __str__(self):
return str(self.equality)
# Define some equations!
equations = [
Equation("Time value of money (discrete)", "FV == PV * (1 + i) ** n"),
Equation("Time value of money (continuous)", "FV == PV * exp(i * n)" )
]
# Create menu
menu_lo = 1
menu_hi = len(equations) + 1
menu_prompt = "\n".join(
[""]
+ ["{}: {}".format(i, eq.label) for i, eq in enumerate(equations, 1)]
+ ["{}: Exit".format(menu_hi)]
+ ["? "]
)
def get_int(prompt, lo=None, hi=None):
while True:
try:
value = int(input(prompt))
if (lo is None or lo <= value) and (hi is None or value <= hi):
return value
except ValueError:
pass
def main():
while True:
choice = get_int(menu_prompt, menu_lo, menu_hi)
if choice == menu_hi:
print("Goodbye!")
break
else:
solutions = equations[choice - 1].solve()
num = len(solutions)
if num == 0:
print("No solutions found")
elif num == 1:
print("1 solution found: " + str(solutions[0]))
else:
print("{} solutions found:".format(num))
for sol in solutions:
print(sol)
if __name__ == "__main__":
main()
Related
I have been trying endlessly to figure out why the variable c in the program below is not been returned. Rather, I was getting an error that says NameError: name 'c' is not defined. I have searched for solutions online, including here, but no answer offered a desirable solution yet. How can I print the content of *c *? Any assistance from you will be much appreciated. Thanks.
# obs=observation, p=model data, s=projected data
def bias_correction(obs, p, s, method='delta', nbins=10, extrapolate=None):
if (method == 'eqm') and (nbins > 1):
binmid = np.arange((1./nbins)*0.5, 1., 1./nbins)
qo = mquantiles(obs[np.isfinite(obs)], prob=binmid)
qp = mquantiles(p[np.isfinite(p)], prob=binmid)
p2o = interp1d(qp, qo, kind='linear', bounds_error=False)
c = p2o(s)
if extrapolate is None:
c[s > np.max(qp)] = qo[-1]
c[s < np.min(qp)] = qo[0]
elif extrapolate == 'constant':
c[s > np.max(qp)] = s[s > np.max(qp)] + qo[-1] - qp[-1]
c[s < np.min(qp)] = s[s < np.min(qp)] + qo[0] - qp[0]
elif method == 'delta':
c = obs + (np.nanmean(s) - np.nanmean(p))
elif method == 'scaling_add':
c = s - np.nanmean(p) + np.nanmean(obs)
elif method == 'scaling_multi':
c = (s/np.nanmean(p)) * np.nanmean(obs)
else:
raise ValueError("incorrect method, choose from 'delta', 'scaling_add', 'scaling_multi' or 'eqm'")
return c # c is the bias-corrected series for s
Here is the image of the full error message.
Thank you all for your kind responses. Fortunately, a friend just helped me figure it out now, and I taught I should share it.
C is from a function, so it is an output term of the function. It isn't a standalone variable.So I need to run the function to produce the output
Here is what I ran that produced my desired output.
obs = ref_sliced.tmp
p = mod_interp.tmp
s = future_interp.tmp
bias_correction(obs, p, s, method='delta',
nbins=10, extrapolate=None)
I am trying to find a efficient solution for the 3n + 1 problem on uvaonlinejudge. The code I have uses memoization using a dictionary. Can anyone suggest an improvement(s) that will help with the execution time of this code? At the moment I am getting a 'Time limit Exceeded' error when I submit the code. If anyone has a working solution to the problem please share it with me. PLEASE DON'T mark this post as DUPLICATE. I have already seen this post and others on stackoverflow but they don't answer the question posted here. My code is as below:
import sys
def recCycleLength(n,cycLenDict):
if n==1:
return 1
if n not in cycLenDict:
if n%2==0:
cycLen = recCycleLength(n//2, cycLenDict)
cycLenDict[n] = cycLen + 1
return cycLen+1
else:
cycLen = recCycleLength(3*n+1, cycLenDict)
cycLenDict[n] = cycLen + 1
return cycLen+1
else:
return cycLenDict[n]
def maxCycle(a, b):
i = a
mydict = {}
maxLength = 1
while i <= b:
m = recCycleLength(i, mydict)
if m > maxLength:
maxLength = m
i = i + 1
return maxLength
for line in sys.stdin:
curr_line=line.split()
num1 = int(curr_line[0])
num2 = int(curr_line[1])
if num1>num2:
num1, num2 = num2, num1
m = maxCycle(num1, num2)
print("{} {} {}".format(num1, num2, m))
I found the problem in your code. Actually, you are not saving cycLenDict generated on the previous interval for next one. And this is why your code is so "slow" because it will generate all possible endings over and over again. Just move it in global scope or make something like this:
import sys
def rec(n, cache):
if n in cache:
return cache[n]
if n % 2 == 0:
cycle = rec(n//2, cache)
else:
cycle = rec(3*n+1, cache)
cache[n] = cycle + 1
return cache[n]
def cycle(a, b, cache):
return max(rec(i, cache) for i in range(a, b+1))
if __name__ == '__main__':
cache = {1: 1}
for line in sys.stdin:
a, b = map(int, line.split())
a, b = min(a, b), max(a, b)
m = cycle(a, b, cache)
print("{} {} {}".format(a, b, m))
The code seems to do the right thing to execute maxCycle optimally by caching all calculated results in mydict.
However, the input to the application consists of many pairs of values to be processed and maxCycle will reset mydict = {} and calculate everything from scratch.
I suggest remembering the results globally instead. A simple modification of the original code would be:
cycLenDict = {} # global dictionary
def recCycleLength(n): # no cycLenDict argument
if n==1:
return 1
if n not in cycLenDict:
# ...
def maxCycle(a, b):
# ...
while i <= b:
m = recCycleLength(i) # no myDict argument
To make everything a little bit nicer looking (without any difference performance-wise, compared to the solution above), make a decorator which remembers the results, so that the remainder of the code does not have to take care of that:
def memoize(func):
"""decorate any function which takes positional arguments to cache its results"""
func.results = {} # results are stored globally as the funtion's attribute
def memoized(*a): # memoized version of func
if a not in func.results: # if not cached
func.results[a] = func(*a) # save to cache
return func.results[a] # return from cache
return memoized
#memoize # with this, recCycleLength is called only once for every n value
def recCycleLength(n):
if n==1:
return 1
elif n%2==0:
return recCycleLength(n//2) + 1
else:
return recCycleLength(3*n+1) + 1
def maxCycle(a, b):
return max(recCycleLength(i) for i in range(a, b+1))
import sys
def cycle(current, count=1):
if current == 1:
return count
if current % 2 == 0:
return cycle(current / 2, count + 1)
return cycle(current * 3 + 1, count + 1)
def max_cycles(lower, upper):
max = 0
for current in range(lower, upper):
result = cycle(current, 1)
if result > max:
max = result
return max
def main(i, j):
if i > j:
(i, j) = (j, i)
print (i, j, max_cycles(i, j))
if __name__ == "__main__":
if len(sys.argv) < 3:
print 'usage: python filename argv1 argv2'
print 'exiting'
exit(0)
main(int(sys.argv[1]), int(sys.argv[2]))
I had this working and now I cannot figure out what I did to make it not function the way I intend it to. When the program is called by the pyshell it asks the questions as it is supposed to but when the reply is given this is where it loses it mind. It was working and would call the function I have defined but now it will not do so.
I might just redefine the responses to use numbers instead of strings to make it easier and faster to complete the entries, but at the moment it is not working so there is no point in doing that until I can get it figured out.
The error is in the run_time() section, it is not calling the function as it should be, I might be missing something. I want it to call the function when the proper response is given.
I did try:
k = (query())
f = function_list[k]
f()
This did not worth either, so I am stuck. I appreciate any help.
def f_to_c():
temp = int(input("How warm is it out?"))
f_or_c = str(input("Is it C or F?"))
if f_or_c == 'c' or f_or_c == 'C':
updatedtemp = float(temp * 9 / 5 + 32)
print (updatedtemp, 'F')
elif f_or_c == 'f' or f_or_c == 'F':
updatedtemp = float((temp - 32) * 5 / 9)
print (updatedtemp, 'C')
def calc_trapezoid_area():
height = float(Input('What is the height of the trapezoid?'))
length1 = float(Input('What is the length of the bottom base?'))
length2 = float(Input('What is the length of the top base?'))
formula = float((1 / 2 * (length1 + length2)) * height)
print ("The area of the trapezoid is:", (formula))
def cal_circle():
pi = float(245850922 / 78256779)
rad_or_diam = str(input('Diameter or Radius?'))
width = float(input('What is the %s of your Circle?' % rad_or_diam))
if rad_or_diam == 'r' or rad_or_diam == 'R':
print (float(pi * width ** 2))
elif rad_or_diam == 'd' or rad_or_diam == 'D':
print (float(pi * width))
def query():
query = str(input('What would you like to calculate? Degrees, Trapezoids, or Circles?')).lower
function_list = {
"degrees": f_to_c,
"trapezoids": calc_trapezoid_area,
"circles": cal_circle
}
def run_time():
try:
f = function_list[query()]
f()
except KeyError:
print ("You messed up!")
if __name__ == '__main__':
while True:
run_time()
x = str(input('True or False?'))
if x == 'True':
break
There are two problems with your query function:
It doesn't return anything; and
It doesn't call lower.
Instead, try:
def query():
prompt = 'What would you like to calculate? Degrees, Trapezoids, or Circles?'
return input(prompt).lower()
Note the following:
input already returns a string, no need to call str();
You need to explicitly return the value; and
Parentheses () are required to actually call lower, otherwise you return the method itself.
Also, in f_to_c, "flaot" != "float".
Finally, you could improve your run_time function, minimising the amount of code in the try block:
try:
f = function_list[query()]
except KeyError:
print ("You messed up!")
else:
f()
This prevents a KeyError from f() being accidentally silenced.
I need to code a program that can get values from this
Very interesting question. Actually, in your case, it is very simple. You can parse the entire source string with ast module, like this
import ast
import operator
functions = {
"add": operator.add,
"abs": operator.abs,
"multiply": operator.mul
}
def recursive_evaluation(current_element):
if isinstance(current_element, ast.Module):
return recursive_evaluation(current_element.body[0].value)
elif isinstance(current_element, ast.Call):
function = functions[current_element.func.id]
args = [recursive_evaluation(item) for item in current_element.args]
return function(*args)
elif isinstance(current_element, ast.Num):
return current_element.n
else:
raise ValueError("Unknown Element " + str(current_element))
source = "abs(add(add(9465,38),multiply(add(63303,146),46)))"
print recursive_evaluation(ast.parse(source))
source = "add(1, -2)"
print recursive_evaluation(ast.parse(source))
source = "abs(add(1, -2))"
print recursive_evaluation(ast.parse(source))
Output
2928157
-1
1
Interesting problem, here is a potential solution. No doubt you could make a much more elegant solution using libraries or lambdas etc. as thefourtheye did in their answer, but this seems to work.
I have done a few test cases at the bottom, set the global verbose to True if you want debug info:
# globals
verbose = False # set to True if you want debug info printed
max_iter = 1000 # this stops infinate loops incase the code does not allow for some input
def solve(problem_str):
def multiply(arg_list):
x = 1
for i in arg_list:
x *= i
return x
def find_innermost(x_str):
a, b, c, i = [0], [0], 0, 0
while True:
i += 1
start = a[-1]+1
a.append(x_str.find('(', start)) # find next (
b.append(x_str.find(',', start)) # find next ,
c = x_str.find(')', start) # find next )
if (a[-1] > c) or (a[-1] == -1):
if (b[-2] > a[-3]) and (b[-2] < a[-2]):
return x_str[b[-2]+1:c+1]
else:
return x_str[a[-3]+1:c+1]
if i >= max_iter:
raise Exception("Infinite loop")
def do_sum(x_str):
args = [int(x) for x in x_str[x_str.find('(')+1:x_str.find(')')].split(',')]
task = x_str[:3].lower()
if task == 'add':
return sum(args)
elif task == 'sub':
return args[0] - sum(args[1:])
elif task == 'abs':
return abs(args.pop())
elif task == 'mul':
return multiply(args)
else:
print x_str + ': Task not recognised, please modify program or input'
raise Exception("Invalid input")
i = 0
while True:
i += 1
if verbose: print 'debug: problem_str:', problem_str
if problem_str.count('(') > 1:
x_str = find_innermost(problem_str)
else:
x_str = problem_str
if verbose: print '.'*6, 'x_str:\t', x_str
x = do_sum(x_str)
if verbose: print '.'*6, 'x:\t', x, '\n'
problem_str = problem_str.replace(x_str, str(x))
if problem_str.count('(') == 0:
return int(problem_str)
if i >= max_iter:
raise Exception("Infinite loop")
if __name__ == '__main__':
p1 = 'abs(add(add(9465,38),multiply(add(63303,146),46)))'
p2 = 'abs(add(multiply(95,multiply(-1,multiply(13,18875))),multiply(-1,add(18293,26))))'
p3 = 'abs(add(subtract(add(add(151,26875),122),254),subtract(237,multiply(-1,56497))))'
r1, r2, r3 = solve(p1), solve(p2), solve(p3)
print 'p1 evaluates to:', r1
print 'p2 evaluates to:', r2
print 'p3 evaluates to:', r3
Let me know if you have any questions about the code.
So i wrote this code and it passes the first test case, and fails all the rest. However, I can't seem to find an input that breaks it. Maybe it's because I've been staring at the code too long, but i would appreciate any help.
The algorithm uses two priority queues for the smallest and largest halves of the current list. Here's the code:
#!/bin/python
import heapq
def fix(minset, maxset):
if len(maxset) > len(minset):
item = heapq.heappop(maxset)
heapq.heappush(minset, -item)
elif len(minset) > (len(maxset) + 1):
item = heapq.heappop(minset)
heapq.heappush(maxset, -item)
N = int(raw_input())
s = []
x = []
for i in range(0, N):
tmp = raw_input()
a, b = [xx for xx in tmp.split(' ')]
s.append(a)
x.append(int(b))
minset = []
maxset = []
for i in range(0, N):
wrong = False
if s[i] == "a":
if len(minset) == 0:
heapq.heappush(minset,-x[i])
else:
if x[i] > minset[0]:
heapq.heappush(maxset, x[i])
else:
heapq.heappush(minset, -x[i])
fix(minset, maxset)
elif s[i] == "r":
if -x[i] in minset:
minset.remove(-x[i])
heapq.heapify(minset)
elif x[i] in maxset:
maxset.remove(x[i])
heapq.heapify(maxset)
else:
wrong = True
fix(minset, maxset)
if len(minset) == 0 and len(maxset) == 0:
wrong = True
if wrong == False:
#Calculate median
if len(minset) > len(maxset):
item = - minset[0]
print int(item)
else:
item = ((-float(minset[0])) + float(maxset[0])) / 2
if item.is_integer():
print int(item)
continue
out = str(item)
out.rstrip('0')
print out
else:
print "Wrong!"
Your original was not so legible, so first I made it object-oriented:
MedianHeapq supports methods rebalance(), add(), remove(), size(), median(). We seriously want to hide the members minset,maxset from the client code, for all sorts of sensible reasons: prevent client from swapping them, modifying them etc. If client needs to see them you just write an accessor.
We also added a __str__() method which we will use to debug visually and make your life easier.
Also added legibility changes to avoid the indexing with [i] everywhere, rename s,x arrays to op,val, add prompts on the raw_input(), reject invalid ops at the input stage.
Your actual computation of the median confuses me (when do you want float and when integer? the rstrip('0') is a bit wack), so I rewrote it, change that if you want something else.
A discussion of the algorithm is here.
Now it is legible and self-contained. Also makes it testable.
You might be making sign errors in your code, I don't know, I'll look at that later.
Next we will want to automate it by writing some PyUnit testcases. doctest is also a possibility. TBC.
Ok I think I see a bug in the sloppiness about locating the median. Remember the minset and maxset can have a size mismatch of +/-1. So take more care about precisely where the median is located.
#!/bin/python
import heapq
class MedianHeapq(object):
def __init__(self):
self.minset = []
self.maxset = []
def rebalance(self):
size_imbalance = len(self.maxset) - len(self.minset)
if len(self.maxset) > len(self.minset):
#if size_imbalance > 0:
item = heapq.heappop(self.maxset)
heapq.heappush(self.minset, -item)
#elif size_imbalance < -1:
elif len(self.minset) > (len(self.maxset) + 1):
item = heapq.heappop(self.minset)
heapq.heappush(self.maxset, -item)
def add(self, value, verbose=False):
if len(self.minset) == 0:
heapq.heappush(self.minset,-value)
else:
if value > self.minset[0]:
heapq.heappush(self.maxset, value)
else:
heapq.heappush(self.minset, -value)
self.rebalance()
if verbose: print self.__str__()
return False
def remove(self,value,verbose=False):
wrong = False
if -value in self.minset:
minset.remove(-value)
heapq.heapify(self.minset)
elif value in maxset:
maxset.remove(value)
heapq.heapify(self.maxset)
else:
wrong = True
self.rebalance()
if verbose: print self.__str__()
return wrong
def size(self):
return len(self.minset)+len(self.maxset)
def median(self):
if len(self.minset) > len(self.maxset):
item = - self.minset[0]
return int(item)
else:
item = (-self.minset[0] + self.maxset[0]) / 2.0
# Can't understand the intent of your code here: int, string or float?
if item.is_integer():
return int(item)
# continue # intent???
else:
return item
# The intent of this vv seems to be round floats and return '%.1f' % item ??
#out = str(item)
#out.rstrip('0') # why can't you just int()? or // operator?
#return out
def __str__(self):
return 'Median: %s Minset:%s Maxset:%s' % (self.median(), self.minset,self.maxset)
# Read size and elements from stdin
N = int(raw_input('Size of heap? '))
op = []
val = []
while(len(val)<N):
tmp = raw_input('a/r value : ')
op_, val_ = tmp.split(' ')
if op_ not in ['a','r']: # reject invalid ops
print 'First argument (operation) must be a:Add or r:Remove! '
continue
op.append(op_)
val.append(int(val_))
mhq = MedianHeapq()
for op_,val_ in zip(op,val): # use zip to avoid indexing with [i] everywhere
wrong = False
if op_ == 'a':
wrong = mhq.add(val_)
elif op_ == 'r':
wrong = mhq.remove(val_)
assert (mhq.size()>0), 'Heap has zero size!'
assert (not wrong), 'Heap structure is wrong!'
if not wrong:
print mhq.__str__()