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

Eqn parser #59

Open
philchalmers opened this issue Aug 22, 2024 · 18 comments
Open

Eqn parser #59

philchalmers opened this issue Aug 22, 2024 · 18 comments

Comments

@philchalmers
Copy link
Collaborator

I've placed a barebones parser in dev/eqn_parser.R which implements some of the ideas I was thinking w.r.t. reducing the amount of LaTeX code for the novice user, but still allow power users freedom. The idea is to try and reduce equations such as

Eqn("\\mathcal{H}_0 : \\mathcal{C} \\mathcal{B} & = ")
Eqn("\\mathbf{H}_0 : \\mathbf{C} \\mathbf{B} & = ")

to something analogous to markdown but for LaTeX equations, such as

Eqn("*H*_0 : *C B* & = ")
Eqn("**H**_0 : **C B** & = ")

Of course, the main limitation is that LaTeX has many more environments to choose from, so I've left it possible for users to define what * and ** should mean in this context, among others. This parser also allows for macro definitions, where for example '*' will give \\times and %n will give the same string that's currently provided by Eqn_newline(). Other macros starting with % are easy to add at this point.

There are number of other avenues to go, such as detecting Greek letter inputs to, say, replace beta -> \\beta to reduce the \\ requirements, but I worry about going too deep here.

@philchalmers
Copy link
Collaborator Author

philchalmers commented Aug 26, 2024

Related to #57, the equation Michael was presenting can now be constructed using the following dev/printEqn.R, which follows a similar presentation format as sprintf() but for latexMatrix, matrix, and character vector substitutions flagged with an % symbol. So the original

C <- latexMatrix(matrix(c(0,1,1,0), nrow=1), matrix = "bmatrix")
B <- latexMatrix('\\beta', ncol = 3, nrow=4,   comma=TRUE, prefix.col = 'y_')
B0 <- latexMatrix('\\beta', ncol = 3, nrow=2, comma=TRUE, prefix.col = 'y_')

with the presentation construction

Eqn("\\mathcal{H}_0 : \\mathbf{C} \\mathbf{B} & = ",
    C, B,
    Eqn_newline(), Eqn_newline(), 
    '&\n',
    B0,
    "= \\mathbf{0}_{(2 \\times 3)}", 
    align=TRUE)

can now be presented with the structure first + substitutions

printEqn("*H*_0 : **C B** & = %C %B \\\\
                          & = %B0 = **0**_{(2 `*` 3)}",
             list(C=C, B=B, B0=B0), `**`='mathbf', align=TRUE)

Or, even more simply if boldsymbol is sufficient (default for ** wrapper) and the objects C, B, and B0 are available in the parent frame, then

printEqn("*H*_0 : **C B** & = %C %B \\\\
                          & = %B0 = **0**_{(2 `*` 3)}", align=TRUE)

I personally find these much more readable and easier to modify.

@friendly
Copy link
Owner

That looks very interesting indeed, but I'll have to study it some more to get a better sense of what can be done with this.

@philchalmers
Copy link
Collaborator Author

Just to complete Phil's example (perhaps for the vignette):

  A <- latexMatrix("a", 2, 2)
  B <- latexMatrix("b", 2, 2)
  kronecker(A, B)
  
  # Generate the 'definition' of Kronecker product,
  Bmat <- latexMatrix('\\mathbf{B}', ncol=1, nrow=1)
  kronecker(A, Bmat)
  
  Eqn("\\mathbf{A} \\otimes \\mathbf{B} = &",
      kronecker(A, Bmat),
      "\\\\[1.5ex]\n= & ",
      kronecker(A, B),
      align = TRUE)

image

For future posterity, here's how printEqn() would look for the Kronecker example:

A <- latexMatrix("a", 2, 2)
B <- latexMatrix("b", 2, 2)
kronecker(A, B) |> Eqn()

# Generate the 'definition' of Kronecker product,
Bmat <- latexMatrix('\\mathbf{B}', ncol=1, nrow=1)
KABmat <- kronecker(A, Bmat)
KAB <- kronecker(A, B)

Eqn("\\mathbf{A} \\otimes \\mathbf{B} = &",
    KABmat,
    "\\\\[1.5ex]\n= & ",
    KAB,
    align = TRUE)


source(here::here('dev', 'printEqn.R'))
printEqn("**A** \\otimes **B** = & %KABmat \\\\[1.5ex]
                               = & %KAB", align=TRUE)

@philchalmers
Copy link
Collaborator Author

philchalmers commented Aug 30, 2024

With the new partition.R, which is always fun with Kronecker products.

A <- latexMatrix("a", 2, 2)
B <- latexMatrix("b", 2, 2)

# Generate the 'definition' of Kronecker product,
Bmat <- latexMatrix('\\mathbf{B}', ncol=1, nrow=1)

source(here::here('dev', 'partition.R'))
source(here::here('dev', 'printEqn.R'))
KABmat <- kronecker(A, Bmat) |> partition(rows=1, columns=1)
KAB <- kronecker(A, B) |> partition(rows=2, columns=2)

printEqn("**A** \\otimes **B** = & %KABmat \\\\[1.5ex]
                               = & %KAB", align=TRUE)

The last of which gives

equation (1)

EDIT: uploaded wrong jpg.

@friendly
Copy link
Owner

In the examples above, we use Eqn_newline(), but also "\\\\[1.5ex] where it's desired to add some extra space.
Could Eqn_newline() gain a vspace= argument for this purpose?

philchalmers added a commit that referenced this issue Aug 31, 2024
@philchalmers
Copy link
Collaborator Author

Added. Eqn_newline(1.5) now gives the desired output, though the metric can be changed as well.

@philchalmers
Copy link
Collaborator Author

Quarto support added for equations by adding Eqn(, quarto=TRUE), but AFAICS there's no way to include ref() as the quarto compiler seems to treat @ instances at different points in the complication. Equations can still be referenced via @eq-myeqn in the usual Quarto way, but not in the inline {r} chunks. Slightly more awkward, but I suppose if one if going the Quarto route then this is a minimal inconvenience as the HTML and PDF outputs use the same format.

@friendly
Copy link
Owner

friendly commented Sep 4, 2024

Great! Hope you don't mind if I tweak the documentation a bit.

@philchalmers
Copy link
Collaborator Author

philchalmers commented Sep 6, 2024

Took some digging, but I finally found a way to make quarto behave well with ref(), which now gives the same behavior as the other methods. I'm looking into a more automated way to detect the CLI type similar to knitr::is_html_output(), but the best I've found so far is quarto::is_using_quarto() and I don't particularly like it in this context as the function is very greedy (returns TRUE if either "_quarto.yml⁠ at its root" or "at least one .qmd file in the directory").

I'm thinking about using something like knitr::current_input() to see if the file extension is .qmd, but this would require some testing and may not behave well with secondary applications such as pkgdown. I'll keep you posted.

@friendly
Copy link
Owner

friendly commented Sep 6, 2024

That looks good. Is it the case that options("quartoEqn") is NULL by default, and presently must be set TRUE to use in a quarto doc?

@philchalmers
Copy link
Collaborator Author

Currently, yes, one would need to use options("quartoEqn" = TRUE) at the beginning of the document to switch to Quarto mode.

I have a working solution to this problem in https://github.com/friendly/matlib/blob/master/dev/Eqn_test_quarto.qmd but it involves a file name constraint in that, for example, mydoc.qmd and mydoc.Rmd or mydoc.Rnw cannot live in the same directory, otherwise an error will be raised. If we're fine with this file naming constraint I'd be happy to port this automation as it should work with 'pkgdown' too.

@friendly
Copy link
Owner

friendly commented Sep 6, 2024

Is setQuartoEqn() to be called by the user, or will this be called by Eqn() and friends?

@philchalmers
Copy link
Collaborator Author

The latter; this shouldn't be the user's problem to automate.

@philchalmers
Copy link
Collaborator Author

Updates to this thread: quarto support now fully functional up to the unique .qmd file limitation, and preview.pdf and preview.packages options have been added to generate equations using LaTeX compliers rather than MathJax whenever this is useful.

image

and with a complied document

image

(not that it's helpful with the border matrix feature, just showing this is possible to preview when PDFs are built).

Also, printEqn() now detects Greek letters so that ** will apply a \boldsymbol{} to detected Greek letters rather than the less attractive \mathbf{}.

@john-d-fox
Copy link
Collaborator

Does it make sense to support \bordermatrix optionally in print.latexMatrix(), say via an argument bordermatrix, which gets its default value from an option and defaults to FALSE if the option isn't set?

@philchalmers
Copy link
Collaborator Author

Yes, I think that's reasonable. It's unfortunate that a side-step is required to deal with MathJax limitations, though having the option to use the more kosher version for PDF outputs is attractive and IMO preferable.

@john-d-fox
Copy link
Collaborator

OK, I'll do that when I have a chance.

@john-d-fox
Copy link
Collaborator

I added support for \bordermatrix{} in print.latexMatrix(). The implementation is straightforward and seems to work fine (but, as usual, should be tested more). A limitation of \bordermatrix{} is that it apparently doesn't support matrix delimiters other than parentheses.

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

No branches or pull requests

3 participants