Solve basic mathematical operations in python 3 - python

I'm trying to develop a simple python method that will allow me to compute basic mathematical operations. The point here is that I can't use eval(), exec() or any other functions that evaluate python statemets, so I have to do it manually. Up to now, I've came across with this piece of code:
solutionlist = list(basicoperationslist)
for i in range(0, len(solutionlist)):
if '+' in solutionlist[i]:
y = solutionlist[i].split('+')
solutionlist[i] = str(int(y[0]) + int(y[1]))
elif '*' in solutionlist[i]:
y = solutionlist[i].split('*')
solutionlist[i] = str(int(y[0]) * int(y[1]))
elif '/' in solutionlist[i]:
y = solutionlist[i].split('/')
solutionlist[i] = str(int(y[0]) // int(y[1]))
elif '-' in solutionlist[i]:
y = solutionlist[i].split('-')
solutionlist[i] = str(int(y[0]) - int(y[1]))
print("The solutions are: " + ', '.join(solutionlist))
So we have two lists of Strings, the basicoperationlist has operations of the following format: 2940-81, 101-16, 46/3, 10*9, 145/24, -34-40.
They will always have two numbers, and one operand in the middle. The problem with my solution is that when I have an operation like the last one, the .split() method splits my list into an empty list and a list with the complete operation. In summary, this solution does not work well when we mix negative numbers an the minus operation. I don't know if it fails in any other case because I've only managed to notice the error I previously described.
The idea is that at the end of the method, I have the solutionlist as a list of Strings that are going to be the ordered answers to the basic mathematical operations.
This is the error that prompts out whenever my code encounters an operation like the last one: ValueError: invalid literal for int() with base 10: ''
The basicoperationslist is defined here:
basicoperationslist = re.findall('[-]*\d+[+/*-]+\d+', step2processedoperation)
As you see, I use a regex to extract the basicoperations from a larger operation. The step2processedoperation is an String that a server sends to my machine. But as example it may contain:
((87/(64*(98-94)))+((3-(97-27))-(89/69)))
It contains complete and balanced mathematical operations.
Maybe someone could help me to solve this problem or maybe I should change this method completely.
Thank you in advance.

I'd ditch the whole splitting approach as it is way too complex and may fail in certain cases as you noticed.
Instead I'd use a regex and the operator module to simplify things.
import re
import operator
operators = {'+': operator.add,
'-': operator.sub,
'*': operator.mul,
'/': operator.truediv}
regex = re.compile(r'(-?\d+)' # 1 or more digits with an optional leading '-'
r'(\+|-|\*|/)' # one of +, - , *, /
r'(\d+)', # 1 or more digits
re.VERBOSE)
exprssions = ['2940-81', '101-16', '46/3', '10*9', '145/24', '-34-40']
for expr in exprssions:
a, op, b = regex.search(expr).groups()
print(operators[op](int(a), int(b)))
# 2859
# 85
# 15.333333333333334
# 90
# 6.041666666666667
# -74
This approach is easier to adapt to new cases (new operators for example)

You can easily use operator and a dict to store the operations instead of a long list of if-else
This solution can also calculate more complicated expressions through recursion.
Define the operations and their order
from operator import add, sub, mul, floordiv, truediv
from functools import reduce
OPERATIONS = {
'+': add,
'-': sub,
'*': mul,
'/': floordiv, # or '/': truediv,
'//': floordiv,
}
OPERATION_ORDER = (('+', '-'), ('//', '/', '*'))
The simple case of a single number
def calculate(expression):
# expression = expression.strip()
try:
return int(expression)
except ValueError:
pass
The calculation
for operations in OPERATION_ORDER:
for operation in operations:
if operation not in expression:
continue
parts = expression.split(operation)
parts = map(calculate, parts) # recursion
value = reduce(OPERATIONS[operation], parts)
# print(expression, value)
return value
Negative first number
before the calculation:
negative = False
if expression[0] == '-':
negative = True
expression = expression[1:]
in the calculation, after the splitting of the string:
if negative:
parts[0] = '-' + parts[0]
So in total this becomes:
def calculate(expression):
try:
return int(expression)
except ValueError:
pass
negative = False
if expression[0] == '-':
negative = True
expression = expression[1:]
for operations in OPERATION_ORDER:
for operation in operations:
if operation not in expression:
continue
parts = expression.split(operation)
if negative:
parts[0] = '-' + parts[0]
parts = map(calculate, parts) # recursion
return reduce(OPERATIONS[operation], parts)
Parentheses
Checking whether there are parentheses can be done easily with re. First we need to make sure it doesn't recognise the 'simple' parenthised intermediary results (like (-1))
PATTERN_PAREN_SIMPLE= re.compile('\((-?\d+)\)')
PAREN_OPEN = '|'
PAREN_CLOSE = '#'
def _encode(expression):
return PATTERN_PAREN_SIMPLE.sub(rf'{PAREN_OPEN}\1{PAREN_CLOSE}', expression)
def _decode(expression):
return expression.replace(PAREN_OPEN, '(').replace(PAREN_CLOSE, ')')
def contains_parens(expression):
return '(' in _encode(expression)
Then to calculate the leftmost outermost parentheses, you can use this function
def _extract_parens(expression, func=calculate):
# print('paren: ', expression)
expression = _encode(expression)
begin, expression = expression.split('(', 1)
characters = iter(expression)
middle = _search_closing_paren(characters)
middle = _decode(''.join(middle))
middle = func(middle)
end = ''.join(characters)
result = f'{begin}({middle}){end}' if( begin or end) else str(middle)
return _decode(result)
def _search_closing_paren(characters, close_char=')', open_char='('):
count = 1
for char in characters:
if char == open_char:
count += 1
if char == close_char:
count -= 1
if not count:
return
else:
yield char
The reason for the () around calculate(middle) is because an intermediate result can be negative, and this might pose problems later on if the parentheses are left out here.
The beginning of the algorithm then changes to:
def calculate(expression):
expression = expression.replace(' ', '')
while contains_parens(expression):
expression = _extract_parens(expression)
if PATTERN_PAREN_SIMPLE.fullmatch(expression):
expression = expression[1:-1]
try:
return int(expression)
except ValueError:
pass
Since intermediary results can be negative, we need regular expressions to split on the - to prevent 5 * (-1) from being split on the -
So I reordered the possible operations like this:
OPERATIONS = (
(re.compile('\+'), add),
(re.compile('(?<=[\d\)])-'), sub), # not match the - in `(-1)`
(re.compile('\*'), mul),
(re.compile('//'), floordiv),
(re.compile('/'), floordiv), # or '/': truediv,
)
The pattern for the - only matches if it is preceded by a ) or a digit. This way we can drop the negative flag and handling
The rest of the algorithms changes then to:
operation, parts = split_expression(expression)
parts = map(calculate, parts) # recursion
return reduce(operation, parts)
def split_expression(expression):
for pattern, operation in OPERATIONS:
parts = pattern.split(expression)
if len(parts) > 1:
return operation, parts
complete algorithm
The complete code can be found here
testing:
def test_expression(expression):
return calculate(expression) == eval(expression.replace('/','//')) # the replace to get floor division
def test_calculate():
assert test_expression('1')
assert test_expression(' 1 ')
assert test_expression('(1)')
assert test_expression('(-1)')
assert test_expression('(-1) - (-1)')
assert test_expression('((-1) - (-1))')
assert test_expression('4 * 3 - 4 * 4')
assert test_expression('4 * 3 - 4 / 4')
assert test_expression('((87/(64*(98-94)))+((3-(97-27))-(89/69)))')
test_calculate()
power:
Adding power becomes as simple as adding
(re.compile('\*\*'), pow),
(re.compile('\^'), pow),
to OPERATIONS
calculate('2 + 4 * 10^5')
400002

Related

If you have 4+3*2, is there an algorithm to calculate this in one pass?

So I implemented this before in JavaScript as a little practice and was sure I found a Wiki page for an algorithm that both converted a typical infix expression to post fix AND calculated the answer in the same pass
In contrast, I can find this (https://en.wikipedia.org/wiki/Shunting-yard_algorithm), but this only converts the expression to postfix - it doesn't actually calculate the answer too
I'm curious, is there something else I should be searching for instead? I found a Wiki article before and was able to implement a function like which could take in strings like calc("(44 / (7 + 4) * 5 - 17) or whatever and it would spit out the answer...the pseudocode was pretty short (like 20 lines max) and I'm pretty sure all the code was nicely contained in a single loop
If anyone could point me in the right direction to find this algorithm/page, I'd really appreciate it. Thanks
The shunting-yard algorithm as described by Wikipedia can easily be modified to compute the value directly. Having popped an operator off the operator stack and its operands off the operand stack, instead of outputting them, compute the result and push it back to the operand stack. To take your example from the heading:
read 4: push 4 on operand stack
read +: push + on operator stack
read 3: push 3 on operand stack
read *: since * has higher precedence than the + at the top
of the operator stack, push *
read 2: push 2 to operand stack
END : pop operator (*), pop two operands (3, 2) push 6
END : pop operator (+), pop two operands (4, 6), push 10
END : operator stack is empty, result (10) is at top of operand stack.
Using regular expressions, you can make a recursive function for the 4 basic operators supporting parentheses and operator precedence :
for example:
import re
from itertools import accumulate
def calc(S):
S = S.replace(" ","")
if not S: return 0
levels = [*accumulate((c=="(")-(c==")") for c in S)]
inner = max(levels)
if inner: # process innermost parentheses
start = levels.index(inner)
end = levels.index(inner-1,start)
return calc(S[:start]+str(calc(S[start+1:end]))+S[end+1:])
tokens = re.split(r"(?<!\*|\/)(\+|\-)",S)
if len(tokens)>1: # additions/subtractions (between operands)
result,sign = 0,1
for t in tokens:
if t == "+": sign = 1
elif t == "-": sign = -1
else: result += sign*calc(t)
return result
tokens = re.split(r"(\*|\/)",S)
if len(tokens)>1: # multiplications and divisions
result,op = 1,"*"
for t in tokens:
if t == "*" : op = "*"
elif t == "/" : op = "/"
elif op == "*": result *= calc(t)
else: result /= calc(t)
return result
return float(S)
output:
print(calc("44 / (7 + 4) * 5 - 17")) # 3.0
This works by recursively substituting operations with their resulting value in the string.
The parentheses grouping are handled first by replacing the innermost group with its resulting value.
When there are no more parentheses, the string is split into additions/subtraction groups and recurses to get the any multiply/divide results between them (thus executing the multiplications/divisions first).
The multiplication/division operations are processed at the next level of recursion
and finally, a string without any operator is treated as a number (float)
Postfix stacking is hidden in the recursion although you could probably track it as you go if you need it.
For a non-recursive solution, you can break down the problem in simpler components:
tokenizing expressions into a partial RPN stack for specific operators
building the RPN notation by successive refinement (using the tokenizer) until only values and operators remain
executing the RPN stack to get the final result
The tokenize() function breaks down an expression into first level tokens for a given set of equal-precedence operators. This returns a mini-RPN stack (only for those operators) with operands being any string between these operators. Tokens that are enclosed in parentheses will be processed on a next refinement pass (i.e. by removing the outermost parentheses)
def tokenize(exp,opers):
result = []
token,operator,enclosed,pLevel = "","",True,0
for c in exp+opers[0]: # extra operator to force out last operation
if c in opers and token and pLevel == 0:
if enclosed: token = token[1:-1] # remove enclosing parentheses
result.append(token)
if operator: result.append(operator)
operator,token,enclosed = c,"",True
else:
token += c
pLevel += (c=="(")-(c==")")
if c != ")" and pLevel==0: enclosed = False
return result
The 'RPN()' function breaks down the whole expression by iteratively refining the tokens in a global RPN stack.
def RPN(S):
result = [S.replace(" ","").replace("**","^")] # unrefined RPN
done = False
while not done: # iterative refinement loop
previous = result.copy()
for opers in (["+","-"],["*","/"],["^"]): # reversed precedence
result = [token for exp in result for token in tokenize(exp,opers)]
done = result == previous
if not done: break # refine more, restart operators
return result
The 'calc()' function takes the components of the RPN stack and executes each operation in order to obtain the resulting value
def calc(S):
values = []
for op in RPN(S): # execute RPN
if op == "+": values[-2:] = [values[-2] + values[-1]]
elif op == "-": values[-2:] = [values[-2] - values[-1]]
elif op == "*": values[-2:] = [values[-2] * values[-1]]
elif op == "/": values[-2:] = [values[-2] / values[-1]]
elif op == "^": values[-2:] = [values[-2] ** values[-1]]
else: values.append(float(op))
return values[0]
output:
print(RPN("44 / (7 + 4) * 5 - 17"))
# ['44', '7', '4', '+', '/', '5', '*', '17', '-']
print(calc("44 / (7 + 4) * 5 - 17"))
# 3.0
note that this simple approach does not support monadic negations such as 2**-3 which you would have to write as 2**(-3)

Evaluate an (almost algebraic) expression without the '*' symbol in python

I have the following content in the value.txt:
2A-25X-8A+34X-5B+11B
If I use MetaFont via terminal bash how below:
#mf
This is METAFONT, Version 2.7182818 (TeX Live 2019/Arch Linux) (preloaded base=mf)
**expr
(/usr/share/texmf-dist/fonts/source/public/knuth-lib/expr.mf
gimme an expr: 2A-25X-8A+34X-5B+11B
>> 6B+9X-6A
gimme an expr:
I can evaluate the expression without the '*' symbol between letters and numbers.
What I want is to do this using Python as cleanly and economically as possible but still without using '*'.
I haven't found anything about it yet.
I also hope it is a syntax that can be implemented with with open, print = and r.
EDIT
A possible idea would be like this:
with open ("value.txt", "r") as value:
data = value.read()
#some python method for evaluate value.txt expression and save in variable value2
print = (value2)
Always interested in questions regarding parsing arithmetic. Here is a pyparsing-based solution (albeit a bit longer than you were hoping, and using more than just with, open, etc.).
The first 30 lines define a class for tallying up the variables, with support for adding, subtracting, and multiplying by an integer. (Integers are modeled as a Tally with a variable of ''.)
The next 30 lines define the actual parser, and the parse-time actions to convert the parsed tokens into cumulative Tally objects.
The final 25 lines are tests, including your sample expression.
The real "smarts" of the parser are in the infixNotation method, which implements the parsing of the various operators, including handling of operator precedence and grouping
with ()'s. The use of "3A" to indicate "3 times A" is done by passing None as the multiplication operator. This also supports constructs like "2(A+2B)" to give "2A+4B".
import pyparsing as pp
# special form of dict to support addition, subtraction, and multiplication, plus a nice repr
class Tally(dict):
def __add__(self, other):
ret = Tally(**self)
for k, v in other.items():
ret[k] = ret.get(k, 0) + v
if k and ret[k] == 0:
ret.pop(k)
return ret
def __mul__(self, other):
if self[''] == 0:
return Tally()
ret = Tally(**other)
for k in ret:
ret[k] *= self['']
return ret
def __sub__(self, other):
return self + MINUS_1 * other
def __repr__(self):
ret = ''.join("{}{}{}".format("+" if coeff > 0 else "-", str(abs(coeff)) if abs(coeff) != 1 else "", var)
for var, coeff in sorted(self.items()) if coeff)
# leading '+' signs are unnecessary
ret = ret.lstrip("+")
return ret
MINUS_1 = Tally(**{'': -1})
var = pp.oneOf(list("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
# convert var to a Tally of 1
var.addParseAction(lambda t: Tally(**{t[0]: 1}))
integer = pp.pyparsing_common.integer().addParseAction(lambda tokens: Tally(**{'': tokens[0]}))
def add_terms(tokens):
parsed = tokens[0]
ret = parsed[0]
for op, term in zip(parsed[1::2], parsed[2::2]):
if op == '-':
ret -= term
else:
ret += term
return ret
def mult_terms(tokens):
coeff, var = tokens[0]
return coeff * var
# only the leading minus needs to be handled this way, all others are handled
# as binary subtraction operators
def leading_minus(tokens):
parsed = tokens[0]
return MINUS_1 * parsed[1]
leading_minus_sign = pp.StringStart() + "-"
operand = var | integer
expr = pp.infixNotation(operand,
[
(leading_minus_sign, 1, pp.opAssoc.RIGHT, leading_minus),
(None, 2, pp.opAssoc.LEFT, mult_terms),
(pp.oneOf("+ -"), 2, pp.opAssoc.LEFT, add_terms),
])
expr.runTests("""\
B
B+C
B+C+3B
2A
-2A
-3Z+42B
2A+4A-6A
2A-25X-8A+34X-5B+11B
3(2A+B)
-(2A+B)
-3(2A+B)
2A+12
12
-12
2A-12
(5-3)(A+B)
(3-3)(A+B)
""")
Gives the output (runTests echoes each test line, followed by the parsed result):
B
[B]
B+C
[B+C]
B+C+3B
[4B+C]
2A
[2A]
-2A
[-2A]
-3Z+42B
[42B-3Z]
2A+4A-6A
[]
2A-25X-8A+34X-5B+11B
[-6A+6B+9X]
3(2A+B)
[6A+3B]
-(2A+B)
[-2A-B]
-3(2A+B)
[-6A-3B]
2A+12
[12+2A]
12
[12]
-12
[-12]
2A-12
[-12+2A]
(5-3)(A+B)
[2A+2B]
(3-3)(A+B)
[]
To show how to use expr to parse your expression string, see this code:
result = expr.parseString("2A-25X-8A+34X-5B+11B")
print(result)
print(result[0])
print(type(result[0]))
# convert back to dict
print({**result[0]})
Prints:
[-6A+6B+9X]
-6A+6B+9X
<class '__main__.Tally'>
{'B': 6, 'A': -6, 'X': 9}

looking for different ways to implement it via python

Write a simple interpreter which understands "+", "-", and "*" operations.
Apply the operations in order using command/arg pairs starting with the initial value of value.
If you encounter an unknown command, return -1.
Examples :
interpret(1, ["+"], [1]) → 2
interpret(4, ["-"], [2]) → 2
interpret(1, ["+", "*"], [1, 3]) → 6
def len_n(val, command, args):
if command == '+' :
return val + args
elif command == '-':
return val - args
elif command == '*':
return val * args
else:
return -1
def interpret(val, command, args):
count = len(command)
for i in range(count) :
val = len_n(val,command[i],args[i])
return val
*****************
************************
final = len_n(val,command[0],args[0])
del command[0]
del args[0]
if command[0:]:
final = interpret(final, command, args)
return final
****************
I tried by above 2 methods both worked as well! what are other methods this can be implemented? Thanks,
A nice way to solve such arithmetic parsing problems is Dijkstras shunting-yard algorithm, which converts arithmetic expressions written in infix notation (like (3 + 1) * 6) into Reverse polish notation (like 3 1 + 6 *), which has each operator after its operands instead of between them.
Reverse polish notation makes it a lot easier to calculate a result:
# Calculates the result of a given arithmetic expression only containing numbers
# and +, * when given in reverse polish notation
outputStack = []
def addNumber(number):
outputStack.append(number)
def addOperator(operator):
result = operator(outputStack.pop(), outputStack.pop())
outputStack.append(result)
def addOperatorByChar(operatorChar):
if (operatorChar == '+'):
addOperator(lambda x, y: x + y)
if (operatorChar == '*'):
addOperator(lambda x, y: x * y)
#unknwon operator, don't do anything
#test code:
addNumber(3)
addNumber(1)
addOperatorByChar("+")
addNumber(6)
addOperatorByChar("*")
print(outputStack[0]) #should be 24 in this example
print(len(outputStack)) #should be one after correct arithmetic expression

Converting All Redundant Floats in a String to Integers

I'm using Sympy to make a custom function which converts complex square roots into their complex numbers. When I input -sqrt(-2 + 2*sqrt(3)*I) I get the expected output of -1 - sqrt(3)*I, however, inputting -sqrt(-2.0 + 2*sqrt(3)*I) (has a -2.0 instead of -2), I get the output -1.0 - 0.707106781186547*sqrt(6)*I.
I've tried to convert the input expression to a string, gotten rid of the '.0 ' and then executed a piece of code to return it to the type sympy.core.add.Mul, which usually works with other strings, but the variable expression is still a string.
expression = str(input_expression).replace('.0 ', '')
exec(f'expression = {expression}')
How do I get rid of the redundant use of floats in my expression, while maintaining its type of sympy.core.add.Mul, so that my function will give a nice output?
P.S. The number 0.707106781186547 is an approximation of 1/sqrt(2). The fact that this number is present in the second output means that my function is running properly, it just isn't outputting in the desired way.
Edit:
For whatever reason, unindenting and getting rid of the function as a whole, running the code as its own program gives the expected output. It's only when the code is in function form that it doesn't work.
Code as Requested:
from IPython.display import display, Math
from sympy.abc import *
from sympy import *
def imaginary_square_root(x, y):
return(sqrt((x + sqrt(x**2 + y**2)) / (2)) + I*((y*sqrt(2)) / (2*sqrt(x + sqrt(x**2 + y**2))))) # calculates the square root of a complex number
def find_imaginary_square_root(polynomial): # 'polynomial' used because this function is meant to change expressions including variables such as 'x'
polynomial = str(polynomial).replace('.0 ', ' ')
exec(f'polynomial = {polynomial}')
list_of_square_roots = [] # list of string instances of square roots and their contents
list_of_square_root_indexes = [] # list of indexes at which the square roots can be found in the string
polynomial_string = str(polynomial)
temp_polynomial_string = polynomial_string # string used and chopped up, hence the prefix 'temp_...'
current_count = 0 # counter variable used for two seperate jobs
while 'sqrt' in temp_polynomial_string: # gets indexes of every instance of 'sqrt'
list_of_square_root_indexes.append(temp_polynomial_string.index('sqrt') + current_count)
temp_polynomial_string = temp_polynomial_string[list_of_square_root_indexes[-1] + 4:]
current_count += list_of_square_root_indexes[-1] + 4
for square_root_location in list_of_square_root_indexes:
current_count = 1 # second job for 'current_count'
for index, char in enumerate(polynomial_string[square_root_location + 5:]):
if char == '(':
current_count += 1
elif char == ')':
current_count -= 1
if not current_count: # when current_count == 0, we know that the end of the sqrt contents have been reached
list_of_square_roots.append(polynomial_string[square_root_location:square_root_location + index + 6]) # adds the square root with contents to a list
break
for individual_square_root in list_of_square_roots:
if individual_square_root in str(polynomial):
evaluate = individual_square_root[5:-1]
x = re(evaluate)
y = im(evaluate)
polynomial = polynomial.replace(eval(individual_square_root), imaginary_square_root(x, y)) # replace function used here is Sympy's replace function for polynomials
return polynomial
poly = str(-sqrt(-2.0 + 2*sqrt(3)*I))
display(Math(latex(find_imaginary_square_root(poly))))
What exactly are you trying to accomplish? I still do not understand. You have a whole chunck of code. Try this out:
from sympy import *
def parse(expr): print(simplify(expr).evalf().nsimplify())
parse(-sqrt(-2.0 + 2*sqrt(3)*I))
-1 - sqrt(3)*I
I think everything that you're fighting to do here can be made easier with what sympy has built in. First, assuming that you're taking in user given strings, I'd recommend using the built in parser's of sympy. Second, sympy will do this exact calculation for you, although with a caveat.
from sympy.parsing.sympy_parser import parse_expr
def simplify_string(polynomial_str):
polynomial = parse_expr(polynomial_str)
return polynomial.powsimp().evalf()
Usage examples:
>>>simplify_string('-sqrt(-2 + 2*sqrt(3)*I)')
-1.0 - 1.73205080756888*I
>>>simplify_string('sqrt(sqrt(1 + sqrt(2)*I) + I*sqrt(3 - I*sqrt(5)))')
1.54878147282944 + 0.78803305913*I
>>>simpify_string('sqrt((3 + sqrt(2 + sqrt(3)*I)*I)*x**2 + (3 + sqrt(5)*I)*x + I*4)'
(x**2*(3.0 + I*(2.0 + 1.73205080756888*I)**0.5) + x*(3.0 + 2.23606797749979*I) + 4.0*I)**0.5
The problem is, that sympy will either work in floats, or exact. If you want sympy to calculate out the numerical value of a square root, it's going to display what could be an int as a float for clarity. You can't fix the typecasting, but a lot of the work that you're trying to do, sympy has built in under the hood.
Edit
You can use .nsimplify() on the polynomial to bring things back to nice looking numbers if possible, but you won't be able to have both evaluated roots, and nice displays in the same form.
The sqrtdenest batteries are already included. If you replace ints expressed as floats it will work:
>>> from sympy import sqrtdenest, sqrt, Float
>>> eq = -sqrt(-2.0 + 2*sqrt(3)*I)
Define a function that will extract Floats that are equal to ints
>>> intfloats = lambda x: dict([(i,int(i)) for i in x.atoms(Float) if i==int(i)])
Use it to transform eq and then apply the sqrtdenest
>>> eq.xreplace(intfloats(eq))
-sqrt(-2 + 2*sqrt(3)*I)
>>> sqrtdenest(_)
-1 + sqrt(3)
A problem with using nsimplify (or any mass simplification) is that it may do more than you want. It's best to use the most specific transformation as possible to limit the impact (and work).
/!\ sqrtdenest appears to have a problem that I will report: it is dropping the I

Sympy won't simplify or expand exponential with decimals

i'm trying to simplify a huge expression of powers of n , and one of the results of sympy throws a (n+1)^1.0 , i noticed that
f=n*((n+1)**1.0)
sympy.expand(f)
doesn't work it stays the same instead of giving n^2+n, so i was wondering if there's any way to perform something like this
Sympy will expand your expression as expected when the power is an integer number. If the power is stored as a rational or a float, it won't work. Your options are either to rewrite your expression using integers, or write some code that will automatically check if a float stores an integer number (up to numerical precision error) and act accordingly.
Here's a starting point for that:
def rewrite_polynomial(p):
args_list = []
if not p.is_Mul:
return None
for m in p.args:
if not m.is_Pow:
args_list.append(m)
else:
pow_val = m.args[1]
if pow_val.is_Float:
pow_val_int = int(pow_val)
if pow_val.epsilon_eq(pow_val_int):
args_list.append(Pow(m.args[0],Integer(pow_val_int)))
else:
args_list.append(m)
else:
args_list.append(m)
return Mul(*args_list)
n = Symbol('n')
f= n*((n+1)**1.0)
g = rewrite_polynomial(f)
print(g)
Based on Yakovs answer, I made a rewrite rule that makes a DFS traversal of the expression tree and replaces powers to integers in float type.
The code is probably not very efficient, but it worked for my use cases.
Since I'm not a sympy expert, I guess there are some edge cases where this code will break.
Anyways, here you go!
import sympy as s
def recurse_replace(expr,pred,func):
if len(expr.args) == 0:
return expr
else:
new_args = tuple(recurse_replace(a,pred,func) for a in expr.args)
if pred(expr):
return func(expr,new_args)
else:
return type(expr)(*new_args)
def rewrite(expr,new_args):
new_args = list(new_args)
pow_val = new_args[1]
pow_val_int = int(new_args[1])
if pow_val.epsilon_eq(pow_val_int):
new_args[1] = s.Integer(pow_val_int)
new_node = type(expr)(*new_args)
return new_node
def isfloatpow(expr):
out = expr.is_Pow and expr.args[1].is_Float
return out
def clean_exponents(expr):
return recurse_replace(expr,isfloatpow,rewrite)
x=s.symbols('x')
expr = (1+x) ** 1.0
s.pprint(expr)
expr2 = recurse_replace(expr,isfloatpow,rewrite)
s.pprint(expr2)
With output
1.0
(x + 1)
x + 1

Categories

Resources