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

Fix for UnivariatePolynomial::__eq__ #847

Closed
wants to merge 14 commits into from

Conversation

myluszczak
Copy link
Contributor

The current implementation of UnivariatePolynomial::__eq__ checks if the dictionaries of the two polynomials are exactly equal, and will return false if comparing polynomials with the dictionaries {{0,0},{1,2}} and {{1,2}} representing 0 + 2x and 2x respectively. Since addition does not remove the entries of a dictionary corresponding to terms that have zero coefficients, this can lead to a situation where SymEngine tells us that (x+1) + (x-1) != 2x, as shown below.
test.zip

My implementation replaces the call to map_uint_mpz_eq with a call to poly_dict_map_uint_mpz_eq, which returns true if two dictionaries are the same (excluding those entries where the coefficient is zero.)

@certik
Copy link
Contributor

certik commented Mar 5, 2016

I wonder if we should just make sure when the polynomial is created, that it is always in a canonical form, so that then you can literally just compare the dictionaries?

@isuruf what do you think?

@isuruf
Copy link
Member

isuruf commented Mar 5, 2016

Yes, that should be easier.
We can use UnivariatePolynomial::from_dict to do that. Current implementation is not necessary which checks if the dictionary size is 0 or 1, which is not needed. UnivariatePolynomial::from_dict should always return a polynomial.

@myluszczak
Copy link
Contributor Author

@isuruf So, something like this?

If we do it this way, we should probably add code to is_canonical to check that the dictionary contains no terms with zero coefficients. This would make the canonical form of the polynomial "0" be a polynomial with an empty dictionary, which would require changing some code in the tester and some of the code in printer.cpp.

@isuruf
Copy link
Member

isuruf commented Mar 5, 2016

@myluszczak, exactly. I think this looks good.

/*for(auto itter = dict.begin(); itter != dict.end(); itter++){
if(0 == itter->second)
return false;
}*/
Copy link
Member

Choose a reason for hiding this comment

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

You can uncomment this code

@isuruf
Copy link
Member

isuruf commented Mar 5, 2016

There are currently 3 ways of creating a polynomial.

  1. UnivariatePolynomial(const RCP<const Symbol> &var, const unsigned int &degree, map_uint_mpz&& dict);
  2. UnivariatePolynomial(const RCP<const Symbol> &var, const std::vector<integer_class> &v);
  3. static RCP<const UnivariatePolynomial> from_dict(const RCP<const Symbol> &var, map_uint_mpz &&d);

Instead of that we can do,

  1. UnivariatePolynomial(const RCP<const Symbol> &var, const unsigned int &degree, map_uint_mpz&& dict);
  2. static RCP<const UnivariatePolynomial> from_vec(const RCP<const Symbol> &var, const std::vector<integer_class> &v);
  3. static RCP<const UnivariatePolynomial> from_dict(const RCP<const Symbol> &var, map_uint_mpz &&d);

Only method 2 and 3 will call 1 and all other references to 1 will be replaced by 3.
What do you think?

@myluszczak
Copy link
Contributor Author

@isuruf I think that's a good plan, and I'll put that in the next commit.

Thanks for the head's up on the redundant checks in get_args; I missed that when I copied the code form the original implementation of from_dict.

@myluszczak
Copy link
Contributor Author

In the above commit I've done what I though would implement isuruf's idea above, however on my machine compilation stops when linking test_arit with the error message

[ 42%] Linking CXX executable test_arit
../../libsymengine.a(polynomial.cpp.o): In function `SymEngine::mul_uni_poly(Teuchos::RCP<SymEngine::UnivariatePolynomial const>, Teuchos::RCP<SymEngine::UnivariatePoly                                       nomial const>)':
/home/myluszcz/ECS193/SYMENGINE/symengine/symengine/polynomial.cpp:290: undefined reference to `SymEngine::UnivariatePolynomial::from_vec(Teuchos::RCP<SymEngine::Symbol                                        const> const&, std::vector<__gmp_expr<__mpz_struct [1], __mpz_struct [1]>, std::allocator<__gmp_expr<__mpz_struct [1], __mpz_struct [1]> > > const&)'
/home/myluszcz/ECS193/SYMENGINE/symengine/symengine/polynomial.cpp:292: undefined reference to `SymEngine::UnivariatePolynomial::from_vec(Teuchos::RCP<SymEngine::Symbol                                        const> const&, std::vector<__gmp_expr<__mpz_struct [1], __mpz_struct [1]>, std::allocator<__gmp_expr<__mpz_struct [1], __mpz_struct [1]> > > const&)'
../../libsymengine.a(series_generic.cpp.o): In function `SymEngine::UnivariatePolynomial::create(Teuchos::RCP<SymEngine::Symbol const> const&, std::vector<__gmp_expr<__                                       mpz_struct [1], __mpz_struct [1]>, std::allocator<__gmp_expr<__mpz_struct [1], __mpz_struct [1]> > > const&)':
/home/myluszcz/ECS193/SYMENGINE/symengine/symengine/polynomial.h:49: undefined reference to `SymEngine::UnivariatePolynomial::from_vec(Teuchos::RCP<SymEngine::Symbol co                                       nst> const&, std::vector<__gmp_expr<__mpz_struct [1], __mpz_struct [1]>, std::allocator<__gmp_expr<__mpz_struct [1], __mpz_struct [1]> > > const&)'
collect2: error: ld returned 1 exit status
symengine/tests/basic/CMakeFiles/test_arit.dir/build.make:104: recipe for target 'symengine/tests/basic/test_arit' failed
make[2]: *** [symengine/tests/basic/test_arit] Error 1
CMakeFiles/Makefile2:381: recipe for target 'symengine/tests/basic/CMakeFiles/test_arit.dir/all' failed
make[1]: *** [symengine/tests/basic/CMakeFiles/test_arit.dir/all] Error 2
Makefile:138: recipe for target 'all' failed
make: *** [all] Error 2

Since it's late, I'm going to shower and go to bed.

return make_rcp<const UnivariatePolynomial>(var, (--(d.end()))->first, std::move(d));
}

RCP<const UnivariatePolynomial> from_vec(const RCP<const Symbol> &var, const std::vector<integer_class> &v){
Copy link
Member

Choose a reason for hiding this comment

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

Change the name to UnivariatePolynomial::from_vec

@isuruf
Copy link
Member

isuruf commented Mar 5, 2016

Can you rebase on top of master and fix merge conflicts? @chenchfort should be able to help you out. If not let me know if you need my help.

@shivamvats
Copy link
Contributor

@myluszczak Are you developing in debug mode?

@myluszczak
Copy link
Contributor Author

@shivamvats Not in general, no. I just compiled and tested it in debug mode, though, and it still works on my machine. The main difference between the behavior of release and debug modes is that in debug mode we call the SYMENGINE_ASSERTs, right?

{{var, integer(d.begin()->first)}});
for(auto itter = d.begin(); itter != d.end(); itter++){
if(integer_class(0) == itter->second)
d.erase(itter);
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 probably what is causing the segfault. If you delete while iterating, the iterator becomes invalid.

@myluszczak
Copy link
Contributor Author

@isuruf So, a better implementation would be to create a new dictionary and only add to that if the coefficient is nonzero?

}
if(dict_.empty())
Copy link
Member

Choose a reason for hiding this comment

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

Space after if

@isuruf
Copy link
Member

isuruf commented Mar 6, 2016

+1. If you can rebase on top of master, we can get this merged

@myluszczak
Copy link
Contributor Author

I'm a bit new to git. That'd be git rebase master in the polydicteq branch, right?

@isuruf
Copy link
Member

isuruf commented Mar 6, 2016

Yes

@char-chen
Copy link
Contributor

You'll need to resolve the conflict.

@myluszczak
Copy link
Contributor Author

@chenchfort wait, which conflict?

@char-chen
Copy link
Contributor

Have you rebased? Did it complain about conflicts?

@myluszczak
Copy link
Contributor Author

I'm rebasing now, and it is complaining about conflicts currently. I am currently resolving them.

@myluszczak
Copy link
Contributor Author

@chenchfort Alright, so I finished the rebase, but now git refuses to push

``To https://github.com/chenchfort/symengine
! [rejected] polydicteq -> polydicteq (non-fast-forward)
error: failed to push some refs to 'https://github.com/chenchfort/symengine'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

--resolved

@char-chen
Copy link
Contributor

Ok force pushed. Looks good.

@isuruf
Copy link
Member

isuruf commented Mar 7, 2016

Closed in favour of #855

@isuruf isuruf closed this Mar 7, 2016
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.

6 participants