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

Greek letters, \bar{}, and \hat{} #6

Closed
ryxcommar opened this issue Jul 26, 2020 · 6 comments
Closed

Greek letters, \bar{}, and \hat{} #6

ryxcommar opened this issue Jul 26, 2020 · 6 comments

Comments

@ryxcommar
Copy link
Contributor

Something like this would be a good idea:

image

I have an implementation here, but I am not sure if it is good, or if it will do unexpected things:

https://github.com/ryxcommar/latexify_py/tree/master

@odashi
Copy link
Collaborator

odashi commented Jul 26, 2020

Yes indeed. This kind of modification should be optional, or it will include unintended changes, e.g.:

  • xi ... \xi or x_i
  • y_hat ... \hat{y} or y_{hat}

Especially the second case is quite troublesome, as the underscore (_) completely behaves differently.

For the interface, some optional parameter of with_latex/get_latex would help to switch the behavior:

@with_latex(math_symbol=True, diacritic=True)
def foobar(...):
  ...

I think math_symbol is okay to add, but consider diacritic is not for now. Probably we need to support subscripts before diacritics.

@ryxcommar
Copy link
Contributor Author

ryxcommar commented Jul 26, 2020

Something like this?

Behaviors:

image

Code:

# ...
from typing import Callable

# ...

class LatexifyVisitor(ast.NodeVisitor):

  def __init__(self, math_symbol=True):
    self.math_symbol = math_symbol
    super(ast.NodeVisitor).__init__()

  def _parse_math_symbols(self, val: str) -> str:
    if not self.math_symbol:
      return val
    else:
      greek_and_hebrew = [
          'aleph', 'alpha', 'beta', 'beth', 'chi', 'daleth',
          'delta', 'digamma', 'epsilon', 'eta', 'gamma', 'gimel',
          'iota', 'kappa', 'lambda', 'mu', 'nu', 'omega', 'omega',
          'phi', 'pi', 'psi', 'rho', 'sigma', 'tau', 'theta',
          'upsilon', 'varepsilon', 'varkappa', 'varphi', 'varpi', 'varrho',
          'varsigma',
          'vartheta', 'xi', 'zeta'
      ]
      if val.lower() in greek_and_hebrew:
        return '\\' + val + ' '
      else:
        return val

  # ........

  def visit_FunctionDef(self, node):
    name_str = r'\operatorname{' + str(node.name) + '}'
    arg_strs = [self._parse_math_symbols(str(arg.arg)) for arg in node.args.args]
    body_str = self.visit(node.body[0])
    return name_str + '(' + ', '.join(arg_strs) + r') \triangleq ' + body_str

  # ........

  def visit_Name(self, node):
    return self._parse_math_symbols(str(node.id))

# ........

def get_latex(fn, math_symbol=True):
  return LatexifyVisitor(math_symbol=math_symbol).visit(ast.parse(inspect.getsource(fn)))

# ........

def with_latex(*args, math_symbol=True):

  class _LatexifiedFunction:
    def __init__(self, fn):
      self._fn = fn
      self._str = get_latex(fn, math_symbol=math_symbol)

  # ........

  if len(args) == 1 and isinstance(args[0], Callable):
    return _LatexifiedFunction(args[0])
  else:
    return lambda fn: _LatexifiedFunction(fn)

@ryxcommar
Copy link
Contributor Author

ryxcommar commented Jul 26, 2020

I also suggest the following:

# Old:
ast.Mult: (lambda: _wrap(l) + _wrap(r)),

# New:
ast.Mult: (lambda: _wrap(l) + ' ' + _wrap(r)),

Otherwise this does not work:

function(beta, x):
    return beta * x

^ That example is why I use return '\\' + val + ' ':

beta * x --> \betax (bad)

versus

beta * x --> \beta x (good)

If you do ast.Mult: (lambda: _wrap(l) + ' ' + _wrap(r)), instead, then it is no longer necessary to do '\\' + val + ' '; you will only need '\\' + val

@odashi
Copy link
Collaborator

odashi commented Jul 26, 2020

SGTM for the behavior of the math_symbol.

You can insert braces as symbol separators: {\beta}{x}. I think every identifier should be put in a brace for safety.

@ryxcommar
Copy link
Contributor Author

Need to be careful with wrapping everything in {}. I realized if you do that for every self.visit_Name, you get this:

image

I am not good with the ast library, so I'm not sure how you would do it 100% correctly. For now I am going to be safe, and only convert a name if it is in greek_and_hebrew.

@odashi
Copy link
Collaborator

odashi commented Jul 27, 2020

Yes. I am considering a better way to traverse ASTs and will change them in some future commits.

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

2 participants