interactions between mutable and immutable matrices

111 views
Skip to first unread message

krastano...@gmail.com

unread,
Aug 6, 2012, 5:39:54 PM8/6/12
to sy...@googlegroups.com
Hi all,

Recently I got to work some more with the matrices module (rref,
jordan form for the ODE solver and various coordinate related
transformations for the diffgeom module).

The current approach to the issue of Matrix not being a Basic subclass
is to use another class called ImmutableMatrix. There is one big
drawback in this approach among all its advantages: Interaction
between matrices frequently requires rebuilding of the matrix.

For instance, imagine a ref method that is used inside the rref method
that is used inside the particular_solution method. If they are called
on a mutable matrix they must create a copy of this matrix and work on
it and then return it. So far so good, however if they are called on
an immutable matrix at the end of each method there is an addition
rebuild operation, this time transforming the intermediate mutable
matrix into an immutable one.

Do you think that this is appropriate and worth the cost? Now that we
have used both Mutable and ImmutableMatrix for some time, do you think
that merging them is possible? Obviously, this again brings up the
issue of mutable Basic objects, however this should not be such a big
problem if the hashes are calculated properly and if it is well
documented.

Aaron Meurer

unread,
Aug 6, 2012, 6:25:27 PM8/6/12
to sy...@googlegroups.com
I don't think it's possible to make a mutable object hashable.
There's no way to make things like dict and set recalculate the hash
of an object if it changes.

Perhaps a better solution would be to make ImmutableMatrix the default
(i.e., Matrix would be a shortcut to ImmutableMatrix instead of
MutableMatrix). Then we could make MutableMatrix truly mutable, i.e.,
most operations would modify the matrix in place instead of creating a
new one.

Of course, this would also mean removing the (to me nonsensical)
unevaluatedness from ImmutableMatrix, and put that into a separate
class if it is really needed.

Regardless of all that, it should be possible to make converting from
one type to another a very cheap operation.

Aaron Meurer

Aaron Meurer

unread,
Aug 6, 2012, 6:46:08 PM8/6/12
to sy...@googlegroups.com
I created a page on the wiki based on an older post of mine to this
mailing list about this:
https://github.com/sympy/sympy/wiki/What-happens-when-you-mess-with-hashing.
You can see from the first example one of the bad things that can
come from changing an object's hash. And in case you were thinking of
changing the object but not the hash, look at the second example.

Aaron Meurer

Matthew Rocklin

unread,
Aug 6, 2012, 7:07:12 PM8/6/12
to sy...@googlegroups.com
Immutability has many virtues. It is nice to create an object, pass it around to many functions, and know that it is still the same. Immutability is very safe. 

Mutability has many virtues. It is fast. People are used to it. 

I think it is important to support both types. I think that people who like immutability are ok with it being a bit slower. Right now the Immutable->mutable->computation->mutable->Immutable process works well I think. It would be better if, as Aaron suggests, we ensure that the transition is very cheap. 

There are many interesting discussions to be had however about defaults and standards. 

Despite being a fan of immutability in symbolic systems I think that we should stick to having MutableMatrix the default. I think that many users will want to do things like 

M = eye(3)
M[2, 2] = 10

If we stop them from doing this I think that they will be confused. 

The unevaluated by default discussion is separate. The next chance I get a free 20 minutes I'll put up a pull request to change the behavior. I do encourage discussion on the topic. In one of the issues someone did want an unevaluated immutable matrix expression. This will still be possible but you'll have to use explicit MatAdds/MatMuls. 

To be clear on the original point. Even if we had a MutableBasic I still think we should have an ImmutableMatrix. I do not think we should merge Mutable and Immutable.

Matthew Rocklin

unread,
Aug 6, 2012, 7:21:33 PM8/6/12
to sy...@googlegroups.com
<hijack>
Evaluation by default of ImmutableMatrix is implemented here

This is orthogonal to the discussion brought up in this e-mail thread. I recommend that further discussion on the evaluation-by-default happens on the above PR. 
</hijack>

Aaron Meurer

unread,
Aug 6, 2012, 10:05:27 PM8/6/12
to sy...@googlegroups.com
On Mon, Aug 6, 2012 at 5:07 PM, Matthew Rocklin <mroc...@gmail.com> wrote:
> Immutability has many virtues. It is nice to create an object, pass it
> around to many functions, and know that it is still the same. Immutability
> is very safe.
>
> Mutability has many virtues. It is fast. People are used to it.

I would argue the exact opposite. Sure, you may be surprised when an
attempt to mutate breaks, but you'll be even more surprised when an
operation that you wouldn't expect to work in place does. The point
is that mutability represents a change in state, which requires a mich
higher cognitive burdon to understand. Some programming languages (in
particular, functional languages) find this concept to be so bad that
they disallow mutability altogether.

>
> I think it is important to support both types. I think that people who like
> immutability are ok with it being a bit slower. Right now the
> Immutable->mutable->computation->mutable->Immutable process works well I
> think. It would be better if, as Aaron suggests, we ensure that the
> transition is very cheap.
>
> There are many interesting discussions to be had however about defaults and
> standards.
>
> Despite being a fan of immutability in symbolic systems I think that we
> should stick to having MutableMatrix the default. I think that many users
> will want to do things like
>
> M = eye(3)
> M[2, 2] = 10

But if they want to do that, then we can just have a __setitem__ that
raises TypeError("ImmutableMatrix does not support item assignment.
Use MutableMatrix instead."), and they user will see immediately that
they just need to do M = MutableMatrix(M), and then they can continue
to do what they want to do. And note that things like

A += B

will work either way (the difference is that the mutable case will be
more memory efficient).

On the other hand, if we make ImmutableMatrix the default, then we can
make MutableMatrix's methods work in place without worrying about them
being too unexpected (because it's assumed that if the user uses
MutableMatrix, that he wants in place operations), and it actually
would be more consistant. For example, MatrixMatrix.row_del works in
place, but MutableMatrix.row_insert does not. Also, many algorithms
can be more efficient if done mutably.

>
> If we stop them from doing this I think that they will be confused.
>
> The unevaluated by default discussion is separate. The next chance I get a
> free 20 minutes I'll put up a pull request to change the behavior. I do
> encourage discussion on the topic. In one of the issues someone did want an
> unevaluated immutable matrix expression. This will still be possible but
> you'll have to use explicit MatAdds/MatMuls.

Right. My only point there was that we can't replace Matrix with
ImmutableMatrix without changing this.

>
> To be clear on the original point. Even if we had a MutableBasic I still
> think we should have an ImmutableMatrix. I do not think we should merge
> Mutable and Immutable.

I agree.

Aaron Meurer
Reply all
Reply to author
Forward
0 new messages