4

I am trying to ceil the integer x divided by a constant:

>>> x = sympy.Symbol("x", integer=True)
>>> (x + 4 - 1) // 4
floor(x/4 + 3/4)

If we take this out of the context of sympy, the expression is incorrect when assuming integer arithmetic. For example, in python 2.7:

>>> floor(9/4 + 3/4)
2.0

What I want is an expression that, when evaluated in a different context, yields the desired (9 + 3) / 4 = 3.

Solutions so far:

sympy.Mul(x + 4 - 1, sympy.Pow(4, -1), evaluate=False)
sympy.factor((x + 4 - 1) / 4)

While these both give the desired (x + 3)/4, they have to be done explicitly for every expression.

I'm looking for something along the lines of:

>>> sympy.assume_integer()
>>> (x + 4 - 1) // 4
(x + 3) / 4

Context: Our project uses SymPy to generate C++, so we generate a string from the sympy expression which needs to evaluate correctly under integer arithmetic. Although floor(x/4 + 3/4).subs(x, 9) indeed yields 3, this is not the context we will evaluate the expression in.


The answer here suggests something along the lines of:

>>> ((x+4-1-(x+4-1)%4)/4)      
x/4 - Mod(x + 3, 4)/4 + 3/4

Which has the same issue as above, i.e. 3/4 is not an integer.

definelicht
  • 428
  • 2
  • 14
  • When I try `floor((x+4-1)/4).replace(x,9)` I do get `3`, which I also get for `floor(9/4+3/4)` and `floor(Rational(9,4) + Rational(3,4))`. Thus I do not understand your question and think your statement is wrong. – laolux Sep 01 '17 at 10:58
  • 1
    I've updated the question with an important detail: we need to use the resulting expression in a different context than sympy. – definelicht Sep 01 '17 at 11:43
  • Two points of clarification: (1) Are you at liberty to say what "outside context" you will be executing the SymPy-generated expression in? You mentioned Python 2.7. Is that the context? Will there be multiple possible contexts? (2) Would it be correct to say you are *really* looking for a way to generate, in SymPy, an expression that evaluates, in the target context, to `ceil(x / k)` where `k` is an integer constant, `x` is an integer variable, and `/` is "true" division? – John Y Sep 01 '17 at 14:28
  • @JohnY (1) it will be evaluated as C++ code, where the symbols refer to integer variables. (2) yes, in this case `ceil(x / k)`, but I'm generally interested if there's a way to let SymPy evaluate expressions in a way that does not change the meaning under integer arithmetic. – definelicht Sep 01 '17 at 14:39
  • Which result do you expect for `(8/3+4/3)` and `(10/3+4/3)`? Or can you for some reason be certain that this will never occur and your result is always exactly integer? – laolux Sep 02 '17 at 09:09

2 Answers2

2

If you want to generate C++ code anyway, you should use SymPy’s code printers, which are specifically designed for this purpose. For example:

from sympy.abc import x
from sympy.printing import cxxcode

expr = (x+3)//4
print(cxxcode(expr))
# 'floor((1.0L/4.0L)*x + 3.0L/4.0L)'

Inserting this into the C++ Code:

# include <stdio.h>
# include <math.h>

int main()
{
    int x = 9;
    printf("%Lf\n", floor((1.0L/4.0L)*x + 3.0L/4.0L));
}

This yields 3.000000. If desired, you need to convert the result to an integer.

Wrzlprmft
  • 4,234
  • 1
  • 28
  • 54
  • Yes, use the code printers. The `str` of a SymPy expression is designed to use SymPy semantics, which for `/` means regular division. If you want the semantics of another language use the code printer for that language. – asmeurer Sep 03 '17 at 09:19
  • By the way, I opened [an issue](https://github.com/sympy/sympy/issues/13253) on the SymPy tracker to make this give a more efficient result. – asmeurer Sep 03 '17 at 09:22
  • This is of course the best solution for printing C++ from SymPy. I will however leave the question open, as this does not answer the original question, which is whether we can make SymPy assume integer arithmetic when evaluating expressions. – definelicht Sep 04 '17 at 08:53
0

I suppose you could use sympy's factor method.

import sympy as sym
x = sym.symbols('x', integer=True)
sym.factor((x + 4 - 1) /4)

This yields (x+3)/4. Is that what you want?

Or try

sym.floor(sym.factor((x + 4 - 1) /4))

If you want floor((x+3)/4).

EDIT

You could define your own printing function:

def arith_print(x):
    print(sym.floor(sym.factor(x)))

This would take any expression you have shown so far and make print it in a arithmetic precision compatible way.

P.S.: To get >>> (x + 4 - 1) // 4 to work one needs a sufficiently recent version of sympy. 0.7.6 is too old.

laolux
  • 1,445
  • 1
  • 17
  • 28
  • Same result in python 2.7.13 and 3.6.2: `>>> from sympy import symbols` `>>> x = symbols("x", integer=True)` `>>> (x + 4 - 1) // 4` `floor(x/4 + 3/4)` – definelicht Sep 02 '17 at 11:19
  • Using either `sympy.factor` or specifying `evaluate=False` gives me a valid expression. But I'm looking for a more general solution that would take integer arithmetic into account _when_ performing evaluation, simplification, factoring etc. – definelicht Sep 02 '17 at 11:21
  • Ideally I'm looking for something along the lines of "sympy.assume_integer()" that does not perform factorizations, evaluations, simplification (...) that change the meaning under integer arithmetic. I've updated the question to state this clearly – definelicht Sep 02 '17 at 11:35
  • `//` works depending on sympy version... Ok, so reading your latest edits you want sympy to calculate with full precision all the way to the end and then automatically print the result in an artithmetic precision compatible format? – laolux Sep 02 '17 at 11:47