-
Notifications
You must be signed in to change notification settings - Fork 270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Symbolic Factorial and Binomial #1337
base: master
Are you sure you want to change the base?
Conversation
symengine/functions.cpp
Outdated
} else if (is_a<Integer>(*arg) | ||
or (is_a_Number(*arg) | ||
and not down_cast<const Number &>(*arg).is_exact())) { | ||
return gamma(add(arg, one)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a factorial
in ntheory
that you can use here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My concern is regarding code duplication that would occur. gamma
already has the cases defined. It would be a literal copy-paste.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This returns an instance of Gamma
sometimes. Can you return a Factorial
instance in that case?
5add636
to
1f41231
Compare
1f41231
to
8fafda5
Compare
a6245b6
to
8fa036d
Compare
@isuruf I think this has progressed enough for a review. |
8fa036d
to
7ba7150
Compare
@isuruf Can I please have a review of this? |
*integer(2)))) { | ||
return gamma(add(arg, one)); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SymEngine's gamma and sympy's factorial evaluates this for floating points. How about just returning gamma(arg + 1)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Floats are being evaluated here, these checks are just to prevent from returning instances of Gamma
. See here.
symengine/functions.cpp
Outdated
down_cast<const Integer &>(*k).as_uint()); | ||
} | ||
if (is_a<Rational>(*n) or is_a<RealDouble>(*n)) { | ||
if (down_cast<const Number &>(*sub(n, k)).is_negative()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this to under is_a_Number(*n) and is_a_Number(*k)
symengine/functions.cpp
Outdated
if (is_a_Number(*k) and down_cast<const Number &>(*k).is_zero()) { | ||
return one; | ||
} else if (is_a_Boolean(*n) or is_a_Boolean(*k) or is_a_Set(*n) | ||
or is_a_Set(*k)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't check this. Garbage values in -> garbage values out
symengine/functions.cpp
Outdated
return one; | ||
} else if (is_a<NaN>(*n)) { | ||
if ((is_a<Integer>(*k) | ||
and down_cast<const Number &>(*k).is_positive())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? Just return NaN
if any one of n
or k
is a NaN
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is SymPy
's output:
In [5]: binomial(nan, x)
Out[5]:
⎛nan⎞
⎜ ⎟
⎝ x ⎠
In [6]: binomial(nan, 3/2)
Out[6]:
⎛nan⎞
⎜ ⎟
⎝1.5⎠
In [7]: binomial(nan, -3)
Out[7]:
⎛nan⎞
⎜ ⎟
⎝ -3⎠
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If one of the arguments is NaN
, the value of the function doesn't make sense. Therefore a NaN
should be returned,
symengine/functions.cpp
Outdated
} else if (is_same_type(*n, *k)) { | ||
if (is_a<Integer>(*n)) { | ||
return binomial(down_cast<const Integer &>(*n), | ||
down_cast<const Integer &>(*k).as_uint()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is the most important case, move it to the top of if-else
symengine/functions.cpp
Outdated
or is_a_Set(*k)) { | ||
throw SymEngineException( | ||
"Boolean and Set objects not allowed in this context."); | ||
} else if (eq(*n, *k) and not(is_a<NaN>(*n) or is_a<Infty>(*n))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move the NaN check to above this, so that you don't have to check here again.
symengine/printer.cpp
Outdated
str_ = s.str(); | ||
} | ||
|
||
void StrPrinter::bvisit(const Binomial &x) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't need a special method here. See how sin
and others are handled
void StrPrinter::bvisit(const Factorial &x) | ||
{ | ||
std::ostringstream s; | ||
s << "(" << apply(x.get_arg()) << ")!"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
x!
should not be printed as (x)!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was actually done for these cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean you should check the precedence of the arg before printing the parenthesis. Take a look at how parenthesis are added. For example, x**n
vs (x+y)**n
where there is no parenthesis around x
in the first case
symengine/functions.cpp
Outdated
return zero; | ||
} | ||
} else if (is_a_Number(*k) and down_cast<const Number &>(*k).is_zero()) { | ||
return one; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also return 0
if k
is a number and k < 0
.
} | ||
|
||
RCP<const Basic> binomial(const RCP<const Basic> &n, const RCP<const Basic> &k) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here first check that k
is an integer. If k
is not an integer, return a Binomial
object.
29b5d87
to
91f1503
Compare
@isuruf Can I take up this issue in a new PR? I guess only the conflicts need to be corrected. |
@sudoWin Go for it. |
Relevant: #1336