Opened 8 years ago

Last modified 14 months ago

#11699 needs_work enhancement

matrix() command should accept some Matlab-style inputs

Reported by: ddrake Owned by:
Priority: minor Milestone: sage-6.4
Component: linear algebra Keywords:
Cc: kini, kcrisman, mforets Merged in:
Authors: Dan Drake Reviewers:
Report Upstream: N/A Work issues:
Branch: u/ddrake/string-matrix-11699 (Commits) Commit: 5d006efd0c03be75475f75f23bb4071ed35ec44c
Dependencies: #24742 Stopgaps:

Description (last modified by jdemeyer)

In https://groups.google.com/d/msg/sage-devel/p-nrpKUBMm8/LUMAfoXsz-UJ Jason Grout mentioned that it would be convenient if Sage accepted the following Matlab syntax for creating matrices:

sage: matrix("1 2 3; 4 5 6; 7 8 9")
[1 2 3]
[4 5 6]
[7 8 9]

This should be an easy change to the matrix() command, and would be very convenient for users, even those with no Matlab background.

Attachments (1)

trac_11699_matlab_input_to_matrix.patch (10.5 KB) - added by ddrake 8 years ago.
apply to main repo

Download all attachments as: .zip

Change History (34)

comment:2 Changed 8 years ago by jason

In particular, elements can be separated by either commas or spaces (or both), and rows are separated by semicolons. So we should be able to easily separate by splitting the string:

rows = a.split(';')
for r in rows:
    elts = r.replace(',', ' ').split()
    # do something to interpret the elts, like map(sage_eval, elts)

comment:3 Changed 8 years ago by jason

Or probably even faster, do the ','->' ' translation once at the beginning to avoid lots of small strings being created:

# if a was the string passed in as the list of elements
a=a.replace(',', ' ')
rows = a.split(';')
elements=[map(sage_eval, r.split()) for r in rows]
# now continue matrix processing with "elements" as the list of elements

This would just be inserted in the first time the elements of a matrix were accessed and would convert the string to a list of elements like it expects.

comment:4 Changed 8 years ago by jason

In fact, we should support multiple lines here too, like Matlab, so you could do:

matrix("""
1 2 3
4 5 6
7 8 9
""")

To do that, do

# if a was the string passed in as the list of elements
a=a.replace(',', ' ').replace(';','\n')
rows = a.split('\n')
# throw away empty rows
rows = [r for r in rows if not r.trim()]
elements=[map(sage_eval, r.split()) for r in rows]
# now continue matrix processing with "elements" as the list of elements

At this point, it might be better to use regular expressions. It's certainly worth a timing test. Note the following matlab examples:

>> a=[1 2 3
4 5 6]

a =

     1     2     3
     4     5     6

>> a=[
1 2 3
4 5 6
]

a =

     1     2     3
     4     5     6

>> a=[  
1 2 3;
4 4 4
]

a =

     1     2     3
     4     4     4

>> a = [
1 2 3; ;
4 5 6]

a =

     1     2     3
     4     5     6

>> a = [;;1 2;;3 4] 

a =

     1     2
     3     4

>> a=[

;
1 2 3]

a =

     1     2     3



comment:5 Changed 8 years ago by jason

Oops, that should r.strip() rather than r.trim() above.

comment:6 Changed 8 years ago by jason

Slightly faster than the double replace, if s is defined at module creation time:

import string
s=string.maketrans(';,','\n ')

Then the double replace above becomes: a.translate(s)

comment:7 Changed 8 years ago by jason

Okay, here's a new iteration, especially since I flipped logic above with the r.strip()

# if a was the string passed in as the list of elements
a=a.replace(',', ' ').replace(';','\n') # or a.translate(s) if we can initialize s
rows = a.split('\n')
# throw away empty rows
rows = [r for r in rows if r.strip()]
elements=[map(sage_eval, r.split()) for r in rows]
# now continue matrix processing with "elements" as the list of elements

comment:8 Changed 8 years ago by ddrake

  • Authors set to Dan Drake
  • Description modified (diff)
  • Status changed from new to needs_review

Okay, try this. I think it still needs some optimization with the string handling stuff, and it could probably use some more doctests, but it's basically ready for review.

comment:9 Changed 8 years ago by ddrake

  • Owner changed from jason, was to (none)

Hrm, Robert Bradshaw just tossed some cold water on my simple string-handling: https://groups.google.com/d/msg/sage-devel/p-nrpKUBMm8/_4DL5li7NcQJ

The quick version: what about 1 + sin(x) (for splitting in spaces) and [1, 2; pi, f(3, 4)] for splitting on commas? Hrm.

comment:10 Changed 8 years ago by jason

quick comment: you should probably use isinstance(args[0], basestring), as that is forward-compatible with unicode strings and python3.

comment:11 Changed 8 years ago by ddrake

  • Status changed from needs_review to needs_work

Another problem: sage_eval evaluates its arguments in a different context than the one in which matrix() gets called. So even though you may have done x = var('x'), it doesn't know about x.

If we do put this in, I think the docstring should be explicit that using strings is limited, and only really works for number literals and similar things. Then later someone can write a full parser and figure out the evaluation context stuff.

comment:12 Changed 8 years ago by ddrake

  • Status changed from needs_work to needs_review

comment:13 follow-up: Changed 8 years ago by jason

Maybe on another ticket, but can I ask for one more enhancement on this? Can we make the following work:

matrix("""
[1 2]
[3 4]
""")

(i.e., any "]" or "[" next to row delimiters are stripped). That would mean that I could cut and paste output from Sage back into Sage to make a matrix.

comment:14 in reply to: ↑ 13 Changed 8 years ago by ddrake

Replying to jason:

Maybe on another ticket, but can I ask for one more enhancement on this? Can we make the following work:

matrix("""
[1 2]
[3 4]
""")

(i.e., any "]" or "[" next to row delimiters are stripped). That would mean that I could cut and paste output from Sage back into Sage to make a matrix.

Sure, no problem. Although you'll still have the split-on-whitespace and context limitations. But plain numerical matrices are common enough so that I think it's worth it.

Changed 8 years ago by ddrake

apply to main repo

comment:15 Changed 7 years ago by jason

See #12354 for another take on this as preparser syntax.

comment:16 Changed 7 years ago by kini

  • Cc kini added

comment:17 Changed 7 years ago by davidloeffler

  • Status changed from needs_review to needs_work
  • Work issues set to failing doctest

This patch causes a doctest to fail in sageinspect.py (see patchbot logs) -- looks like somebody helpfully wrote a doctest which relies on the docstring of "matrix()" being exactly 34 lines long. (There is also another failure on the most recent run, but I suspect that is just a random glitch.)

comment:18 Changed 6 years ago by jason

argh, I was trying to use this today, thinking it was already in...

comment:19 Changed 6 years ago by kcrisman

  • Cc kcrisman added

comment:20 Changed 6 years ago by jdemeyer

  • Milestone changed from sage-5.11 to sage-5.12

comment:21 Changed 5 years ago by vbraun_spam

  • Milestone changed from sage-6.1 to sage-6.2

comment:22 Changed 5 years ago by vbraun_spam

  • Milestone changed from sage-6.2 to sage-6.3

comment:23 Changed 5 years ago by vbraun_spam

  • Milestone changed from sage-6.3 to sage-6.4

comment:24 follow-up: Changed 4 years ago by ddrake

  • Branch set to u/ddrake/string-matrix-11699
  • Commit set to ddc2e812af55b6cc5abf3440423210b4bef0977d
  • Work issues changed from failing doctest to failing doc build

I've ported this over to 6.9.beta3. It builds, works, and passes doctests, but the documentation doesn't build -- I have doctests with \n in them, and these are not interpreted by Sphinx correctly, and then it complains about indentation being messed up. Advice for how to fix that welcome.


New commits:

ddc2e81add Matlab-style strings for matrix() constructor

comment:25 in reply to: ↑ 24 ; follow-up: Changed 4 years ago by jdemeyer

Replying to ddrake:

Advice for how to fix that welcome.

Use

r"""
docstring
"""

instead of

"""
docstring
"""

comment:26 Changed 4 years ago by git

  • Commit changed from ddc2e812af55b6cc5abf3440423210b4bef0977d to 5d006efd0c03be75475f75f23bb4071ed35ec44c

Branch pushed to git repo; I updated commit sha1. New commits:

5d006efraw string for docstrings so Sphinx handles \n -- thanks Jeroen Demeyer

comment:27 in reply to: ↑ 25 Changed 4 years ago by ddrake

Replying to jdemeyer:

Use

r"""
docstring
"""

instead of

"""
docstring
"""

That's it! Thank you.

comment:28 Changed 4 years ago by ddrake

  • Status changed from needs_work to needs_review
  • Work issues failing doc build deleted

comment:29 Changed 3 years ago by vdelecroix

Failing doctest

sage -t --long src/sage/misc/sageinspect.py
**********************************************************************
File "src/sage/misc/sageinspect.py", line 1959, in sage.misc.sageinspect.sage_getsourcelines
Failed example:
    sage_getsourcelines(matrix)[1]
Expected:
    732
Got:
    867
**********************************************************************
1 item had failures:
   1 of  28 in sage.misc.sageinspect.sage_getsourcelines
    [284 tests, 1 failure, 28.10 s]
----------------------------------------------------------------------
sage -t --long src/sage/misc/sageinspect.py  # 1 doctest failed
----------------------------------------------------------------------

comment:30 Changed 3 years ago by vdelecroix

  • Status changed from needs_review to needs_work

comment:31 Changed 2 years ago by mforets

  • Cc mforets added

comment:32 Changed 14 months ago by jdemeyer

  • Dependencies set to #24742

I don't plan to work on this, but if anybody does: it should be on top of #24742.

comment:33 Changed 14 months ago by jdemeyer

  • Description modified (diff)
Note: See TracTickets for help on using tickets.