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

Matrix connected components decomposition #19045

Merged
merged 8 commits into from
Apr 3, 2020

Conversation

sylee957
Copy link
Member

@sylee957 sylee957 commented Apr 1, 2020

References to other Issues or PRs

#16207

Brief description of what is fixed or changed

I see there were some needs of using connected_components.
So I wrap two APIs for this first for finding connected components and second for finding a similar matrix which is block diagonal.

I would also wrap most of the new decomposition methods using the most economical structure possible (with block diagonal matrix and permutation matrix) because

  1. It is the most economical representation.
  2. It loses a lot of usefulness after people start to run some search algorithms to find block structures or permutation from the explicit result, and it poorly utilizes some intermediate results. (I saw this was done poorly in jordan-decomposition related methods)

But someone would find these stuff unintuitive or difficult to use since I don't think that matrix expressions API is more famous than Matrix.
In the examples, I have just used as_explicit because it prints block diagonal matrix into some awkward forms.

Other comments

Release Notes

  • matrices
    • Added connected_components and connected_components_decomposition for matrix which decomposes a matrix into a block diagonal form.
    • Added todok function to find dictionary of keys format from any dense or sparse matrices.
    • Added BlockDiagMatrix.get_diag_blocks to provide an user API to get diagonal blocks from the matrix.

@sympy-bot
Copy link

sympy-bot commented Apr 1, 2020

Hi, I am the SymPy bot (v158). I'm here to help you write a release notes entry. Please read the guide on how to write release notes.

Your release notes are in good order.

Here is what the release notes will look like:

  • matrices
    • Added connected_components and connected_components_decomposition for matrix which decomposes a matrix into a block diagonal form. (#19045 by @sylee957)

    • Added todok function to find dictionary of keys format from any dense or sparse matrices. (#19045 by @sylee957)

    • Added BlockDiagMatrix.get_diag_blocks to provide an user API to get diagonal blocks from the matrix. (#19045 by @sylee957)

This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.6.

Note: This comment will be updated with the latest check if you edit the pull request. You need to reload the page to see it.

Click here to see the pull request description that was parsed.

<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->
#16207

#### Brief description of what is fixed or changed

I see there were some needs of using `connected_components`.
So I wrap two APIs for this first for finding connected components and second for finding a similar matrix which is block diagonal.

I would also wrap most of the new decomposition methods using the most economical structure possible (with block diagonal matrix and permutation matrix) because
1. It is the most economical representation.
2. It loses a lot of usefulness after people start to run some search algorithms to find block structures or permutation from the explicit result, and it poorly utilizes some intermediate results. (I saw this was done poorly in jordan-decomposition related methods)

But someone would find these stuff unintuitive or difficult to use since I don't think that matrix expressions API is more famous than `Matrix`.
In the examples, I have just used `as_explicit` because it prints block diagonal matrix into some awkward forms.

#### Other comments

#### Release Notes

<!-- Write the release notes for this release below. See
https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more information
on how to write release notes. The bot will check your release notes
automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->

- matrices
  - Added `connected_components` and `connected_components_decomposition` for matrix which decomposes a matrix into a block diagonal form.
  - Added `todok` function to find dictionary of keys format from any dense or sparse matrices.
  - Added `BlockDiagMatrix.get_diag_blocks` to provide an user API to get diagonal blocks from the matrix.
<!-- END RELEASE NOTES -->

Update

The release notes on the wiki have been updated.

@sympy-bot
Copy link

sympy-bot commented Apr 1, 2020

🟠

Hi, I am the SymPy bot (v158). I've noticed that some of your commits add or delete files. Since this is sometimes done unintentionally, I wanted to alert you about it.

This is an experimental feature of SymPy Bot. If you have any feedback on it, please comment at sympy/sympy-bot#75.

The following commits add new files:

  • f1f4226:
    • sympy/matrices/graph.py
    • sympy/matrices/tests/test_graph.py

If these files were added/deleted on purpose, you can ignore this message.

@codecov
Copy link

codecov bot commented Apr 1, 2020

Codecov Report

Merging #19045 into master will increase coverage by 0.049%.
The diff coverage is 95.238%.

@@              Coverage Diff              @@
##            master    #19045       +/-   ##
=============================================
+ Coverage   75.737%   75.787%   +0.049%     
=============================================
  Files          648       649        +1     
  Lines       168798    168852       +54     
  Branches     39786     39798       +12     
=============================================
+ Hits        127844    127969      +125     
+ Misses       35387     35319       -68     
+ Partials      5567      5564        -3

if M[i, i] is M.zero:
E.append((i, i))

return strongly_connected_components((V, E))
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably be connected_components in which case duplicate edges don't need to be added here.

Copy link
Contributor

Choose a reason for hiding this comment

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

With todok this can just be:

return connected_components((range(N), M.todok()))

Copy link
Member Author

Choose a reason for hiding this comment

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

Now, the test fails in python3.5 probably because of dictionary ordering. Does connected_components make unique results? If not, I'm thinking of sorting the output.

Copy link
Contributor

Choose a reason for hiding this comment

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

The results returned from connected_components can be in any order. The strongly_connected_components function returns components in topological order which is also not unique.

@oscarbenjamin
Copy link
Contributor

What is the expected way to get the blocks from a block matrix. I see I can use args but I expected blocks to do it except:

In [27]: A                                                                                                                        
Out[27]: 
⎡a  0  b  0⎤
⎢          ⎥
⎢0  e  0  f⎥
⎢          ⎥
⎢c  0  d  0⎥
⎢          ⎥
⎣0  g  0  h⎦

In [28]: P, B = A.connected_components_decomposition()                                                                            

In [29]: B                                                                                                                        
Out[29]: 
⎡⎡a  b⎤        ⎤
⎢⎢    ⎥    𝟘   ⎥
⎢⎣c  d⎦        ⎥
⎢              ⎥
⎢        ⎡e  f⎤⎥
⎢  𝟘     ⎢    ⎥⎥
⎣        ⎣g  h⎦⎦

In [30]: B.blocks                                                                                                                 
Out[30]: 
⎡⎡a  b⎤        ⎤
⎢⎢    ⎥    𝟘   ⎥
⎢⎣c  d⎦        ⎥
⎢              ⎥
⎢        ⎡e  f⎤⎥
⎢  𝟘     ⎢    ⎥⎥
⎣        ⎣g  h⎦⎦

In [31]: B.args                                                                                                                   
Out[31]: 
⎛⎡a  b⎤  ⎡e  f⎤⎞
⎜⎢    ⎥, ⎢    ⎥⎟
⎝⎣c  d⎦  ⎣g  h⎦⎠

There also seems to be a bug here:

In [33]: P*B                                                                                                                      
---------------------------------------------------------------------------
AttributeError
...
~/current/sympy/sympy/sympy/matrices/expressions/blockmatrix.py in <listcomp>(.0)
    331 
    332     def _eval_inverse(self, expand='ignored'):
--> 333         return BlockDiagMatrix(*[mat.inverse() for mat in self.args])
    334 
    335     def _eval_transpose(self):

AttributeError: 'MutableDenseMatrix' object has no attribute 'inverse'
...

I guess that should be inv rather than inverse.

@sylee957
Copy link
Member Author

sylee957 commented Apr 1, 2020

I see blocks was reserved to align with the same properties BlockMatrix, so it always returns some storage of immutable matrix.

def blocks(self):
from sympy.matrices.immutable import ImmutableDenseMatrix
mats = self.args
data = [[mats[i] if i == j else ZeroMatrix(mats[i].rows, mats[j].cols)
for j in range(len(mats))]
for i in range(len(mats))]
return ImmutableDenseMatrix(data, evaluate=False)

I think that the only thing api that came before to serve the purpose is get_diag_blocks so if we don't want users to use args, it can be made for generic matrix expression API.

@smichr
Copy link
Member

smichr commented Apr 3, 2020

This is looking good. I hope some linear algebra users will smile after seeing this. I'll leave you and Oscar to figure out when it is ready.

@oscarbenjamin
Copy link
Contributor

The reason I ask about args is because looking at this the first thing that comes to my mind is how do I actually get the blocks if I want to use them for something. I think that the doc should explain how to use this. The purpose of getting the blocks is so that you can compute eigenvalues etc so you need the block as a matrix on its own. Putting it altogether in a blockmatrix as a decomposition is nice but in practice not what is wanted in a situation where you would want to use this.

@oscarbenjamin
Copy link
Contributor

Looks good.

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

Successfully merging this pull request may close these issues.

None yet

4 participants