What should Matrix([1,2,3]) give?

20 views
Skip to first unread message

smichr

unread,
Aug 29, 2011, 3:26:58 AM8/29/11
to sympy
I posted something to an issue which doesn't automatically ping to
everyone so am copying the discussion here.

Currently, there is an inconsistency between sympy and numpy since
Matrix([1,2,3]) gives a column vector while the same argument in numpy
gives a row vector.

I believe that sympy did the right thing in interpreting this as a
column vector since the current syntax
is that the rows of the Matrix should be passed in a list. So the
elements of [1,2,3] should be interpreted
as rows and produce a column vector and Matrix([[1,2,3]]) should
produce a row vector. But numpy will
not consider changing their syntax: a request to that effect was
almost instantly tabled.

The issue about what to do about the inconsistency was discussed in
http://code.google.com/p/sympy/issues/detail?id=884 and got
sidetracked without any final decision being made about what to do. I
think that sympy should correct this issue and make a slight change,
too: drop the requirement that the rows be past with the surrounding
brackets.

Requiring surrounding brackets is just the opposite of other sympy
objects like Set(1,2,3) and Tuple(1,2,3) where the args are not put
inside of an iterable. This was done, perhaps, to be consistent with
the idea that `foo.func(*foo.args)` rebuild `foo` and it makes sense
for Set's args to be thought of as being 1,2,3 not a single list
[1,2,3]. In the same way, then, Matrix's args are rows of a matrix, so
they shouldn't be put inside of an iterable, they should just be
passed as Matrix(r1) or Matrix(r1, r2) (where ri is a list of values
in the row) not as Matrix([r1, r2]).

If the surrounding brackets are dropped then the question arises, "Can
Matrix(1,2,3)" be used as a shortcut for a column vector instead of
`Matrix([1],[2],[3])`. The best argument against this is by Pearu
Peterson:

1) usage of Matrix(1,2,3,4) is unconventional
2) there is ambiquity with Matrix(3,4): with your definition
it is a column vector of length 2, but in sage and Maple it would
be a 3x4 matrix of zeros.

That would mean that the only way to create a column vector is to
transpose the row vector (or have a subclass of Matrix named vector
which instantiates as `Vector(1,2,3)` and returns
`Matrix([1,2,3]).T`.

But in counter to (1), such notation is consistent with how sympy
wraps things up. And the Matrix method wraps up rows so
Matrix(1,2,3,4) corresponds to 4 rows. And counter to (2) is that
sympy is not sage; we have a zeros and lambda construction:

>>> Matrix(3,4,lambda x,y:0)
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
>>> zeros(4)[:3,:]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]

Questions
Should we make Matrix like Set and Tuple and not require the outermost
brackets?
if no, then we just say sympy got it right and numpy got it
wrong:
Matrix([1,2,3]) should be a column vector since the rows are
listed inside the outer brackets.
also, if no, why aren't bounding brackets necessary for other
container types like Set, Tuple, and Dict (when added)?
if yes, should we allow Matrix(1,2,3) to be short for Matrix([1],
[2],[3])?
if no, should we have a Vector class which is just a subclass
of Matrix that returns a column vector for its input?
also, if no, why does SparseMatrix allow this syntax:
SparseMatrix(1,2,3) gives a column vector.

Currently there's no nice way to be compatible with numpy unless we
disallow the Matrix(1,2,3) syntax, drop the outer bracket requirement,
change the current calls like Matrix([1,2,3]) to Matrix([1,2,3]).T and
then say that numpy matrix args are sympy's Matrix args with an extra
set of brackets:

Matrix([1,2,3]).T = numpy.matrix([[1,2,3]]).T
Matrix([1,2,3]) = numpy.matrix([[1,2,3]])
Matrix([1,2,3],[4,5,6]) = numpy.matrix([[1,2,3],[4,5,6]])

Robert Kern

unread,
Aug 29, 2011, 1:17:33 PM8/29/11
to sy...@googlegroups.com
On Mon, Aug 29, 2011 at 02:26, smichr <smi...@gmail.com> wrote:
> I posted something to an issue which doesn't automatically ping to
> everyone so am copying the discussion here.
>
> Currently, there is an inconsistency between sympy and numpy since
> Matrix([1,2,3]) gives a column vector while the same argument in numpy
> gives a row vector.
>
>    I believe that sympy did the right thing in interpreting this as a
> column vector since the current syntax
>    is that the rows of the Matrix should be passed in a list. So the
> elements of [1,2,3] should be interpreted
>    as rows and produce a column vector and Matrix([[1,2,3]]) should
> produce a row vector.  But numpy will
>    not consider changing their syntax: a request to that effect was
> almost instantly tabled.

Sorry if the speed of that seemed rude. I just happened to be checking
my email when that ticket came in. :-)

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless
enigma that is made terrible by our own mad attempt to interpret it as
though it had an underlying truth."
  -- Umberto Eco

Aaron Meurer

unread,
Aug 29, 2011, 1:25:56 PM8/29/11
to sy...@googlegroups.com

I'm not subscribed to the numpy list, so I didn't see that message,
but would you recommend that SymPy use the same syntax as numpy? Can
you perhaps give some of the reasoning behind numpy's syntax?

Aaron Meurer

Sherjil Ozair

unread,
Aug 29, 2011, 1:27:49 PM8/29/11
to sy...@googlegroups.com
I think for consistency's sake, we should make this return a row matrix.
Matrix([1,2,3]).T would be the corresponding column matrix.


--
You received this message because you are subscribed to the Google Groups "sympy" group.
To post to this group, send email to sy...@googlegroups.com.
To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sympy?hl=en.


Robert Kern

unread,
Aug 29, 2011, 2:00:21 PM8/29/11
to sy...@googlegroups.com

Well, numpy.matrix has to interact with numpy.ndarrays and thus has to
deal with issues that sympy.Matrix may not. numpy.array([1,2,3]) is
treated as a row-vector in most contexts when it is mixed with rank-2
arrays due to numpy's broadcasting semantics. numpy.matrix() is not
just used as a constructor from list literals, it also converts
ndarray objects to matrix objects when such conversions need to be
done implicitly.

[~]
|1> va = np.array([1,2,3])

[~]
|2> ma = np.arange(9).reshape([3,3])

[~]
|3> va
array([1, 2, 3])

[~]
|4> ma
array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])

[~]
|5> va + ma
array([[ 1, 3, 5],
[ 4, 6, 8],
[ 7, 9, 11]])

[~]
|6> vm = np.matrix([1,2,3])

[~]
|7> vm
matrix([[1, 2, 3]])

[~]
|8> np.matrix(va)
matrix([[1, 2, 3]])

[~]
|9> mm = np.matrix(va)

[~]
|10> mm
matrix([[1, 2, 3]])

[~]
|11> mm = np.matrix(ma)

[~]
|12> mm
matrix([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])

[~]
|14> va + mm
matrix([[ 1, 3, 5],
[ 4, 6, 8],
[ 7, 9, 11]])

[~]
|15> vm + mm
matrix([[ 1, 3, 5],
[ 4, 6, 8],
[ 7, 9, 11]])

Even if we did agree that numpy.matrix() *ought* to behave differently
if we were designing it again, the code has been out in the wild for
many, many years. Changing it now would break a lot of code that
depends on the current semantics. We simply can't change it now.
That's why I closed the ticket without input from the rest of the
devs. It's just a non-starter.

I can't make a recommendation for SymPy one way or the other. The
numpy.matrix() semantics are the way they are for reasons internal to
numpy. What you need to consider is how important it is to you to
match numpy's semantics. I don't really know if there are use cases
are for going back and forth between the two types or if the only
concern is having similar semantics for pedagogical reasons.
Maintaining internal consistency inside SymPy may be more important.
Either way is a reasonable choice.

Chris Smith

unread,
Aug 29, 2011, 2:11:50 PM8/29/11
to sy...@googlegroups.com
>
> Sorry if the speed of that seemed rude. I just happened to be checking
> my email when that ticket came in. :-)

Thanks for letting me know why a reply was in my box almost the second
after I pressed the return key :-) I did search the tickets and didn't
find a similar issue but I figured you must have been through this
already. Your need to maintain legacy compatibility is understandable.

/c

Chris Smith

unread,
Aug 29, 2011, 2:14:05 PM8/29/11
to sy...@googlegroups.com
On Mon, Aug 29, 2011 at 11:12 PM, Sherjil Ozair <sherji...@gmail.com> wrote:
> I think for consistency's sake, we should make this return a row matrix.
> Matrix([1,2,3]).T would be the corresponding column matrix.

I've got a proposal nearly ready:
--we let Matrix([1,2,3]) give a row vector so it's compatible with numpy;
--we drop the requirement (but still allow0 the bounding brackets (so
it's still compatible with numpy but also behaves like sympy Set and
Tuple);
--we introduce the Vector(1,2,3) or Vector(*[1,2,3]) as the
alternative to Matrix([1,2,3]).T which a) gives an easy fix to
existing code where Matrix([1,2,3]) might be hard to find. (If it's
split over many lines, the head should still be easy to find and can
be changed from Matrix([ to Vector(*[ and b) gives a meaningful and
transparent alias for Matrix([1,2,3]).T

What do you think? https://github.com/sympy/sympy/pull/581

Also, there are some interface tidying changes and docstrings added to
dot and projection. I would really appreciate it if someone woring
often with matrices would check that I've done the right things there.

/c

Aaron Meurer

unread,
Aug 29, 2011, 2:33:31 PM8/29/11
to sy...@googlegroups.com
I see. So the main reason for the numpy syntax is consistency with
broadcasting, which doesn't really make sense in SymPy (or at least
not at the moment). Actually, I originally thought we should try to
match the numpy syntaxes, but as I look at them, I'm not sure. For
example, I said we should try to match the slicing syntax, but there
are some aspects of the way numpy does that that some people don't
like (see issue http://code.google.com/p/sympy/issues/detail?id=2544).

So I think we need to consider things from the SymPy point of view,
like you said. One thing to consider is that eventually we will have
an ImmutableMatrix(), which is Basic, but which will have the same
syntax as Matrix(). So it will need to satisfy the
ImmutableMatrix(*args) invariant.

We also should consider backwards compatibility breaks of our own.
Changing Matrix([1, 2, 3]) to a row vector will break a lot of code.

Aaron Meurer

Chris Smith

unread,
Aug 29, 2011, 2:41:50 PM8/29/11
to sy...@googlegroups.com
>
> We also should consider backwards compatibility breaks of our own.
> Changing Matrix([1, 2, 3]) to a row vector will break a lot of code.
>
It broke at 0.7.0 in not allowing Matrix(1,2,3) anymore. I think I
have a pretty easy fix to ease the pain of backward compatibility as
described in the pull request.

Vinzent Steinberg

unread,
Aug 31, 2011, 7:34:49 AM8/31/11
to sympy
On Aug 29, 8:33 pm, Aaron Meurer <asmeu...@gmail.com> wrote:
> I see.  So the main reason for the numpy syntax is consistency with
> broadcasting, which doesn't really make sense in SymPy (or at least
> not at the moment).  Actually, I originally thought we should try to
> match the numpy syntaxes, but as I look at them, I'm not sure.  For
> example, I said we should try to match the slicing syntax, but there
> are some aspects of the way numpy does that that some people don't
> like (see issuehttp://code.google.com/p/sympy/issues/detail?id=2544).
>
> So I think we need to consider things from the SymPy point of view,
> like you said.  One thing to consider is that eventually we will have
> an ImmutableMatrix(), which is Basic, but which will have the same
> syntax as Matrix().  So it will need to satisfy the
> ImmutableMatrix(*args) invariant.
>
> We also should consider backwards compatibility breaks of our own.
> Changing Matrix([1, 2, 3]) to a row vector will break a lot of code.

I think we can break numpy compatibility here. (IIRC they are not that
happy with their matrix interface anyway.)

For me Matrix([1,2,3]) returning a column vector is just convenient,
because it is closer to the mathematical conventions and most often
used. If you want a row vector, you can use the explicit
Matrix([[1,2,3]]) (or transposition of course).

Vinzent

Chris Smith

unread,
Aug 31, 2011, 9:13:33 AM8/31/11
to sy...@googlegroups.com
> I think we can break numpy compatibility here. (IIRC they are not that
> happy with their matrix interface anyway.)
>
> For me Matrix([1,2,3]) returning a column vector is just convenient,
> because it is closer to the mathematical conventions and most often
> used. If you want a row vector, you can use the explicit
> Matrix([[1,2,3]]) (or transposition of course).

So do you think no change should take place or do you think having
Matrix and others (Set, Tuple, Dict) behave the same way -- not wrap
their args in a container -- is important?

/c

Aaron Meurer

unread,
Aug 31, 2011, 11:36:24 AM8/31/11
to sy...@googlegroups.com
I agree with Vinzent. Matrix([1, 2, 3]) is a special case against the
rest of the syntax, because the main syntax is two levels of nested
lists, so we can make it return whatever we want. The same for
Matrix(1, 2, 3). So I think both should return column vectors, which
are the most used.

The main reason it's important for these classes is that if you make
Tuple([1]) == Tuple(1), then you can't create a nested Tuple, i.e.,
Tuple(Tuple(1)) would just be reduced to Tuple(1).

So the question is, if you would ever want to create a nested Matrix?
I think you might, with some kind of block matrix, though I'm not so
sure that it's as important that it doesn't automatically denest in
this case. What do others think?

Aaron Meurer

>
> /c

Ronan Lamy

unread,
Aug 31, 2011, 12:43:08 PM8/31/11
to sy...@googlegroups.com
Le mercredi 31 août 2011 à 09:36 -0600, Aaron Meurer a écrit :
> I agree with Vinzent. Matrix([1, 2, 3]) is a special case against the
> rest of the syntax, because the main syntax is two levels of nested
> lists, so we can make it return whatever we want. The same for
> Matrix(1, 2, 3). So I think both should return column vectors, which
> are the most used.

I don't think we should add more special cases. The Matrix constructor
is already excessively complex. Besides, handling iterables and
non-iterables in different ways is fraught with problems: what'll happen
when we want a Matrix whose elements are iterable (for instance some
kind of polynomial object)??

Aaron Meurer

unread,
Aug 31, 2011, 3:06:34 PM8/31/11
to sy...@googlegroups.com
On Wed, Aug 31, 2011 at 10:43 AM, Ronan Lamy <ronan...@gmail.com> wrote:
> Le mercredi 31 août 2011 à 09:36 -0600, Aaron Meurer a écrit :
>> I agree with Vinzent.  Matrix([1, 2, 3]) is a special case against the
>> rest of the syntax, because the main syntax is two levels of nested
>> lists, so we can make it return whatever we want.  The same for
>> Matrix(1, 2, 3).  So I think both should return column vectors, which
>> are the most used.
>
> I don't think we should add more special cases. The Matrix constructor
> is already excessively complex. Besides, handling iterables and
> non-iterables in different ways is fraught with problems: what'll happen
> when we want a Matrix whose elements are iterable (for instance some
> kind of polynomial object)??

That's why I was wondering about nested matrices. Also, for Matrix(1,
2, 3), allowing this might limit future syntaxes. So contrary to what
I just said, I'm not so sure if allowing that is a good idea or not at
this point. We definitely should keep Matrix([1, 2, 3]) as it is,
though.

Aaron Meurer

smichr

unread,
Sep 1, 2011, 8:16:38 AM9/1/11
to sympy
OK, it looks like the instantiation is not going to change. Could
someone interested in Matrix take a look at the other changes:

All indices (except absolute indices) behave like indices to python
lists. So negative indices are now supported:
M[1,-1] gives the last element of row 1
col_insert(-1, c) inserts c before last column
col_insert(M.cols, c) will append c (since M.cols is greater than the
last column number)
col_insert(oo, c) will append c (since oo is greater than the last
column number)

The dot product is a little more flexible, returning a list if given
anything other than a single row or col

project and dot docstrings are expanded

coverage has been increased

arguments to zeros and ones can be given as foo(i, j) and not only
foo((i,j)) and foo(i)

Aaron Meurer

unread,
Sep 1, 2011, 12:36:55 PM9/1/11
to sy...@googlegroups.com
I didn't read any code, but here's my thoughts on what you said here

On Thu, Sep 1, 2011 at 6:16 AM, smichr <smi...@gmail.com> wrote:
> OK, it looks like the instantiation is not going to change.  Could
> someone interested in Matrix take a look at the other changes:
>
> All indices (except absolute indices) behave like indices to python
> lists. So negative indices are now supported:
> M[1,-1] gives the last element of row 1
> col_insert(-1, c) inserts c before last column
> col_insert(M.cols, c) will append c (since M.cols is greater than the
> last column number)
> col_insert(oo, c) will append c (since oo is greater than the last
> column number)

I am +1 to all of these (though I haven't looked at any code).

>
> The dot product is a little more flexible, returning a list if given
> anything other than a single row or col

I don't know if that's a great idea. You can just do matrix
multiplication if you want multiple dot products (and it may even be
faster).

>
> project and dot docstrings are expanded
>
> coverage has been increased
>
> arguments to zeros and ones can be given as foo(i, j) and not only
> foo((i,j)) and foo(i)

We ought to deprecate foo((i, j)).

Aaron Meurer

Chris Smith

unread,
Sep 1, 2011, 10:39:07 PM9/1/11
to sy...@googlegroups.com
>> The dot product is a little more flexible, returning a list if given
>> anything other than a single row or col
>
> I don't know if that's a great idea.  You can just do matrix
> multiplication if you want multiple dot products (and it may even be
> faster).

It always does matrix multiplication now. It just puts the results in
a list (if more than one dot was done) or returns the single scalar
(which is the current expectation).

Vinzent Steinberg

unread,
Sep 2, 2011, 9:20:32 AM9/2/11
to sympy
On 31 Aug., 21:06, Aaron Meurer <asmeu...@gmail.com> wrote:
> That's why I was wondering about nested matrices.  Also, for Matrix(1,
> 2, 3), allowing this might limit future syntaxes.  So contrary to what
> I just said, I'm not so sure if allowing that is a good idea or not at
> this point.  We definitely should keep Matrix([1, 2, 3]) as it is,
> though.

I agree. We should not add Matrix(1,2,3), and keep Matrix([1,2,3]) as
it is in my opinion.

Vinzent
Reply all
Reply to author
Forward
0 new messages