Skip to content
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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

ShikharJ
Copy link
Member

@ShikharJ ShikharJ commented Oct 4, 2017

Relevant: #1336

} 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));
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay.

Copy link
Member

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?

@ShikharJ ShikharJ force-pushed the Combinatorics branch 2 times, most recently from a6245b6 to 8fa036d Compare January 14, 2018 11:14
@ShikharJ ShikharJ changed the title [WIP] Implement Symbolic Factorial and Binomial Implement Symbolic Factorial and Binomial Jan 14, 2018
@ShikharJ
Copy link
Member Author

@isuruf I think this has progressed enough for a review.

@ShikharJ
Copy link
Member Author

@isuruf Can I please have a review of this?

*integer(2)))) {
return gamma(add(arg, one));
}
}
Copy link
Member

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)?

Copy link
Member Author

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.

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()) {
Copy link
Member

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)

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)) {
Copy link
Member

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

return one;
} else if (is_a<NaN>(*n)) {
if ((is_a<Integer>(*k)
and down_cast<const Number &>(*k).is_positive())) {
Copy link
Member

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

Copy link
Member Author

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⎠

Copy link
Member

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,

} 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());
Copy link
Member

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

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))) {
Copy link
Member

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.

str_ = s.str();
}

void StrPrinter::bvisit(const Binomial &x)
Copy link
Member

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()) << ")!";
Copy link
Member

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)!

Copy link
Member Author

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.

Copy link
Member

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

return zero;
}
} else if (is_a_Number(*k) and down_cast<const Number &>(*k).is_zero()) {
return one;
Copy link
Member

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)
{
Copy link
Member

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.

@sudoWin
Copy link
Contributor

sudoWin commented Jan 14, 2021

@isuruf Can I take up this issue in a new PR?

I guess only the conflicts need to be corrected.

@ShikharJ
Copy link
Member Author

@sudoWin Go for it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants