Skip to content
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

[FEATURE] Supporting Matrix Notation #48

Open
TheCedarPrince opened this issue Jan 3, 2022 · 8 comments
Open

[FEATURE] Supporting Matrix Notation #48

TheCedarPrince opened this issue Jan 3, 2022 · 8 comments

Comments

@TheCedarPrince
Copy link

Hi @Kolaru ,

I was tinkering with MathTeXEngine and was trying to render a matrix.
Using LaTeXStrings.jl, I wrote the following snippet:

latex_string = L"""
$\begin{matrix}
1 & 2 & 3\\
a & b & c
\end{matrix}$
"""

And when I tried rendering it like so:

generate_tex_elements(latex_string)

I got the following error:

ERROR: TeXParseError: unexpected error while parsing
at position 7 (string index 7)
\begin{matrix}
1 & 2 & 3\\
a & b & c
\end{matrix}
      ^
Stack before
[1] TeXExpr :expr


Stacktrace:
  [1] texparse(data::String; showdebug::Bool)
    @ MathTeXEngine ~/.julia/packages/MathTeXEngine/zH12o/src/parser/parser.jl:285
  [2] texparse
    @ ~/.julia/packages/MathTeXEngine/zH12o/src/parser/parser.jl:271 [inlined]
  [3] _broadcast_getindex_evalf
    @ ./broadcast.jl:670 [inlined]
  [4] _broadcast_getindex
    @ ./broadcast.jl:643 [inlined]
  [5] _getindex
    @ ./broadcast.jl:666 [inlined]
  [6] _broadcast_getindex
    @ ./broadcast.jl:642 [inlined]
  [7] getindex
    @ ./broadcast.jl:597 [inlined]
  [8] copy
    @ ./broadcast.jl:899 [inlined]
  [9] materialize
    @ ./broadcast.jl:860 [inlined]
 [10] generate_tex_elements(str::LaTeXString, font_family::MathTeXEngine.FontFamily)
    @ MathTeXEngine ~/.julia/packages/MathTeXEngine/zH12o/src/engine/layout.jl:295
 [11] generate_tex_elements(str::LaTeXString)
    @ MathTeXEngine ~/.julia/packages/MathTeXEngine/zH12o/src/engine/layout.jl:289
 [12] top-level scope
    @ REPL[12]:1

caused by: TeXParseError: unsupported command \begin
at position 7 (string index 7)
\begin{matrix}
1 & 2 & 3\\
a & b & c
\end{matrix}
      ^
Stack before
[1] TeXExpr :expr


Stacktrace:
  [1] _end_command_builder!(stack::DataStructures.Stack{Any}, p::Int64, data::String)
    @ MathTeXEngine ~/.julia/packages/MathTeXEngine/zH12o/src/parser/parser.jl:233
  [2] macro expansion
    @ ~/.julia/packages/MathTeXEngine/zH12o/src/parser/parser.jl:256 [inlined]
  [3] macro expansion
    @ ~/.julia/packages/Automa/1KOLQ/src/codegen.jl:149 [inlined]
  [4] texparse(data::String; showdebug::Bool)
    @ MathTeXEngine ~/.julia/packages/MathTeXEngine/zH12o/src/parser/parser.jl:283
  [5] texparse
    @ ~/.julia/packages/MathTeXEngine/zH12o/src/parser/parser.jl:271 [inlined]
  [6] _broadcast_getindex_evalf
    @ ./broadcast.jl:670 [inlined]
  [7] _broadcast_getindex
    @ ./broadcast.jl:643 [inlined]
  [8] _getindex
    @ ./broadcast.jl:666 [inlined]
  [9] _broadcast_getindex
    @ ./broadcast.jl:642 [inlined]
 [10] getindex
    @ ./broadcast.jl:597 [inlined]
 [11] copy
    @ ./broadcast.jl:899 [inlined]
 [12] materialize
    @ ./broadcast.jl:860 [inlined]
 [13] generate_tex_elements(str::LaTeXString, font_family::MathTeXEngine.FontFamily)
    @ MathTeXEngine ~/.julia/packages/MathTeXEngine/zH12o/src/engine/layout.jl:295
 [14] generate_tex_elements(str::LaTeXString)
    @ MathTeXEngine ~/.julia/packages/MathTeXEngine/zH12o/src/engine/layout.jl:289
 [15] top-level scope
    @ REPL[12]:1  

I saw that this issue was somewhat similar to supporting \begin{align} statements in #25 .
So, out of curiosity, how difficult would it be to create support for rendering matrices in MathTeXEngine?
Is this something that is out of the purview of the engine or would it be doable?

@TheCedarPrince
Copy link
Author

@davibarreira following up from here: JuliaAnimators/Javis.jl#448

How difficult do you think this would be to implement as well?
I don't really know where to look in the engine right now to get started, but I am assuming it may be something similar to how fractions are written using the engine?
Not too sure at this point.
Have any thoughts?

@davibarreira
Copy link

I don't think is that hard, because a matrix could be pretty much just a bunch of text spaced evenly and some big brackets in the side. A pseudo-code I think for it would be something like:

