Related
I was wondering if I could get help solving a generic cubic polynomial with coefficients a, b, c and d. I don't want to use scipy or numpy.
For any complex-valued parameters, I want to find all 3 roots of the equation.
This is what i attempted so far,
def cubic_formula(a,b,c,d):
if not a==0:
x=-(b**3)/(27*(a**3))+(b*c)/(6*(a**2))-d/(2*a)
y=x**2+(c/(3*a)-b/(9*(a**2)))**3
return ((x-(y**(1/2)))**(1/3))+((x+(y**(1/2)))-b/(3*a)**(1/3)
elif not b==0:
br=c**2-4*b*d
rt=(-c+(br**(1/2)))/(2*b),(-c-(br**(1/2)))/(2*b)
return rt if not br==0 else -c/(2*b)
elif not c==0:
return -d/c
else:
if d==0:
How do I simplify my solution if d = 0? and how do I retrieve all the results as length-3 (or less if the solutions are fewer) tuple of numbers?
I know x = 0 is a solution that can be taken out x(a^2 + bx + c) = 0, which wields a normal quadratic function and another root at x = 0, however, I don't know how to code it in python and print out the answer.
Thanks in advance!
EDIT:
Only thing wrong with my code was that
if not a==0:
x=-(b**3)/(27*(a**3))+(b*c)/(6*(a**2))-d/(2*a)
y=x**2+c/(3*a)-b/(9*(a**2)))**3
return ((x-(y**(1/2)))**(1/3))++(x+(y**(1/2)))-b/(3*a)**(1/3)
only returned 1 value instead of 3 :) Nothing to do with
else
if d=0
When d = 0, ax³ + b x² + c x + d = 0 degenerates to x = 0 and the quadratic ax² + bx + c = 0.
In all cases, you can return the results as a list or a tuple (possibly empty). E.g. return (a+b, a-b).
I am trying to solve this optimization problem in Python. I have written the following code using PuLP:
import pulp
D = range(0, 10)
F = range(0, 10)
x = pulp.LpVariable.dicts("x", (D), 0, 1, pulp.LpInteger)
y = pulp.LpVariable.dicts("y", (F, D), 0, 1, pulp.LpInteger)
model = pulp.LpProblem("Scheduling", pulp.LpMaximize)
model += pulp.lpSum(x[d] for d in D)
for f in F:
model += pulp.lpSum(y[f][d] for d in D) == 1
for d in D:
model += x[d]*pulp.lpSum(y[f][d] for f in F) == 0
model.solve()
The one-but-last line here returns: TypeError: Non-constant expressions cannot be multiplied. I understand it is returning this since it cannot solve non-linear optimization problems. Is it possible to formulate this problem as a proper linear problem, such that it can be solved using PuLP?
It is always a good idea to start with a mathematical model. You have:
min sum(d, x[d])
sum(d,y[f,d]) = 1 ∀f
x[d]*sum(f,y[f,d]) = 0 ∀d
x[d],y[f,d] ∈ {0,1}
The last constraint is non-linear (it is quadratic). This can not be handled by PuLP. The constraint can be interpreted as:
x[d] = 0 or sum(f,y[f,d]) = 0 ∀d
or
x[d] = 1 ==> sum(f,y[f,d]) = 0 ∀d
This can be linearized as:
sum(f,y[f,d]) <= (1-x[d])*M
where M = |F| (number of elements in F, i.e. |F|=10). You can check that:
x[d]=0 => sum(f,y[f,d]) <= M (i.e. non-binding)
x[d]=1 => sum(f,y[f,d]) <= 0 (i.e. zero)
So you need to replace your quadratic constraint with this linear one.
Note that this is not the only formulation. You could also linearize the individual terms z[f,d]=x[d]*y[f,d]. I'll leave that as an exercise.
I have 2 tables(DataFrames), each has 2 columns. Let's say M1["a1","b1"] and M2["a2","b2"].
(M1 and M2 are actually refer to the same csv. I just describe them as two tables because of the function G below.)
I also have a function G = a1*b1*a2*b2 + a1*b1.
Just to make my question more clear, I would write function G as G(n,m) = a1(n)*b1(n)*a2(m)*b2(m) + a1(n)*b1(n) and would like to mention that (a1[n],b1[n]) always come in fixed pair, i.e., there is no (a1[3],b1[5]).
Later, I want to plot this function G with n corresponds with x-axis and m corresponds with y-axis.
The value of G itself will correspond with z-axis.
The final purpose is to find which (a,b) pair that gives the minimum value of G, if any.
How should I write function G in python?
Writing the following simply gives me error.
for n in a1,b1:
for m in a2,b2:
G = a1*b1*a2*b2 + a1*b1 #works, but the result consists only 1 column
G[:,:] = a1[n]*b1[n]*a2[m]*b2[m] + a1[n]*b1[n] #error
print(G)
I used simpler variable names above to simplify the post.
Here is my real code.
NMOS_gm_gmid = pandas.read_csv('NMOS_gm_gmid.csv', sep=',' , encoding='UTF-8')
NMOS_gm_gmid = NMOS_gm_gmid.apply(pandas.to_numeric, errors='coerce')
NMOS_ro_gmid = pandas.read_csv('NMOS_ro_gmid.csv', sep=',' , encoding='UTF-8')
NMOS_ro_gmid = NMOS_ro_gmid.apply(pandas.to_numeric, errors='coerce')
gm1 = NMOS_gm_gmid.iloc[:10,2]
ro1 = 1 / NMOS_ro_gmid.iloc[:10,2] * 1e6
gm2 = NMOS_gm_gmid.iloc[:10,2]
ro2 = 1 / NMOS_ro_gmid.iloc[:10,2] * 1e6
Gm = gm1*ro1*gm2*ro2 + gm1*ro1
You can use numpy broadcast here:
# M1, M2 from the same dataframe:
a1,b1,a2,b2 = df[['a1','b1','a2','b2']].to_numpy().T
G = (a1 * b1 + 1) * (a2*b2)[:,None]
My idea is as follows:
import random
list=[]
while True:
A = random.uniform(0,3150) / 3150
B = random.uniform(0,2200) / 2200
C = random.uniform(0,1000) / 1000
D = random.uniform(0,2600) / 2600
E = random.uniform(0,2540) / 2540
F = random.uniform(0,1200) / 1200
G = random.uniform(0,1050) / 1050
if A+B+C+D+E+F+G == 0.965:
list[0] = A
list[1] = B
list[2] = C
list[3] = D
list[4] = E
list[5] = F
list[6] = G
print(list)
break
But even if I operate this code, it takes too long to get a list.
Should I
Firstly, I dont understand why you're using random.uniform(0, upperLim)/upperLimbecause it will always be a value between 0 and 1. But if that is a necessary part of your implementation then go ahead. Otherwise change it to random.uniform(0, 1) because that avoids an unnecessary multiplication and gives you greater precision too.
To make sure the sum of the random numbers is 0.965 here's what you can do to avoid looping and checking multiple times:
Firstly change the upper limit from the one you specified to something that returns max 0.965 i.e, random.uniform(0, 3150 * 0.965) / 3150
After this, generate the numbers according to the conditions that have already been met
maxLists = 100
currLists = 0
while True:
A = random.uniform(0, 0.965) # I'll use (0, 0.965) but you can replace it
B = random.uniform(0, 0.965 - A)
C = random.uniform(0, 0.965 - (A + B))
D = random.uniform(0, 0.965 - (A + B + C))
E = random.uniform(0, 0.965 - (A + B + C + D))
F = random.uniform(0, 0.965 - (A + B + C + D + E))
G = 0.965 - (A + B + C + D + E + F)
# populate them into a list
listOfNums = [A, B, C, D, E, F, G]
# get numbers that are zero because we want to prevent only the first few numbers adding
# upto 0.965 all the time
zeros = [i for i, e in enumerate(listOfNums) if e == 0]
nonZeros = [i for i, e in enumerate(listOfNums) if e != 0]
# do something with numbers here, maybe randomly take some part of the value from
# non zero indices and assign it to indices with zero value if you need the numbers
# to have some minimum value or if you have some other constraint
currLists += 1
print(listOfNums)
if currLists == maxLists: break
EDIT :
Here are some of the results I got after running it
[0.17790933642353207, 0.203575715409321, 0.17296325968456305, 0.12888905609400236, 0.07906382215736181, 0.19622480233464165, 0.006374007896577938]
[0.049602151767929197, 0.12732071710856213, 0.6719775449266687, 0.08616832676415115, 0.002068199017310945, 0.0015719942386102515, 0.026291066176767575]
[0.4216568638854053, 0.0841285730604016, 0.12581422942385648, 0.04125099314750179, 0.013789268384205427, 0.12463265303883869, 0.1537274190597907]
[0.39352655740635734, 0.08302194874949533, 0.05585858753600888, 0.14417023258593673, 0.17742466007873198, 0.042698164836977186, 0.06829984880649254]
[0.836553479500795, 0.019661470617368986, 0.06300565338226506, 0.021033910322500717, 0.0234921077113921, 0.0002043707861913963, 0.0010490076794867909]
[0.5334487166183782, 0.07743001493044013, 0.3431304879017562, 0.001616778025312949, 0.003948535326924425, 0.001755908717321748, 0.003669558479866386]
You can see the last few results are approaching zero, which is why you will either need to take some part of the previous values and add it to them or you can shuffle the numbers around randomly
Here is one way to do it, using a generator
import random
def solution(precision):
while True:
A = random.uniform(0,3150) / 3150
B = random.uniform(0,2200) / 2200
C = random.uniform(0,1000) / 1000
D = random.uniform(0,2600) / 2600
E = random.uniform(0,2540) / 2540
F = random.uniform(0,1200) / 1200
G = random.uniform(0,1050) / 1050
if abs(A+B+C+D+E+F+G - 0.965) <= precision:
yield [A, B, C, D, E, F, G]
and you get values satisfying the condition by calling
next(solution(0.001)) as many times as you need.
If you ask for a better precision, say 0.00001, it may take much longer to compute a solution.
With SymPy, I can plot a function with:
f, a = symbols('f a')
f = a + 10
plot(f)
However, if I define the function as:
f, a, b = symbols('f a b')
f = a + b
b = 10
plot(f)
Then I get an error stating:
ValueError: The same variable should be used in all univariate
expressions being plotted.
How can I plot f if I define f = a + b, considering that b is assigned a constant value before plotting the function?
The lines
f, a, b = symbols('f a b')
f = a + b
b = 10
don't change b in the expression. If you print f you'll see that it is still defined as a + b.
You are confusing Python variables with SymPy symbols. In the first line, the Python variable b points to a SymPy symbol named b (in fact, they need not be the same name; you could have also written x = Symbol('b') and y = a + x). In the second line, the variable f points to a SymPy expression containing the symbol b. In the third line, the variable b points to the integer 10. This doesn't not change any previous lines that used the variable b, since they have already been run. It's no different than if you ran
a = 1
b = 1
c = a + b
b = 2
You would expect the value of c at the end to be 2, not 3. Similarly, when b points to a Symbol, expressions you create with it use a Symbol, but if you change it to point to a number, it doesn't affect previous lines from when it was a Symbol.
The recommended way to deal with this in SymPy is to avoid assigning the same variable to a symbol and then later to a non-symbol (it's worth pointing out that your definition of f in the first line is completely useless, since you immediately redefine it in the second line). To replace a symbol in an expression, use subs:
a, b = symbols('a b')
f = a + b
f1 = f.subs(b, 10)
Note that subs does not change the original f. It returns a new expression.
This document may also help clear this confusion up.
If you didn't want to use substitution as in the other answer, you could make f an actual function of course
def f(a, b):
return a + b
a = symbols('a')
b = 10
plot(f(a,b))
You must substitute b into f:
plot(f.subs('b', b))