Predict the number of iterations required - iterated weighted average - python

Pardon me but I could find a better title. Please look at this super-simple Python program:
x = start = 1.0
target = 0.1
coeff = 0.999
for c in range(100000):
print('{:5d} {:f}'.format(c, x))
if abs(target - x) < abs((x - start) * 0.01):
break
x = x * coeff + target * (1 - coeff)
Brief explaination: this program moves x towards target calculating iteratively the weighted average of x and target with coeff as weight. It stops when x reaches the 1% of the initial difference.
The number of iterations remains the same no matter the initial value of x and target.
How can I set coeff in order to predict how many iterations will take place?
Thanks a lot.

Let's make this a function, f.
f(0) is the initial value (start, in this case 1.0).
f(x) = f(x - 1) * c + T * (1 - c).
(So f(1) is the next value of x, f(2) is the one after that, and so on. We want to find the value of x where |T - f(x)| < 0.01 * |f(0) - f(x)|)
So let's rewrite f(x) to be linear:
f(x) = f(x - 1) * c + T * (1 - c)
= (f(x - 2) * c + T * (1 - c)) * c + T * (1 - c)
= (f(x - 2) * c ** 2 + T * c * (1 - c)) + T * (1 - c)
= ((f(x - 3) * c + T * (1 - c)) * c ** 2 + T * c * (1 - c)) + T * (1 - c)
= f(x - 3) * c ** 3 + T * c ** 2 * (1 - c) + T * c * (1 - c) + T * (1 - c)
= f(0) * c ** x + T * c ** (x - 1) * (1 - c) + T * c ** (x - 2) * (1 - c) + ... + T * c * (1 - c) + T * (1 - c)
= f(0) * c ** x + (T * (1 - c)) [(sum r = 0 to x - 1) (c ** r)]
# Summation of a geometric series
= f(0) * c ** x + (T * (1 - c)) ((1 - c ** x) / (1 - c))
= f(0) * c ** x + T (1 - c ** x)
So, the nth value of x will be start * c ** n + target * (1 - c ** n).
We want:
|T - f(x)| < 0.01 * |f(0) - f(x)|
|T - f(0) * c ** x - T (1 - c ** x)| < 0.01 * |f(0) - f(0) * c ** x - T (1 - c ** x)|
|(c ** x) * T - (c ** x) f(0)| < 0.01 * |(1 - c ** x) * f(0) - (1 - c ** x) * T|
(c ** x) * |T - f(0)| < 0.01 * (1 - c ** x) * |T - f(0)|
c ** x < 0.01 * (1 - c ** x)
c ** x < 0.01 - 0.01 * c ** x
1.01 * c ** x < 0.01
c ** x < 1 / 101
x < log (1 / 101) / log c
(I somehow ended up with x < when it should be x >, but it gives the correct answer. With c = 0.999, x > 4612.8, and it terminates on step 4613).
In the end, it is independant of start and target.
Also, for a general percentage difference of p,
c ** x > p * (1 - c ** x)
c ** x > p - p c ** x
(1 + p) c ** x > p
c ** x > p / (1 + p)
x > log (p / (1 + p)) / log c
So for a coefficient of c, there will be log (1 / 101) / log c steps.
If you have the number of steps you want, call it I, you have
I = log_c(1 / 101)
c ** I = 1 / 101
c = (1 / 101) ** (1 / I)
So c should be set to the Ith root of 1 / 101.

Your code reduces the distance between x and the target by a factor of coeff in each execution of the loop. Thus, if start is greater than target, we get the formula
target - x = (x - start) * coeff ** c
where c is the number of loops we have done.
Your ending criterion is (again, if start is greater than target),
x - target < (start - x) * 0.01
Solving for x by algebra we get
x > (target + 0.01 * s) / (1 + 0.01)
Substituting that into our first expression and simplifying a bit makes both start and target drop out of the inequality--now you see why those values did not matter--and we get
0.01 / (1 + 0.01) < coeff ** c
Solving for c we get
c > log(0.01 / (1 + 0.01), coeff)
So the final answer for the number of loops is
ceil(log(0.01 / (1 + 0.01), coeff))
or alternatively, if you do not like logarithms to an arbitrary base,
ceil(log(0.01 / (1 + 0.01)) / log(coeff))
You could replace that first logarithm in that last expression with its result, but I left it that way to see what different result you would get if you change the constant in your end criterion away from 0.01.
The result of that expression in your particular case is
4613
which is correct. Note that both the ceil and log function are in Python's math unit, so remember to import those functions before doing that calculation. Also note that Python's floating point calculations are not exact, so your actual number of loops may differ from that by one, if you change the values of coeff or of 0.01.

Related

How to make fsolve function work inside a for loop?

I am writing a function to calculate three nonlinear equations. I have the function to calculate them, but the thing is I give initial guess to them - which works fine. But now I have a constraint for x between (1/4 and 1/3). For that, I am making 2 for lops. One is for ptinting 13 different values, the other one for the constraint.
However the code does not give me the result at all:
import numpy as np
import math
from scipy.optimize import fsolve
def equations(vars):
x, y, z = vars
eq1 = ((x / (1 - x)) - (((2.5*np.cos(z)) / (8 * np.pi * np.sin(z) ** 2)) * (1 + (design_ratio * np.tan(z)))))
eq2 = ((y / (1 + y)) - (2.5 / (8 * np.pi * np.cos(z))) * (1 -design_ratio *( (1 / np.tan(z)))))
eq3 = np.tan(z) - ((1-x) /( 1.40 * (1+y)))
return [eq1, eq2, eq3]
n=13
for i in range(0, n):
for j in range(25555, 33333):
x = 0.00001 *x
x, y, z = fsolve(equations, (0.328, 0.048, 28))
print(x, y, z)
Frankly, using a loop is a terrible approach to handle the box constraint 1/4 <= x <= 1/3. Since fsolve doesn't support (box) constraints, you can rewrite the problem
Solve F(x,y,z) = 0 with 1/4 <= x <= 1/3
as an equivalent minimization problem
min np.sum(F(x,y,z)**2) s.t. 1/4 <= x <= 1/3
and solve it by means of scipy.optimize.minimize like this:
import numpy as np
from scipy.optimize import minimize
def F(vars):
x, y, z = vars
eq1 = ((x / (1 - x)) - (((2.5*np.cos(z)) / (8 * np.pi * np.sin(z) ** 2)) * (1 + (design_ratio * np.tan(z)))))
eq2 = ((y / (1 + y)) - (2.5 / (8 * np.pi * np.cos(z))) * (1 -design_ratio *( (1 / np.tan(z)))))
eq3 = np.tan(z) - ((1-x) /( 1.40 * (1+y)))
return np.array([eq1, eq2, eq3])
bounds = [(1./4, 1./3), (None, None), (None, None)]
res = minimize(lambda vars: np.sum(F(vars)**2), x0=(0.328, 0.048, 28), bounds=bounds)

np.int64 behaves differently from int in math-operations

I have come across a very strange problem where i do a lot of math and the result is inf or nan when my input is of type <class 'numpy.int64'>, but i get the correct (checked analytically) results when my input is of type <class 'int'>. The only library functions i use are np.math.factorial(), np.sum() and np.array(). I also use a generator object to sum over series and the Boltzmann constant from scipy.constants.
My question is essentially this: Are their any known cases where np.int64 objects will behave very differently from int objects?
When i run with np.int64 input, i get the RuntimeWarnings: overflow encountered in long_scalars, divide by zero encountered in double_scalars and invalid value encountered in double_scalars. However, the largest number i plug into the factorial function is 36, and i don't get these warnings when i use int input.
Below is a code that reproduces the behaviour. I was unable to find out more exactly where it comes from.
import numpy as np
import scipy.constants as const
# Some representible numbers
sigma = np.array([1, 2])
sigma12 = 1.5
mole_weights = np.array([10,15])
T = 100
M1, M2 = mole_weights/np.sum(mole_weights)
m0 = np.sum(mole_weights)
fac = np.math.factorial
def summation(start, stop, func, args=None):
#sum over the function func for all ints from start to and including stop, pass 'args' as additional arguments
if args is not None:
return sum(func(i, args) for i in range(start, stop + 1))
else:
return sum(func(i) for i in range(start, stop + 1))
def delta(i, j):
#kronecker delta
if i == j:
return 1
else:
return 0
def w(l, r):
# l,r are ints, return a float
return 0.25 * (2 - ((1 / (l + 1)) * (1 + (-1) ** l))) * np.math.factorial(r + 1)
def omega(ij, l, r):
# l, r are int, ij is and ID, returns float
if ij in (1, 2):
return sigma[ij - 1] ** 2 * np.sqrt(
(np.pi * const.Boltzmann * T) / mole_weights[ij - 1]) * w(l, r)
elif ij in (12, 21):
return 0.5 * sigma12 ** 2 * np.sqrt(
2 * np.pi * const.Boltzmann * T / (m0 * M1 * M2)) * w(l, r)
else:
raise ValueError('(' + str(ij) + ', ' + str(l) + ', ' + str(r) + ') are non-valid arguments for omega.')
def A_prime(p, q, r, l):
'''
p, q, r, l are ints. returns a float
'''
F = (M1 ** 2 + M2 ** 2) / (2 * M1 * M2)
G = (M1 - M2) / M2
def inner(w, args):
i, k = args
return ((8 ** i * fac(p + q - 2 * i - w) * (-1) ** (r + i) * fac(r + 1) * fac(
2 * (p + q + 2 - i - w)) * 2 ** (2 * r) * F ** (i - k) * G ** w) /
(fac(p - i - w) * fac(q - i - w) * fac(r - i) * fac(p + q + 1 - i - r - w) * fac(2 * r + 2) * fac(
p + q + 2 - i - w)
* 4 ** (p + q + 1) * fac(k) * fac(i - k) * fac(w))) * (
2 ** (2 * w - 1) * M1 ** i * M2 ** (p + q - i - w)) * 2 * (
M1 * (p + q + 1 - i - r - w) * delta(k, l) - M2 * (r - i) * delta(k, l - 1))
def sum_w(k, i):
return summation(0, min(p, q, p + q + 1 - r) - i, inner, args=(i, k))
def sum_k(i):
return summation(l - 1, min(l, i), sum_w, args=i)
return summation(l - 1, min(p, q, r, p + q + 1 - r), sum_k)
def H_i(p, q):
'''
p, q are ints. Returns a float
'''
def inner(r, l):
return A_prime(p, q, r, l) * omega(12, l, r)
def sum_r(l):
return summation(l, p + q + 2 - l, inner, args=l)
val = 8 * summation(1, min(p, q) + 1, sum_r)
return val
p, q = np.int64(8), np.int64(8)
print(H_i(p,q)) #nan
print(H_i(int(p) ,int(q))) #1.3480582058153066e-08
Numpy's int64 is a 64-bit integer, meaning it consists of 64 places that are either 0 or 1. Thus the smallest representable value is -2**63 and the biggest one is 2**63 - 1
Python's int is essentially unlimited in length, so it can represent any value. It is equivalent to a BigInteger in Java. It's stored as a list of int64s essentially that are considered a single large number.
What you have here is a classic integer overflow. You mentioned that you "only" plug 36 into the factorial function, but the factorial function grows very fast, and 36! = 3.7e41 > 9.2e18 = 2**63 - 1, so you get a number bigger than you can represent in an int64!
Since int64s are also called longs this is exactly what the warning overflow encountered in long_scalars is trying to tell you!

Python holding back the value from the previous execution

This is a program to find the roots of a quadratic equation, but when i execute the program more than once the values from the previous execution still remain in the list root. How can I clear it?
When I put del root in the function quad(), it gives an error UnboundLocalError: local variable 'root' referenced before assignment. Why?
import math
import cmath
root=[]
def roots(a:int,b:int,c:int):
if ((b**2)-4*a*c)>=0:
x1=(-b+(math.sqrt((b**2)-4*a*c)))/(2*a)
x2=(-b-(math.sqrt((b**2)-4*a*c)))/(2*a)
else:
x1=(-b+cmath.sqrt((b**2)-4*a*c))/(2*a)
x2=(-b-cmath.sqrt((b**2)-4*a*c))/(2*a)
root.append(x1)
root.append(x2)
return root
def quad():
a=int(input("enter the co-efficient of x^2-integer"))
b=int(input("enter the co-efficient of x-integer"))
c=int(input("enter the constant-integer"))
roots(a,b,c)
print(root)
del root
convert root to a local variable,
import math
import cmath
def calculate_roots(a: int, b: int, c: int):
roots = []
if ((b ** 2) - 4 * a * c) >= 0:
x1 = (-b + (math.sqrt((b ** 2) - 4 * a * c))) / (2 * a)
x2 = (-b - (math.sqrt((b ** 2) - 4 * a * c))) / (2 * a)
else:
x1 = (-b + cmath.sqrt((b ** 2) - 4 * a * c)) / (2 * a)
x2 = (-b - cmath.sqrt((b ** 2) - 4 * a * c)) / (2 * a)
roots.append(x1)
roots.append(x2)
return roots
def quad():
a = int(input("enter the co-efficient of x^2-integer"))
b = int(input("enter the co-efficient of x-integer"))
c = int(input("enter the constant-integer"))
roots = calculate_roots(a, b, c)

using log in numpy to solve for the loan repayment formula

I am solving for n in the loan repayment formula at http://financeformulas.net/Loan_Payment_Formula.html
This simplified version only has 1 value for n:
P = (r * PV) / (1 - (1 + r) ** -n)
P = payment
PV = present value
r = rate per period
n = number of periods
I believe I have solved for n:
P = (r * PV) / (1 - (1 + r)**-n)
P * (1 - (1 + r)**-n) = (r * PV)
1 - (1 + r)**-n = (r * PV) / P
1 = (r * PV / P) + (1 + r) ** -n
1 - (r * PV / P) = (1 + r) ** -n
1 - (r * PV / P) = 1 /(1 + r) ** n
((1 + r) ** n) * (1 - (r * PV / P)) = 1
(1 + r) ** n = 1 / (1 - (r * PV / P))
n * log(1 + r) = log(1 / (1 - (r * PV / P)))
n = log(1 / (1 - (r * PV / P))) / log(1 + r) # finally done!
My problem is converting this to python. When I do log(3) on my calculator I get 0.477
When I use numpy I get:
In [1]: import numpy
In [2]: numpy.log(3)
Out[2]: 1.0986122886681098
In [3]: numpy.log10(3)
Out[3]: 0.47712125471966244
Therefore as of now my assumption to translate to python using numpy is:
periods_of_time = numpy.log10(1 / (1 - (r * PV / P))) / numpy.log10(1 + r)
This article shows how to solve for variables that are an exponent...you use log: http://mathonweb.com/help_ebook/html/expolog_4.htm
When I run it as so I get an error:
def get_periods_from_payment(present_value, rate_per_period, payment):
rate_as_decimal = rate_per_period / 100
return log10(1 / (1 - (rate_per_period * present_value / payment))) / log10(1 + rate_per_period)
# test get_interest_paid_from_periods()
result = get_interest_paid_from_periods(35000, 0.5, 180)
print(result)
assert result == 18163
The error is:
$ python interest_calculator.py
295.35
interest_calculator.py:59: RuntimeWarning: invalid value encountered in log10
return log10(1 / (1 - (rate_per_period * present_value / payment))) / log10(1 + rate_per_period)
nan
Traceback (most recent call last):
File "interest_calculator.py", line 101, in <module>
assert result == 180
AssertionError
A similar answer says this means there is a negative value in the exponent, but if I solved correctly it should work.
The log on your calculator is base 10 while log in Python is base e.
Since you're dividing a log by another log of the same base it actually doesn't matter which base you use for your logarithm. This is because you're implicitly using the base change rule to work out the logarithm of 1 / (1 - (r * PV / P)) with base 1+r.

What's wrong with this function to solve cubic equations?

I am using Python 2 and the fairly simple method given in Wikipedia's article "Cubic function". This could also be a problem with the cube root function I have to define in order to create the function mentioned in the title.
# Cube root and cubic equation solver
#
# Copyright (c) 2013 user2330618
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at http://www.mozilla.org/MPL/2.0/.
from __future__ import division
import cmath
from cmath import log, sqrt
def cbrt(x):
"""Computes the cube root of a number."""
if x.imag != 0:
return cmath.exp(log(x) / 3)
else:
if x < 0:
d = (-x) ** (1 / 3)
return -d
elif x >= 0:
return x ** (1 / 3)
def cubic(a, b, c, d):
"""Returns the real roots to cubic equations in expanded form."""
# Define the discriminants
D = (18 * a * b * c * d) - (4 * (b ** 3) * d) + ((b ** 2) * (c ** 2)) - \
(4 * a * (c ** 3)) - (27 * (a ** 2) * d ** 2)
D0 = (b ** 2) - (3 * a * c)
i = 1j # Because I prefer i over j
# Test for some special cases
if D == 0 and D0 == 0:
return -(b / (3 * a))
elif D == 0 and D0 != 0:
return [((b * c) - (9 * a * d)) / (-2 * D0), ((b ** 3) - (4 * a * b * c)
+ (9 * (a ** 2) * d)) / (-a * D0)]
else:
D1 = (2 * (b ** 3)) - (9 * a * b * c) + (27 * (a ** 2) * d)
# More special cases
if D != 0 and D0 == 0 and D1 < 0:
C = cbrt((D1 - sqrt((D1 ** 2) - (4 * (D0 ** 3)))) / 2)
else:
C = cbrt((D1 + sqrt((D1 ** 2) - (4 * (D0 ** 3)))) / 2)
u_2 = (-1 + (i * sqrt(3))) / 2
u_3 = (-1 - (i * sqrt(3))) / 2
x_1 = (-(b + C + (D0 / C))) / (3 * a)
x_2 = (-(b + (u_2 * C) + (D0 / (u_2 * C)))) / (3 * a)
x_3 = (-(b + (u_3 * C) + (D0 / (u_3 * C)))) / (3 * a)
if D > 0:
return [x_1, x_2, x_3]
else:
return x_1
I've found that this function is capable of solving some simple cubic equations:
print cubic(1, 3, 3, 1)
-1.0
And a while ago I had gotten it to a point where it could solve equations with two roots. I've just done a rewrite and now it's gone haywire. For example, these coefficients are the expanded form of (2x - 4)(x + 4)(x + 2) and it should return [4.0, -4.0, -2.0] or something similar:
print cubic(2, 8, -8, -32)
[(-4+1.4802973661668753e-16j), (2+2.9605947323337506e-16j), (-2.0000000000000004-1.1842378929335002e-15j)]
Is this more a mathematical or a programming mistake I'm making?
Update: Thank you, everyone, for your answers, but there are more problems with this function than I've iterated so far. For example, I often get an error relating to the cube root function:
print cubic(1, 2, 3, 4) # Correct solution: about -1.65
...
if x > 0:
TypeError: no ordering relation is defined for complex numbers
print cubic(1, -3, -3, -1) # Correct solution: about 3.8473
if x > 0:
TypeError: no ordering relation is defined for complex numbers
Wolfram Alpha confirms that the roots to your last cubic are indeed
(-4, -2, 2)
and not as you say
... it should return [4.0, -4.0, -2.0]
Not withstanding that (I presume) typo, your program gives
[(-4+1.4802973661668753e-16j), (2+2.9605947323337506e-16j), (-2.0000000000000004-1.1842378929335002e-15j)]
Which to accuracy of 10**(-15) are the exact same roots as the correct solution. The tiny imaginary part is probably due, as others have said, to rounding.
Note that you'll have to use exact arithmetic to always correctly cancel if you are using a solution like Cardano's. This one of the reasons why programs like MAPLE or Mathematica exist, there is often a disconnect from the formula to the implementation.
To get only the real portion of a number in pure python you call .real. Example:
a = 3.0+4.0j
print a.real
>> 3.0
Hooked's answer is the way to go if you want to do this numerically. You can also do it symbolically using sympy:
>>> from sympy import roots
>>> roots('2*x**3 + 8*x**2 - 8*x - 32')
{2: 1, -4: 1, -2: 1}
This gives you the roots and their multiplicity.
You are using integer values - which are not automatically converted to floats by Python.
The more generic solution will be to write coefficients in the function as float numbers - 18.0 instead of 18, etc. That will do the trick
An illustration - from the code:
>>> 2**(1/3)
1
>>> 2**(1/3.)
1.2599210498948732
>>>

Categories