latexify_py icon indicating copy to clipboard operation
latexify_py copied to clipboard

Support expanding some functions

Open odashi opened this issue 3 years ago • 3 comments

Follows #63

It is good to provide a expand_functions boolean option to control whether some composite functions are expanded or not, e.g.:

  • hypot(x, y) -> $\sqrt{x^2 + y^2}$
  • atan2(y, x) -> $\arctan{\frac{y}{x}}$
  • expit(x) -> $\frac{1}{1+\exp{(-x)}}$

odashi avatar Oct 25 '22 08:10 odashi

Is it better expit(x) to be $\frac{1}{1+e^{-x}}$ ?

MAKMED1337 avatar Oct 25 '22 09:10 MAKMED1337

@MAKMED1337 Not sure, but it looks there should be another option to control the behavior around $\exp$ .

odashi avatar Oct 25 '22 10:10 odashi

I think this can be solved by introducing function-level controls:

  • Register replacement rules Call -> AST for each applicable functions. If the expanded AST has other calls, it is kept as-is.
  • Assin a flag for each rule to control whether the rule is applied or not.
  • Add a flag to control the overall behavior (i.e., we perform the replacement if all_flag && fn_flag[fn_name] is True)
  • For the replacer routine (it may be NodeTransformer), if it finds a matched name, replace it to AST, and recursively applies the rules to replacements too.

This allows us to control the behavior more precisely. E.g., users can choose the result of expit(x) from either $\frac{1}{1+\exp{(-x)}}$ or $\frac{1}{1+e^{-x}}$

This also involves some caveats:

  • It causes infinite replacement if the rule has identity or cyclic rules: f(x) -> f(x), or f(x) -> g(x) and g(x) -> f(x), which may result in stack overflow. Since it is not easy to detect such behavior in advance, it may be necessary to constrain the maximum recursion of the replacements.
  • If with_latex takes every function-level flags, the syntax may become very complicated. I think providing a config function is better, something like:
import latexify

config = latexify.Config.defaults()
config.expand_function("expit")

@latexify.with_latex(config)
def f(x):
    return expit(x)

# Will generate: \mathrm{f}(x) \triangleq \frac{1}{1+\exp{(-x)}}

odashi avatar Oct 25 '22 17:10 odashi

List of methods in math that I think we should allow expansion for:

  • exp(x) -> $e^x$
  • exp2(x) -> $2^x$
  • expm1(x) -> $\exp(x) - 1$
  • log1p(x) -> $\log(1 + x)$
  • pow(x, y) -> $x^y$
  • atan2(x) -> $\arctan(y / x)$
  • erf -> too complicated, also I don't think integrals are supported... yet
  • erfc -> see above
  • gamma -> maybe for positive integers? I think it's best to leave alone for now
  • lgamma -> see above

Let me know what you think @odashi, and I'll get started on these

ZibingZhang avatar Nov 21 '22 10:11 ZibingZhang

I think non-elementary functions (erf, erfc, gamma, lgamma) should be avoided from this option. Other candidates look fine to me.

odashi avatar Nov 21 '22 11:11 odashi

I also think our current implementation (#125) naturally solves https://github.com/google/latexify_py/issues/65#issuecomment-1290288048 , as users can choose whether exp is expanded or not.

odashi avatar Nov 21 '22 11:11 odashi

This is generally supported in the current implementation. We'd support individual functions in separate issues (if needed).

odashi avatar Nov 28 '22 08:11 odashi