Skip to content

Instantly share code, notes, and snippets.

@seanrostami
Last active July 1, 2018 03:20
Show Gist options
  • Save seanrostami/3945da948b3b59a7d9792cca7ca06f66 to your computer and use it in GitHub Desktop.
Save seanrostami/3945da948b3b59a7d9792cca7ca06f66 to your computer and use it in GitHub Desktop.
Converts a matrix from one of MATLAB's various slightly different formats (your own input to MATLAB, or saved to .m file, or copied/pasted from MATLAB's stdout) to AMS's LaTeX format. For details (e.g. usage), read the file's docstring.
"""This function, MATrix2LaTeX, converts any matrix in MATLAB's format(s) to AMS's LaTeX-format.
One example of the former is "
[ 0.53730, 0.33377, 0.53773; 0.34990, 0.38121, 0.16256 ]
".
Another example of the former is "
A = 0.53730 0.33377 0.53773
0.34990 0.38121 0.16256
".
An example of the latter is "
\\begin{bmatrix}
0.53730 & 0.33377 & 0.53773 \\\\
0.34990 & 0.38121 & 0.16256
\\end{bmatrix}
".
Exponential notation, like 4.01574771256261e-01, is converted sensibly.
USAGE: Tell MATLAB to save the matrix as a .m text file (not a .mat binary file!) and execute this script in Python with the .m as argument.
EXAMPLE: python .../MATrix2LaTeX.py .../somematrix.m
(it is acceptable to use other extensions, like .txt, or no extension)
The LaTeX version will be contained in a .tex file with the same name and in the same location as the input.
The function MATrix2LaTeX is tolerant of many small errors, like strange whitespace patterns."""
import os
import string
def MATrix2LaTeX( infname, outfname, capsize = 180000 ):
"""The MATLAB-format matrix is assumed to be text contained at the location provided as the first argument.
Output is written as text to the location provided as the second argument.
The third parameter is a limit on the size of the input file that will be accepted, and defaults to 180 KB. This number is roughly the size of the text file needed to store a 110x85 matrix in long exponential format, which is what you would have if you were somehow able to fit 10 rows per inch and 10 columns per inch on a standard 8.5" x 11" paper, likely a drastic overestimate. Certainly, you shouldn't be trying to put anything larger into a LaTeX file...
The script is more complicated than it "should" be, because I wanted it to be very robust. If I made strict assumptions about the input then various things could be simplified."""
if os.path.getsize(infname) > capsize: # bytes
return # input is too large!
f = open( infname, "rt" )
s = f.read()
f.close()
s = s.replace( ",", " " ) # commas separate entries in a row but are optional in MATLAB -- uniformize by deleting commas (insert a space to avoid merging tightly packed entries like "x,y")
s = s.replace( "...\n", " " ) # MATLAB inserts, even when saving to a file, these idiotic "..." into rows when it considers the row to be too big
# note: .read's documentation says all newline variations converted to "\n" automatically, so previous line works as intended on data coming from another system (similar for .write, relevant below)
R = 0
L = s.find( "[" ) # find from left
if L < 0:
L = s.find( "=" ) # find from left
L = ( 0 if L < 0 else L+1 )
R = len( s )
else:
L += 1
R = s.rfind( "]" )
assert R > 0, "format of input seems to be invalid!" # should never trigger
s = s[L:R].lstrip().rstrip() # extract the "core"
entries = ""
if ";" in s: # indicates one of two possible MATLAB formats
for w in string.whitespace: # first, uniformize the whitespace
s = s.replace( w, " " )
# use a regexp to delete anything unexpected?
while " "*2 in s: # compress consecutive spaces to a single space
s = s.replace( " "*2, " " )
if "e" in s: # if exponential notation, get strings representing the entries, but don't convert format yet
entries = s.replace( ";", " " ).split() # default delimiters are whitespace
s = s.replace( "; ", ";" ).replace( " ;", ";" ) # after compressing spaces, rows could be separated by " ; " or "; " or " ;" or ";"
s = s.replace( ";", "\\\\" )
else:
while " "*2 in s: # replace substrings of spaces by a single space
s = s.replace( " "*2, " " )
if "e" in s: # if exponential notation, get strings representing the entries, but don't convert format yet
entries = s.replace( "\n", " " ).split()
s = s.replace( "\n ", "\n" ).replace( " \n", "\n" )
s = s.replace( "\n", "\\\\" )
s = s.replace( " ", " & " ) # at this point, the only spaces left should be exactly one between each pair of entries
s = s.replace( "\\\\", " \\\\\n" ) # to make the LaTeX more readable (can only do this after inserting "&"!)
for x in entries: # now that the matrix is formatted, convert exponential notation
[ m, e ] = x.split( "e" ) # split into mantissa and exponent
e = str( int( e ) ) # converts '+01' to '1' etc. (worthwhile to do something more efficient?)
s = s.replace( x, m + " \\times 10^{" + e + "}" )
f = open( outfname, "wt" )
f.write( "\\begin{bmatrix}\n" + s + "\n\\end{bmatrix}" )
f.close()
if __name__ == "__main__":
import sys
R = sys.argv[1].rfind( os.extsep )
if R < 0: # no extension, type must be implicit
R = len( sys.argv[1] )
MATrix2LaTeX( sys.argv[1], (sys.argv[1])[:R] + os.extsep + "tex" ) # replace extension by (or append, if none) .tex, but otherwise keep the same location and filename
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment