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

Bug Fixed : Matrix power of identity matrix fails #9823 #9830

Closed
wants to merge 0 commits into from

Conversation

kevalds51
Copy link
Contributor

Added the test to exercise the code change. (as requested by the @moorepants )
Modified the code such that no error is generated for both MatPow method and ** operator.
removed extra blank line which resulted in test_code_quality fail.

@bjodah
Copy link
Member

bjodah commented Aug 17, 2015

I think this looks fine and is a nice addition, one peculiar inconsistency I noticed though:

>>> I3=sympy.Identity(3)
>>> I3**3
I
>>> I3**-1
I^-1
>>> (I3**-1).doit()
I

i.e. for negative powers the doit() call is neccessary for simplification.
Maybe there is a good reason for this?

@kevalds51
Copy link
Contributor Author

Thanks for pointing that out @bjodah
This can be easily solved by changing the position of the elif condition in line 119-120 and putting it above the 'elif other is isInverse' statement

I will make the change and update the pull request.
Cheers

@oliverlee
Copy link
Contributor

@bjodah @kevalds51 This problem also occurs with ZeroMatrix as MatPow only explicitly evaluates instances of MatrixBase: #9823 (comment)

@moorepants
Copy link
Member

@kevalds51, @oliverlee and I just spoke. We can accept this PR instead of Oliver's but you need to fix the ZeroMatrix issue here too and add tests for it. Maybe see Oliver's PR for how he did it.

@kevalds51
Copy link
Contributor Author

@moorepants
Thanks for the advice, i'll start working on it right off.

@kevalds51
Copy link
Contributor Author

@moorepants I am getting this test_code_quality fail, can you suggest me on how to cope up with it.

assert MatPow(l, 2).doit() == l
assert MatPow(l, -1).doit() == l
assert MatPow(l, 0).doit() == l

Copy link
Member

Choose a reason for hiding this comment

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

You have trailing spaces on this line. FYI you should run ./bin/test sympy/utilities/tests/test_code_quality.py the output will tell you about the code quality issues.

Here's what I get

$ ./bin/test sympy/utilities/tests/test_code_quality.py
================================================================ test process starts =================================================================
executable:         /home/leosartaj/projects/sympy/my/bin/python  (2.7.10-final-0) [CPython]
architecture:       64-bit
cache:              yes
ground types:       python 
random seed:        56687482
hash randomization: on (PYTHONHASHSEED=2022273946)

sympy/utilities/tests/test_code_quality.py[6] F.....                                                                                            [FAIL]

______________________________________________________________________________________________________________________________________________________
_______________________________________________ sympy/utilities/tests/test_code_quality.py:test_files ________________________________________________
  File "/home/leosartaj/projects/sympy/sympy/utilities/tests/test_code_quality.py", line 228, in test_files
    check_directory_tree(SYMPY_PATH, test, exclude)
  File "/home/leosartaj/projects/sympy/sympy/utilities/tests/test_code_quality.py", line 107, in check_directory_tree
    check_files(glob(join(root, pattern)), file_check, exclusions)
  File "/home/leosartaj/projects/sympy/sympy/utilities/tests/test_code_quality.py", line 123, in check_files
    file_check(fname)
  File "/home/leosartaj/projects/sympy/sympy/utilities/tests/test_code_quality.py", line 146, in test
    test_this_file(fname, test_file)
  File "/home/leosartaj/projects/sympy/sympy/utilities/tests/test_code_quality.py", line 169, in test_this_file
    assert False, message_space % (fname, idx + 1)
AssertionError: File contains trailing whitespace: /home/leosartaj/projects/sympy/sympy/matrices/expressions/tests/test_matpow.py, line 108.

================================================ tests finished: 5 passed, 1 failed, in 2.88 seconds =================================================
DO *NOT* COMMIT!

@kevalds51
Copy link
Contributor Author

@moorepants Is this ok to be merged? can you please review this again and tell me.

@leosartaj I appreciate it, that trailing space troubled me a lot earlier.

@@ -70,7 +70,7 @@ def test_doit_square_MatrixSymbol_symsize():

def test_doit_with_MatrixBase():
X = ImmutableMatrix([[1, 2], [3, 4]])
assert MatPow(X, 0).doit() == ImmutableMatrix(Identity(2))
assert MatPow(X, 0).doit().as_explicit() == ImmutableMatrix(Identity(2))
Copy link
Member

Choose a reason for hiding this comment

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

Why is the test changed? You shouldn't change a test unless it was testing against an incorrect solution.

You're new code should not affect existing tests (unless the tests were wrong), it should only need new tests added.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@moorepants
I did it because ImmutableMatrix(Identity(2)) returns the matrix in explicit form. (So i guess the test is wrong.)
So inspite of ImmutableMatrix(Identity(2)), it can be just : Identity(2).
or we can just check the explicit form of LHS term

@moorepants
Copy link
Member

@oliverlee Can you review this since you thought about it more?

@oliverlee
Copy link
Contributor

We want any powers of Identity and positive powers of (square) ZeroMatrix to be equal to themselves, not just the explicit representation.

Test functions names should use Identity and ZeroMatrix as those are the classes being tested and to conform with other written tests.

Also, I wasn't thinking when writing my tests, but the inverse of ZeroMatrix is not ZeroMatrix.

@jksuom
Copy link
Member

jksuom commented Aug 21, 2015

Should the 0'th power of ZeroMatrix also be defined?

@oliverlee
Copy link
Contributor

It is here:

@jksuom
Copy link
Member

jksuom commented Aug 21, 2015

OK, that's what I had in mind.

@kevalds51
Copy link
Contributor Author

@oliverlee
Ill take that very important observation into consideration.
Thanks

@kevalds51
Copy link
Contributor Author

I don't understand why the most recent commit is failing, can anyone please help.

@leosartaj
Copy link
Member

This is probably due to #9846. This is fixed now. You should update your branch with the current master.

@kevalds51
Copy link
Contributor Author

@oliverlee @moorepants
Does this look good now?

@@ -70,7 +70,7 @@ def test_doit_square_MatrixSymbol_symsize():

def test_doit_with_MatrixBase():
X = ImmutableMatrix([[1, 2], [3, 4]])
assert MatPow(X, 0).doit() == ImmutableMatrix(Identity(2))
assert MatPow(X, 0).doit() == Identity(2)
Copy link
Contributor

Choose a reason for hiding this comment

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

This test shouldn't change. If you take an instance of ImmutableMatrix to the zero power, you expect to get back an ImmutableMatrix.

@oliverlee
Copy link
Contributor

  • The use of as_explicit() in your ZeroMatrix and Identity tests seem unnecessary; positive powers should return the same class.
  • I don't think the result of taking an ImmutableMatrix to the zero power should be a different class but maybe someone else who is more familiar with the matrix stuff can weigh in.
  • I'm not sure how errors should be handled with regards to the ZeroMatrix taken to negative powers. Maybe a separate issue should be opened for that?

Other than that, looks fine to me.

@kevalds51
Copy link
Contributor Author

@oliverlee
So should I just remove the unnecessary test cases? Is that all?

@oliverlee
Copy link
Contributor

@kevalds51 Sure, and also revert changes to that one test case. I don't know how errors should be raised though.

@kevalds51
Copy link
Contributor Author

@oliverlee
The issue with the test case is that it returns matrix in explicitly! This leads to an assertion error.
So either I have to change the RHS to Identity(2) or test for LHS explicitly as shown below

X = ImmutableMatrix([[1, 2], [3, 4]])
assert MatPow(X, 0).doit() == Identity(2)

or

X = ImmutableMatrix([[1, 2], [3, 4]])
assert MatPow(X, 0).doit().as_explicit() == ImmutableMatrix(Identity(2))

Can you or @moorepants help me with this

@oliverlee
Copy link
Contributor

@kevalds51
In MatPow.doit(), a base argument that is an instance of MatrixBase will always result in a call to base.__pow__(exp), unless the exponent is 1. In either case, an instance of MatrixBase is returned.

Your patch always returns an instance of Identity if the exponent is zero without taking the class of the base into account, which is change from the previous behavior.

@kevalds51
Copy link
Contributor Author

@oliverlee How about this?
I guess this commit is quite suitable, and meets all the requirements specified so far.
Good enough to merge?

@kevalds51
Copy link
Contributor Author

@bjodah can you please review this?

@@ -110,7 +110,11 @@ def __rmul__(self, other):
def __pow__(self, other):
if not self.is_square:
raise ShapeError("Power of non-square matrix %s" % self)
if other is S.NegativeOne:
if self.is_ZeroMatrix:
return self
Copy link
Member

Choose a reason for hiding this comment

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

This seems to return the zero matrix for all powers while line 467 returns the identity for the 0th power of the zero matrix.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jksuom That is because, zero matrix raised to any positive power is zero matrix itself, whereas zero matrix raised to power 0 is an Identity matrix.

Copy link
Member

Choose a reason for hiding this comment

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

Will the exponent other be positive here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes.
If an instance of a zero matrix is raised to -ve power it will raise the value error saying matrix is invertible, as in : (ZeroMatrix(3,3))**-2

Copy link
Member

Choose a reason for hiding this comment

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

It seems that ZeroMatrix.__pow__ already takes care of all powers of a ZeroMatrix: zeroth, negative (could be if other < 0:) and positive. Under which conditions would a ZeroMatrix appear as an argument of MatrixExpr.__pow__ making this line necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After running a few checks, I agree to you that this line is redundant, I will make the changes as soon as possible.

Earlier I wasn't able to get you, sorry for the misunderstanding.

@jksuom
Copy link
Member

jksuom commented Aug 29, 2015

It's a PyPy issue. I have restarted the job.

@kevalds51
Copy link
Contributor Author

@jksuom All good now?

@@ -465,6 +467,8 @@ def __pow__(self, other):
raise ShapeError("Power of non-square matrix %s" % self)
if other == 0:
return Identity(self.rows)
if other<1:
Copy link
Contributor

Choose a reason for hiding this comment

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

other<1 -> other < 1 , space needed

@gxyd
Copy link
Contributor

gxyd commented Aug 30, 2015

You can insert space at all other places as well, it makes the code more of readable.

assert MatPow(l, 2).doit() == l
assert MatPow(l, -1).doit() == l
assert MatPow(l, 0).doit() == l

Copy link
Contributor

Choose a reason for hiding this comment

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

One more line here.

@gxyd
Copy link
Contributor

gxyd commented Aug 31, 2015

@kevalds51 you should take a look at this comment.

@kevalds51
Copy link
Contributor Author

@oliverlee You want to review this one more time?

@kevalds51
Copy link
Contributor Author

@moorepants does this look good?

@@ -56,14 +56,25 @@ def doit(self, **kwargs):
args = self.args
base = args[0]
exp = args[1]
if isinstance(base, MatrixBase) and exp.is_number:
if exp.is_zero and base.is_square:
try:
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure why you're using a try/catch statement to evaluate an object of instance MatrixBase to the zero power. It's already defined here:

def __pow__(self, num):

But it looks like all tests pass and this addresses the issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was done to deal with special cases, such as instances of Immutable matrix (being raised to power 0), where I have to return it in the form of base.class(Identity(base.shape[0])), and not just as Identity((base.shape[0])).

If not done so, there will be issues while dealing with Immutable matrix instance or other similar ones. (It did lead to assertion errors in test cases in previous commits)

Copy link
Contributor

Choose a reason for hiding this comment

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

Can't you check if it's an instance of MatrixBase and calculate the result normally? There weren't any problems with that before so I don't see why that wouldn't work here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To solve the issue in the first place (for powers of both Identity and Zero Matrix), changes were made that required further adding of conditions.
Also there were other examples that gave wrong results for power zero (some were not tested in the test cases), to deal with that I had to add try/catch to return the proper instance.

@kevalds51
Copy link
Contributor Author

I'm facing some problems in managing branches, so I'll be closing this pull request.

@bjodah Should I create another request (this exact same one) after I fix the issues on my machine?

@bjodah
Copy link
Member

bjodah commented Sep 14, 2015

@kevalds51, there really shouldn't be a need for you to open another pull request. If you do, the people who have reviewed this PR would have to go over everything again from scratch (or cross reference everything from the review here). More specifically, what troubles are you facing with git, maybe we can help?

@kevalds51
Copy link
Contributor Author

That would be great. I just got used to Git a few weeks back.
Talking of this pull request, I pushed it from the master branch (of the forked repo on my machine). I don't know what to do if I want to work on some other issue...

@bjodah
Copy link
Member

bjodah commented Sep 14, 2015

I see. You should be able to do something along these lines:

$ git remote add upstream git://github.com/sympy/sympy.git
$ git fetch upstream
$ git checkout -b a_new_branch
$ git rebase upstream/master
$ # edit some files, commit the changes and push them to your repo:
$ git push origin --set-upstream a_new_branch
$ # if you want to go back to your master branch (which currently corresponds to this PR):
$ git checkout master

EDIT: added fetch on row 2

@kevalds51
Copy link
Contributor Author

Worked nicely, thanks

@moorepants
Copy link
Member

@kevalds51 Any reason you closed this?

@kevalds51
Copy link
Contributor Author

I had pushed from master of my local repo, this also led to merge conflicts in new PRs.
I have saved this work, do you want me to open another PR for this?

@moorepants
Copy link
Member

If we haven't gotten your work on this merged, we want to. You can open a new PR if you are having git issues. But in the future, ask us about the git issues and merge conflicts and we can help you solve them.

@kevalds51
Copy link
Contributor Author

Sure, it is settled once and for all. As of now, I'll open a new PR.

@kevalds51
Copy link
Contributor Author

Please refer to PR #9978

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.

7 participants