\begin{matrix}
1 & 2 & 3\\
a & b & c
\end{matrix}
  • Check whether it is a matrix, and if it's the case, collect each of the text in the "cells", i.e. [1, 2, 3; a, b, c].
  • Computer the number of rows and columns;
  • Compute the dimensions of each element text, i.e. height and width;
  • Compute the adjusted location of each text.

Don't know if the idea is clear. But it could even be implemented in Luxor directly. Although, it would be better if it was done here.

@Kolaru
Copy link
Owner

Kolaru commented Jan 3, 2022

The main problem I can see is to correctly parse environnments, which is simply not supported currently.

I will need a bit of time to thing about it and how to properly represent the parsed information.

Then the layouting of the elements should not be too hard.

Overall it is definitely doable, I'll have a go at it (and other issues) at the end of the week.

I don't really know where to look in the engine right now to get started

If you want to have a go directly and need some help, don't hesitate to bother me here or on slack ;)

Generally speaking there are always two parts:

  • The parser (the hard one) : currently a poorly documented marvel of black-magic using Automa.jl. It gets the useful information out of latex string to be passed to the engine proper.
  • The engine (the fun one) : here most likely all you need is in this function
    function tex_layout(expr, state)
    that simply has a clause for every possible supported construct and builds them recursively.

The good part about the second one is that it is pretty much pure geometry at this point so if you come up with the algorithm to layout the matrix elements in Javis (based on a reasonnable data structure for the elements, e.g. a julia matrix of latex elements), it should be easy to adapt it and include it in MathTeXEngine.jl.

@TheCedarPrince
Copy link
Author

@Kolaru this is a great explanation.
I took a look at Automa.jl - how fascinating!
It seems to use a nifty set of rules to parse out text.
Am I thinking correctly, you would use this approach to parse out the LaTeX to get each element of matrix?
I see what you mean as from there, putting each element into a corresponding Julia matrix would be rather straightforward.

I'll see if I can experiment with this on Thursday.
Thanks for the pointers - much appreciated.
Let me know if you have more thoughts or ideas as well, but I think experimenting with Automa.jl is going to be where I'll start!

@Kolaru
Copy link
Owner

Kolaru commented Jan 5, 2022

Am I thinking correctly, you would use this approach to parse out the LaTeX to get each element of matrix?

You are, but there are of course some more tricks involved.

Since I had some time in the train and you look motivated to look at the parser, I just added a short dev doc for the parser here: https://github.com/Kolaru/MathTeXEngine.jl/tree/master/src/parser

@TheCedarPrince
Copy link
Author

This is brilliant!
Thank you @Kolaru - yes, I am feeling motivated so let's see what progress I can get done here. :)

@TheCedarPrince
Copy link
Author

Hey @Kolaru !
I had a look through your dev doc that you prepared.
That was perfect and I loved the final line: 🙂

You can watch the rise and fall of the stack by passing showdebug=true to texparse. It is currently not as fun as to watch an old empire rise and fall, but beware, it is nearly as verbose.

I am now switching over to looking through Automa.jl so we will see what I learn there.
I did look at parser.jl and based on what I saw, it seems we need to first address the problem of LaTeX environment parsing as mentioned in #25 .
So, based on the parser logic, it seems like the following problems are present:

  • Recognize a command by \ presence
  • Perform a lookahead to disambiguate between command and environment (looks like the logic may be similar to things like \vec{v}vector LaTeX syntax
  • Find group { and } which again seems similar to vec parsing
  • Find the end of the environment

Some of these look either solved or partially solved, but I do not know.
So it feels like a first step is to address the problem of environment recognition then, we can move specifically to types of environments present.
What do you think?

@Kolaru
Copy link
Owner

Kolaru commented Jan 7, 2022

Yes you mostly got it. The way I would go about it is

  1. Register the \begin command as a 1 argument command i.e. with canonical form (:argument_gatherer, [:environnement, 1]). Everything is already implemented there, so once \begin{envname} is read, there will be (:environnement, ["envname"]) on top of the stack. (*)
  2. While an :environnement expr is on top of the stack adapt the parsing and push the data to it to get something like (:environnement, ["envname", data...]). This is where most of the work is needed I believe.
  3. Register the \end command similar to the \begin command, with e.g. (:argument_gatherer, [:endenv, 1]).
  4. Add a special rule in the parser such that when an :endenv is found it terminates the :environnement currently on top of the stack. If the expression on top of the stack is not an environnement, or if its name does not match the one of the \end command, raise a parsing error. It means that there is either a dangling \end{envname} without the corresponding \begin or another unfinished construct inside the \begin...\end block.
  5. Find out all the problems you only see when you actually implement the stuff :D

I think that should be pretty much it.

(*) This is what we do for example for \vec which is registered here (\vec is part of the combining_accents family):

for command in combining_accents
combining_char = get_symbol_char(command)
symbol_expr = TeXExpr(:symbol, combining_char)
command_to_canonical[command] = TeXExpr(:argument_gatherer, [:combining_accent, 2, symbol_expr])
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants