Sympy Linsolve unexpected results - python

I am trying to solve a system of equations with Linsolve, but obviously must have misunderstood something, since I keep getting unexpected results. Say I want to solve the two following equations:
a + b = 0
a - b + c = 0
I would expect the result:
b = 0.5*c
Instead Sympy returns the empty set. With nonlinsolve I get (-a), which doesn't make much sense either:
>>> import sympy
>>> a, b, c = sympy.symbols('a b c')
>>> Eqns = [a + b, a - b + c]
>>>sympy.linsolve(Eqns, b)
()
>>>sympy.nonlinsolve(Eqns, b)
(-a)
I think I'm going insane, please help :)

You also need to pass the other variable. So pass as many variables as equations or it's unsolvable, just like by hand.
import sympy as sp
a, b, c = sp.symbols('a b c')
Eqns = [a + b, a - b + c]
sp.solve(Eqns, b, a)

Related

Informing sympy about inequality between variables

I am trying to solve a system in Sympy of the form
max(a,b+c) == a^2
I would like for example, to tell Sympy to search for a solution where $max(a,b+c) = a$ and $max(a,b+c) = b+c$. Is that possible in some way? I trying doing it through solve and solving a system of inequalities as in:
import sympy as sp
b = sp.Symbol('b', finite = True)
c = sp.Symbol('c', finite = True)
eq = sp.Max(a,b+c) - a**2
sp.solve([eq, a > b+c], a)
But I get the error:
The inequality, Eq(-x**2 + Max(x, _b + _c), 0), cannot be solved using
solve_univariate_inequality.
Is there anyway such type of equations can be solved? Or can I at least substitute $Max(a,b+c)$ to some case at least to simplify the expression?
Option 1
SymPy struggles solving equations with Min and Max. It is a little bit better at solving Piecewise equalities but it is still not great. Here is how I would tackle this specific problem using rewrite(Piecewise):
from sympy import *
a, b, c = symbols('a b c', real=True)
eq = Max(a, b+c) - a**2
solution = solve(eq.rewrite(Piecewise), a)
print(solution)
This gives
[Piecewise((0, b <= -c), (nan, True)), Piecewise((1, b + c <= 1), (nan, True)), Piecewise((-sqrt(b + c), b + c > -sqrt(b + c)), (nan, True)), Piecewise((sqrt(b + c), b + c > sqrt(b + c)), (nan, True))]
So this tells you that SymPy found 4 solutions all conditional on what b and c are. They seem like valid solutions after plugging them in. I'm not sure if those are all the solutions though.
SymPy might struggle a lot more if equations are more complicated than this.
The solutions would probably look even better if you added positive=True instead of real=True in the code above. Always try to give as much information as possible when defining symbols.
Option 2
Another route for solving these equations would be by substituting Max(a, b+c) for a and keep in mind that those solutions are for a >= b+c and repeat for b+c >= a. This would probably work better for more complicated equations.
For this specific example can do so by doing something like:
from sympy import *
a, b, c = symbols('a b c', real=True)
eq = Max(a, b+c) - a**2
eq1 = eq.subs(Max(a, b+c), a)
solution1 = solveset(eq1, a)
eq2 = eq.subs(Max(a, b+c), b+c)
solution2 = solveset(eq2, a)
solution = Piecewise((solution1, a > b+c), (solution2, a < b+c), (solution1.union(solution2), True))
print(solution)
Giving the same answer as above but a bit more readable:
Piecewise((FiniteSet(0, 1), a > b + c), (FiniteSet(sqrt(b + c), -sqrt(b + c)), a < b + c), (FiniteSet(0, 1, sqrt(b + c), -sqrt(b + c)), True))
Notice how you need to know the arguments of the Max before hand and that there is only one Max. Combining conditions with more than 1 max will be difficult especially since both solutions hold when they are equal.
I suggest this option if you are solving equations interactively instead of an in an automated fashion.
Option 3
I haven't tested this one but I hope this provides the same answers in the more general case where you have multiple Max varying arguments for each Max. Each Max can only take in 2 arguments though.
from sympy import *
a, b, c = symbols('a b c', real=True)
eq = Max(a, b+c) - a**2
eqs = [eq]
conditions = [True]
for f in preorder_traversal(eq):
new_eqs = []
new_conds = []
if f.func == Max:
for equation, condition in zip(eqs, conditions):
new_eqs.append(equation.subs(f, f.args[0]))
new_conds.append(And(condition, f.args[0] >= f.args[1]))
new_eqs.append(equation.subs(f, f.args[1]))
new_conds.append(And(condition, f.args[0] <= f.args[1]))
eqs = new_eqs
conditions = new_conds
solutions = []
for equation in eqs:
solutions.append(solveset(equation, a))
pieces = [(solution, condition) for solution, condition in zip(solutions, conditions)]
solution = Piecewise(*pieces)
print(solution)
This gives the same as above except for that last equality section:
Piecewise((FiniteSet(0, 1), a >= b + c), (FiniteSet(sqrt(b + c), -sqrt(b + c)), a <= b + c))
I could not combine both solutions when both of the inequalities hold so you just have to keep that in mind.

how to nudge substitutions in sympy

i have the following where a, b, c, d are all sympy symbols
p = a + b + 2*c + d
q = a + b + c + d
i wanted to do this:
p = p.subs(a + b + c + d, q)
and wanted to get this:
p = q + c
but p remains unchanged.
what should i be doing to get to p = q + c?
the matching performed by subs() seems to be looking for strict ordering, and hence it didn't split up the '2*c' term.
should i be using replace() instead of subs().
EDIT:
code as follows:
import sympy
a, b, c, d = sympy.symbols('a,b,c,d')
p = a + b + 2*c + d
q = a + b + c + d
r = p.subs(a + b + c + d, q)
print r
EDIT #2
https://groups.google.com/forum/#!topic/sympy/b_Yv6s15Y0Q
my problem is similar to the groups.google link, just that in that case subs() do its job.
First a comment about your code: since you define q to be the sum a+b+c+d you will never see the change, even if it did work (but it doesn't). Something else that does work is the following:
>>> p = a + b + 2*c + d
>>> q = var('q')
>>> p.extract_additively(a+b+c+d) + q
c + q
There is also an extract_multiplicatively method.

sum function vs long addition

I am wondering if the sum() builtin has an andventage over a long addition ?
is
sum(filter(None, [a, b, c, d]))
faster than
a + b + c + d
assuming I am using CPython ?
thanks
EDIT: What if those variables are Decimals ?
A quick example (note that, to try to be fairer, the sum version takes a tuple argument, so you don't include the time for building that structure (a, b, c, d), and doesn't include the unnecessary filter):
>>> import timeit
>>> def add_up(a, b, c, d):
return a + b + c + d
>>> def sum_up(t):
return sum(t)
>>> t = (1, 2, 3, 4)
>>> timeit.timeit("add_up(1, 2, 3, 4)", setup="from __main__ import sum_up, add_up, t")
0.2710826617188786
>>> timeit.timeit("sum_up(t)", setup="from __main__ import sum_up, add_up, t")
0.3691424539089212
This is pretty much inevitable - add_up doesn't have any function call overhead, it just does 3 binary adds. But the different forms have different uses - sum doesn't care how many items are given to it, whereas you have to write each name out with +. In an example with a fixed number of items, where speed is crucial, + has the edge, but for almost all general cases sum is the way to go.
With Decimals:
>>> t = tuple(map(Decimal, t))
>>> a = Decimal(1)
>>> b = Decimal(2)
>>> c = Decimal(3)
>>> d = Decimal(4)
>>> timeit.timeit("add_up(a, b, c, d)", setup="from __main__ import sum_up, add_up, t, a, b, c, d")
0.5005962150420373
>>> timeit.timeit("sum_up(t)", setup="from __main__ import sum_up, add_up, t, a, b, c, d")
0.7599533142681025

Error in for loop. (Finding three integers)

So, our teacher gave us an assignment to find three integers a, b c. They are in all between 0 and 450 using Python.
a = c + 11 if b is even
a = 2c-129 if b is odd
b = ac mod 2377
c = (∑(b-7k) from k = 0 too a-1) +142 (Edited. I wrote it wrong. Was -149)
I tired my code that looks like this: (Still a newbie. I guess a lot of my code is wrong)
for a, b, c in range(0, 450):
if b % 2 == 0:
a = c + 11
else:
a = 2 * c - 129
b = (a * c) % 2377
c = sum(b - 7 * k for k in range(0, a - 1))
but I get the error:
for a, b, c in range(0, 450):
TypeError: 'int' object is not iterable
What am I doing wrong and how can I make it check every number between 0 and 450?
The answers by Nick T and Eric hopefully helped you solve your issue with iterating over values of a, b, and c. I would like to also point out that the way you're approaching this problem isn't going to work. What's the point of iterating over various values of a if you're going to re-assign a to something anyway at each iteration of the loop? And likewise for b and c. A better approach involves checking that any given triple (a, b, c) satisfies the conditions given in the assignment. For example:
from itertools import product, tee
def test(a, b, c):
flags = {'a': False,
'b': False,
'c': False}
if (b % 2 == 0 and a == c+11) or (b % 2 == 1 and a == 2*c-129):
flags['a'] = True
if b == (a * c) % 2377:
flags['b'] = True
if c == sum(b - 7*k for k in range(a-1)) - 149:
flags['c'] = True
return all(flags.values()) # True if zero flags are False
def run_tests():
# iterate over all combinations of a=0..450, b=0..450, c=0..450
for a, b, c in product(*tee(range(451), 3)):
if test(a, b, c):
return (a, b, c)
print(run_tests())
NOTE: This is a slow solution. One that does fewer loops, like in glglgl's answer, or Duncan's comment, is obviously favorable. This is really more for illustrative purposes than anything.
import itertools
for b, c in itertools.product(*[range(450)]*2):
if b % 2 == 0:
a = c + 11
else:
a = 2 * c - 129
derived_b = (a * c) % 2377
derived_c = sum(b - 7 * k for k in range(0, a - 1))
if derived_b == b and derived_c == c:
print a, b, c
You need to nest the loops to brute-force it like you are attempting:
for a in range(451): # range(450) excludes 450
for b in range(451):
for c in range(451):
...
It's very obviously O(n3), but if you want a quick and dirty answer, I guess it'll work—only 91 million loops, worst case.
The stuff with [0, 450] is just as a hint.
In fact, your variables are coupled together. You can immediately eliminate at least one loop directly:
for b in range(0, 451):
for c in range(0, 451):
if b % 2: # odd
a = 2 * c - 129
else:
a = c + 11
if b != (a * c) % 2377: continue # test failed
if c != sum(b - 7 * k for k in range(a)): continue # test failed as well
print a, b, c
should do the job.
I won't post full code (after all, it is homework), but you can eliminate two of the outer loops. This is easiest if you iterate over c.
You code should then look something like:
for c in range(451):
# calculate a assuming b is even
# calculate b
# if b is even and a and b are in range:
# calculate what c should be and compare against what it is
# calculate a assuming b is odd
# calculate b
# if b is odd and a and b are in range:
# calculate what c should be and compare against what it is
Extra credit for eliminating the duplication of the code to calculate c
a = c + 11 if b is even
a = 2c-129 if b is odd
b = ac mod 2377
c = (∑(b-7k) from k = 0 to a-1) +142
This gives you a strong relation between all 3 numbers
Given a value a, there are 2 values c (a-11 or (a+129)/2), which in turn give 2 values for b (ac mod 2377 for both values of c, conditioned on the oddity of the result for b), which in turn gets applied in the formula for validating c.
The overall complexity for this is o(n^2) because of the formula to compute c.
Here is an implementation example:
for a in xrange(451):
c_even = a - 11
b = (a*c_even) % 2377
if b % 2 == 0:
c = sum(b - 7 * k for k in range(a)) + 142
if c == c_even:
print (a, b, c)
break
c_odd = (a+129)/2
b = (a*c_odd) % 2377
if b % 2 == 1:
c = sum(b - 7 * k for k in range(a)) + 142
if c == c_odd:
print (a, b, c)
break

True/False right angle function in python

I can't seem to get my function to work. When I type in 3 for a, 2 for b, and 3.61 for c. That works. However, when I try those values in a different order (Ex: 3.61 for a, 3 for b and 2 for c) It returns as false. I can't figure out what the problem is. Thanks in advance!
a = input("Enter a ")
b = input("Enter b ")
c = input("Enter c ")
def isright_angled():
if abs((a**2+b**2)-(c**2)) < 0.1 or abs((c**2-a**2)-(b**2)) < 0.1 or abs((c**2-b**2)-(a**2)) < 0.1:
return True
else:
return False
print isright_angled()
The hypotenuse, if the triangle is right-angled, will be the largest of a, b and c. You can use that to avoid duplicating the test 3 times (this is the "don't repeat yourself" principle). A second thing to avoid is that if something: return True else: return False. It's usually better expressed as simply return something. Thirdly, functions can take arguments rather than relying on global variables: this makes things easier to understand and there's then less chance of functions interfering with each other. I find a * a easier to understand than a ** 2 but that's personal taste. Putting all that together:
def is_approximately_right_angled(a, b, c):
a, b, c = sorted([a, b, c])
return abs(a * a + b * b - c * c) < 0.1
a = input('enter a ')
b = input('enter b ')
c = input('enter c ')
print is_approximately_right_angled(a, b, c)
If it's not working, you can speed up your development by adding some checks. If you were writing a big program you can write unit tests, but here just some asserts in the module will avoid you having to type a, b, c in each time to test.
I'd add something like this (before the a = input... line):
assert is_approximately_right_angled(3, 4, 5)
assert is_approximately_right_angled(3, 5, 4)
assert is_approximately_right_angled(3, 2, 3.61)
assert not is_approximately_right_angled(3, 5, 5)
With these lines in place, you can have some confidence in the code before you get to type numbers in. When you find cases where the code doesn't work you can add them as additional checks.
a = int(input("Enter the side length" ))
b = int(input("Enter the side length" ))
c = int(input("Enter the side length" ))
def is_right_triangle(a,b,c):
'''
is_right_triangle(a,b,c) -> bool
returns True if a,b,c is a right triangle with hypotenuse c
'''
a, b, c = sorted([a, b, c])
return a*a + b*b == c*c
print(is_right_triangle(a,b,c))
for more accuracy you can use
return abs(a * a + b * b - c * c) < 0.001

Categories