-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Make all matrix properties accessible as @property #15883
Comments
Generally properties should be cheap to compute, and if it isn't cheap it should be a function. That doesn't seem to be the case here though. However, the fact that there are arguments to the function is another reason it has to be a function. I agree there's no way to do a real deprecation here if we wanted to change this. Also, atoms comes from Basic, where it is a function (it takes an optional parameter). |
I would like to contribute. |
This isn't a good issue to contribute on. We first need to decide if it is worth making this change. I am thinking it might not, as it would be a backwards incompatible change and we cannot do a deprecation. |
I would personally have some question about, for which criteria should a computation to be considered cheap or expensive? For example, almost every matrix properties would have at least O(n^2) time complexity, for obvious reason that there should be elementwise investigation to search for a certain property. However, to generalize that anything that needs elementwise investigation on 'data' should be considered expensive in SymPy, I think a lot of SymPy's assumption systems on |
An idea I have is that I would make everything wrapped under I would also split I'd try to finish this discussion before next release, since it introduces a lot of mess for working with matrix methods. Some users may face some abrupt changes, but I'd expect that abruptly changing to using I would expect some other questions like how should things be simplified that I have not answered, but I will tell you the reasons. Any questions? |
Instead of splitting |
Unfortunately, it's a different matter since |
Good point, mixed it up with the quick out on symmetric real matrices. How about |
I would prefer something like We already have bugs from In [19]: Matrix([[0, 0], [0, 0]]).is_zero
Out[19]: True
In [20]: Integral(Matrix([[0, 0], [0, 0]]), x)
Out[20]:
⌠
⎮ ⎡0 0⎤
⎮ ⎢ ⎥ dx
⎮ ⎣0 0⎦
⌡
In [21]: Integral(Matrix([[0, 0], [0, 0]]), x).doit()
Out[21]: 0 |
On another note, and please disregard if you are sick of hearing of caching, but if you guys don't like the idea of custom matrix caching - @sylee957 because of memory usage for memoizing and @oscarbenjamin for not wanting to add more crud to an already messy matrix interface - then consider adding one or both of the following to the immutable matrix classes:
This will enable caching of these but only for the immutable classes (which I understand is the direction the matrix module is heading in anyway). For @oscarbenjamin it uses the current SymPy caching mechanism and for @sylee957 it is a fixed size cache so memory usage will not get out of control. And it has a decent impact on a single call to
Without caching this call takes 1.34s, with caching it is 0.81s. Of the two the EDIT: I am referring to the behavior of EDIT2: I mention caching in a theoretical discussion because if all these things become properties and the old functions are deprecated somehow then it makes sense to cache the expensive ones. |
We should separate out functions like eigenvals. They should be separate functions that sympify their args and use cacheit. |
You mean standalone function as opposed to matrix method. Would cacheit work if calling |
I (personally) think that all substantial code should be split out as separate functions. There is no need to deprecate the methods but they should be simple wrappers and the docs should suggest the functions: class Matrix:
def eigenvals(self, *args, **kwargs):
'''Compute the eigenvalues of a matrix. See eigenvals function for details'''
from sympy.matrices.eigen import eigenvals
return eigenvals(self, *args, **kwargs)
@cacheit
def eigenvals(M):
M = _sympify(M)
# Actual code here Then internally all calculations are done with immutable matrices so anything useful can be cached. Then we can have a whole module full of different functions for computing eigenvalues/vectors which provides a natural place to document the different methods and when they might be useful. |
I understand what you mean, I was just curious if cacheit would work with mutable matrix arguments, I understood we would be returning immutable, but had a look through the lru_cache code and it would handle it ... obviously ... as it does mutable lists or dicts. On the other hand a design question, is it wise to always force operations to return immutable matrices? Would there be cases where someone would be working with large matrices and would want to change some elements of some of these returned matrices in place - if so they would have to create a new mutable matrix from the returned immutable even if the original argument was mutable. |
We should soft-deprecate mutable matrices and encourage users to use immutable matrices. Having By "soft-deprecate" I mean that we should make it straight-forward to do everything with immutable matrices and emphasise ways of doing that in the docs. Mutable matrices can be mentioned as a side-note at the end with text that clearly discourages using them. In terms of the implementation the mutable matrices code should be completely separate from the everything else. There should be no need to worry about the possibility of having mutable matrices in any of the core algorithms etc. |
The alternative would be creating a new mutable matrix at the point of return every time. I think that's fine though as long as it's clear how to avoid using mutable matrices in the first place. |
So have the new mutable matrix implement wrappers for these new standalone functions which would cast the returned immutable to a mutable? |
Well it might not be so bad since many of these functions don't do so well on very large matrices anyway... |
The good thing about what you are suggesting is that it can be done incrementally, and then when the matrix classes reach a point where they are essentially wrappers they can be reorganized. |
The first thing to fix is we need a recommended way to create an immutable matrix and then we should recommend that in the docs: Unfortunately the name So we need something like |
You read my mind, I was thinking |
Add some deprecation warnings further down the line when instantiating |
I suppose
I'm not sure if we'll ever be able to deprecate this fully. It's been too prominent in the docs. Anyone who has written their code in the currently recommended way would have to change it. Look how much of SymPy's own codebase depends on matrices being mutable (#16790) and project that across the userbase. I think this will just be a long-term soft-deprecation like Lines 7 to 11 in ee2c266
|
Actually most of those hits are tests, the matrix module itself and |
If I understand the In fact you could even flip the hierarchy around and have |
@oscarbenjamin - Just sent up a test of what I understand you to mean - #18253 |
The properties in
MatrixProperties
class incommon.py
are not accessed in consistent manner right now.Though it may have been a very old issue.
And also,These may not have been consistent,though But hasThese may be leaved as-is like before.One of the problem it had introduced, it that some optional arguments like
simplify
would not work once it is set to property,unlike the code advertises like it should be
sympy/sympy/matrices/common.py
Lines 1155 to 1156 in cb8a524
because
a.is_hermitian
as a whole becomes a boolean object after called if set as property, and it would not be callable.I think there would not be a possible way to overload such thing in python nature, as something like
a.is_hermitian
to be used as property while also asa.is_hermitian()
ora.is_hermitian(simplify=False)
,so literally it would be no different than making the function hardcoded with simplify.
It would also mean that it will be tricky to make compatible with legacies, if we make any changes.
Because it will simply not be usable as functions once we add
@property
decorator with it.If it should be made changes with, I think there should be some compatibility layer added with some global variables, to make an option to choose whether to use Old_MatrixProperty class or New_MatrixProperty class,
so that it would be possible to throw some deprecation warning that you should change some global variable, and then it may be possible to use
a.is_symmetric
instead ofa.is_symmetric()
or such.The text was updated successfully, but these errors were encountered: