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

Simplify Complex Arguments to Exp #1332

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

Conversation

ShikharJ
Copy link
Member

Relevant: #1330

@ShikharJ
Copy link
Member Author

Ping @certik.

@ShikharJ
Copy link
Member Author

@isuruf Can you review as well?

Copy link
Contributor

@certik certik left a comment

Choose a reason for hiding this comment

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

This seems like the right fix. I'll wait for @isuruf to also review it.

for (const auto &p : dict) {
if (eq(*p.first, *pi) and eq(*p.second, *one)) {
return add(cos(mul(mul(I, minus_one), b)),
mul(I, sin(mul(mul(I, minus_one), b))));
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems correct, as you can always do this transformation. The only question remaining is whether this transformation is only applied when it can simplify the exp(), and it seems it is.

It is probably possible to optimize this return statement further, using the information we know (i.e. not calling sin/cos at all), but that can be done later.

@certik
Copy link
Contributor

certik commented Sep 27, 2017

I don't understand how the code coverage could decrease with this PR. By clicking on it, it found some difference in add.cpp? That's weird.

@isuruf
Copy link
Member

isuruf commented Sep 27, 2017

Can you add some examples like ?

exp(I*pi/2 + x)
exp(I*pi/10 + x)
exp(I*pi*x)/exp(x)

@ShikharJ
Copy link
Member Author

ShikharJ commented Oct 2, 2017

Ping @isuruf

REQUIRE(eq(*r1, *r2));

r1 = exp(add(div(mul(I, pi), integer(10)), x));
r2 = mul(exp(x), exp(div(mul(I, pi), integer(10))));
Copy link
Member

Choose a reason for hiding this comment

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

Above 2 check the same thing. Can you comment here with the string form of r1?

REQUIRE(eq(*r1, *mul(I, minus_one)));

r1 = div(exp(mul(mul(I, pi), x)), exp(x));
r2 = mul(exp(mul(minus_one, x)), exp(mul(mul(I, pi), x)));
Copy link
Member

Choose a reason for hiding this comment

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

Can you comment the string form of r1?

REQUIRE(eq(*r1, *r2));

r1 = exp(add(div(mul(I, pi), integer(10)), x));
REQUIRE(r1->__str__() == "exp(x)*(I*sin((1/10)*pi) + cos((1/10)*pi))");
Copy link
Member

Choose a reason for hiding this comment

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

This is too complicated than it needs to be.

REQUIRE(eq(*r1, *mul(I, minus_one)));

r1 = div(exp(mul(mul(I, pi), x)), exp(x));
REQUIRE(r1->__str__() == "exp(-x + I*x*pi)");
Copy link
Member

Choose a reason for hiding this comment

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

Can you change this test to checking that r1 is a Pow and that the base and exp are E and -x + I * x * pi respectively ?

if ((is_a<Integer>(*mul(coef, I))
or (is_a<Rational>(*mul(coef, I))
and (eq(*mul(coef, I), *rational(1, 2))
or eq(*mul(coef, I), *rational(-1, 2)))))) {
Copy link
Member

@isuruf isuruf Oct 3, 2017

Choose a reason for hiding this comment

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

mul(coef, I) is calculated repeatedly. How about exp(3*pi*I/2)?

RCP<const Number> coef = down_cast<const Add &>(*b).get_coef();
RCP<const Basic> s = one;
for (const auto &p : dict) {
if (eq(*p.first, *pi) and is_a_Complex(*p.second)) {
Copy link
Member

Choose a reason for hiding this comment

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

Use is_a<Complex> to keep the consistency as above.

for (const auto &p : dict) {
if (eq(*p.first, *pi) and eq(*p.second, *one)) {
return add(cos(mul(mul(I, minus_one), b)),
mul(I, sin(mul(mul(I, minus_one), b))));
Copy link
Member

Choose a reason for hiding this comment

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

Since we only care about multiples of I*pi/2, how about checking for the multiple, and writing the formula for the 4 cases? Will be faster.

Copy link
Member Author

Choose a reason for hiding this comment

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

To be clear, which four cases are we talking about?

Copy link
Member

Choose a reason for hiding this comment

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

If the arg is n*I*pi/2, whether n = 0,1,2,3 mod 4

RCP<const Basic> s = one;
for (const auto &p : dict) {
if (eq(*p.first, *pi) and is_a_Complex(*p.second)) {
s = exp(mul(p.first, p.second));
Copy link
Member

Choose a reason for hiding this comment

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

Can you take out the logic of mul to a new function and then call it from here so that we know if s was simplified that it is a number, else we can do Add::dict_add_term(new_dict, p.second, p.first);

@isuruf
Copy link
Member

isuruf commented Oct 3, 2017

@ShikharJ, optimizations I'm talking about maybe very small and won't matter for most of the cases, but when changing a very important function like pow, add, mul, I want to be extra careful not to add overhead.

@certik
Copy link
Contributor

certik commented Oct 3, 2017

Yes, we have to be very careful when modifying core routines like in this PR.

@ShikharJ
Copy link
Member Author

ShikharJ commented Oct 4, 2017

Ping @isuruf.

return mul(s, make_rcp<const Pow>(
a, Add::from_dict(coef, std::move(new_dict))));
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to carefully benchmark these two if branches, before and after this PR. So the first if clause would run for something like E^(a*b). The second one for E^(a+b). Correct?

If so, then we need to benchmark these two things and similar things a lot.

@@ -87,6 +87,25 @@ int Pow::compare(const Basic &o) const
return base_cmp;
}

bool exp_mul_helper(const RCP<const Basic> &b)
Copy link
Member

Choose a reason for hiding this comment

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

How about this function returning -1, 0, 1,2,3 where -1 is when it is not what we want and the others n in the function beloe

RCP<const Number> coef = down_cast<const Add &>(*b).get_coef();
RCP<const Basic> s = one;
for (const auto &p : dict) {
if (eq(*p.first, *pi)
Copy link
Member

Choose a reason for hiding this comment

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

To do this, you don't need to iterate the dictionary. Just use dict.find to find the value pi.

Copy link
Member Author

Choose a reason for hiding this comment

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

Is there a way I can remove that particular entry of pi as well? That way, there'd be no need of iteration at all.

for (const auto &p : dict) {
if (eq(*p.first, *pi)
and exp_mul_helper(mul(p.first, p.second))) {
s = exp(mul(p.first, p.second));
Copy link
Member

Choose a reason for hiding this comment

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

Can a call to exp be avoided here?

Copy link
Member Author

@ShikharJ ShikharJ Oct 22, 2017

Choose a reason for hiding this comment

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

At the expense of code duplication it can be. I can't seem to think of another way.

@certik
Copy link
Contributor

certik commented Nov 2, 2017

What needs to be done here to finish it?

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

Successfully merging this pull request may close these issues.

None yet

3 participants