latexify_py icon indicating copy to clipboard operation
latexify_py copied to clipboard

Generates algorithmic environment

Open odashi opened this issue 3 years ago • 13 comments

I have worked recently to investigate if we could generate the LaTeX algorithm environment from the function. Actually it can work like this:

FfJj0MyVEAAe01M

I think this feature is really useful for almost arbitrary functions.

odashi avatar Oct 21 '22 04:10 odashi

There are several caveats.

Specifically, IPython supports only MathJax, meaning that it accepts only a single expression. To output more complicated LaTeX document, we need to complie the source directly, and convert the generated file to an image that IPython accepts.

In the work above, I used pdflatex and pdf2image. They requires to install external libraries, e.g., texlive with the algorithmicx package (for pdflatex) and Poppler (for pdf2image).

odashi avatar Oct 21 '22 04:10 odashi

An option to implement it for IPython is to use immediate newline in the single expression with appropriate manual spacing, but it requires some hacks. I think it would be better to apply external commands to generate the compiled image.

odashi avatar Oct 21 '22 04:10 odashi

Can this, if implemented, be added to the ipynb example?

luisarandas avatar Oct 26 '22 22:10 luisarandas

@luisArandas Yes, the poc works on Jupyter and/or Colab and we can add examples.

odashi avatar Oct 27 '22 01:10 odashi

I guess composing everything into array would be another option to show the result on Jupyter. Maybe we can introduce multiple codegen (one for algorithmic, one for jupyter).

FgdCA52aMAA5ZQX

odashi avatar Nov 07 '22 23:11 odashi

I'd agree with multiple code gen, targeting either Jupyter or algorithmic. I'd be interested in taking a look into algorithmic at some point.

ZibingZhang avatar Nov 21 '22 18:11 ZibingZhang

I have some ideas about this.

  • First, we can provide the ExpressionCodegen, which converts ast.expr to LaTeX. I think ast.expr is the minimal fragment of AST that we can share the same behavior among different output styles.
  • Then, we provide several codegens, such as FunctionCodegen, AlgorithmCodegen, IPythonAlgorithmCodegen, or something else, to process the whole syntax. They invokes ExpressionCodegen internally to obtain the underlying expressions, and organize all results along with the style that the codegen finally produces.

This design has also an advantage about programmatic use of this library. We can define a function latexify.compile_expression("...expr..."), which directly invokes ExpressionCodegen and returns the LaTeX. (also discussed in #108)

odashi avatar Nov 21 '22 23:11 odashi

I would think that ExpressionCodegen(ast.NodeVisitor) would be the parent class to FunctionCodegen, AlgorithmCodegen, etc., since I think the visiting of nodes for expressions should be shared among them?

Would you happen to have the LaTeX code for the example MAX_IN_ARRAY function in the image above? Not super familiar with using Latex in this way, and it would be nice for an example to get a sense of what the end result would be here.

ZibingZhang avatar Nov 22 '22 13:11 ZibingZhang

ExpressionCodegen(ast.NodeVisitor) would be the parent class to FunctionCodegen, AlgorithmCodegen, etc

We don't need inheritance (and shouldn't adopt inheritance here IMO) because there's basically no advantage over composition in this case. We could define ExpressionCodegen as just a kind of function.

For the first example, I tried to synthesize the following LaTeX environment: https://www.overleaf.com/learn/latex/Algorithms (note that there are many options/variants of this environment)

odashi avatar Nov 22 '22 19:11 odashi

Maybe an required keyword argument to specify the argument to target?

ZibingZhang avatar Nov 24 '22 14:11 ZibingZhang

I guess you meant some argument to specify the output style. Yes we need to have one for it (and maybe it'd be better to integrate the current function and expression behaviors into "style")

odashi avatar Nov 25 '22 03:11 odashi

Maybe adding a sytle=StyleEnum field in the config, and instead of latexify.expression being a wrapper around latexify.function with use_signature=False. Instead it would be a wrapper with config.style = Style.EXPRESSION?

That way we could have things like

@latexify.function(style=Style.EXPRESSION)
def fact(n):
    return 1 if n == 0 else n * fact(n - 1)

and we could have

Style(str, enum.Enum):
    EXPRESSION = "expression"
    FUNCTION = "function"
    ALGPSEUDOCODE = "algpseudocode"

ZibingZhang avatar Nov 27 '22 17:11 ZibingZhang

Yes. And we should have functions for every choice of style. As this is an essential config, users shouldn't write the style parameter every time.

odashi avatar Nov 27 '22 19:11 odashi

An option to implement it for IPython is to use immediate newline in the single expression with appropriate manual spacing, but it requires some hacks. I think it would be better to apply external commands to generate the compiled image.

I think it might be nice to try to do this using said hacks so that we don't add unnecessary dependancies.

image

I don't think it would actually be that difficult. Do you mind if I try to accomplish this, or did you prefer to go the image approach?

ZibingZhang avatar Dec 11 '22 01:12 ZibingZhang

I don't like the image approach actually, so going with two codegens is fine.

~~~~

Better to use \hspace{width} rather than whitespace characters, because indentation does not mean anything about the expression itself. (it also allows us to control the amount of the depth afterwards)

\mathrm{\textbf{foo}}

Better to use \mathbf{foo}.

See also my previous example.

odashi avatar Dec 11 '22 04:12 odashi