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

Add More Functions #15

Merged
merged 4 commits into from
Aug 5, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 25 additions & 4 deletions latexify/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ 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
return name_str + '(' + ', '.join(arg_strs) + r')\triangleq ' + body_str

def visit_Return(self, node):
return self.visit(node.value)
Expand Down Expand Up @@ -82,7 +82,7 @@ def visit_Call(self, node):
else:
if callee_str.startswith('math.'):
callee_str = callee_str[5:]
lstr = r'\operatorname{' + callee_str + '}\left('
lstr = r'\operatorname{' + callee_str + r'}\left('
rstr = r'\right)'

arg_strs = [self.visit(arg) for arg in node.args]
Expand All @@ -103,18 +103,19 @@ def visit_UnaryOp(self, node):
def _wrap(child):
latex = self.visit(child)
if isinstance(child, ast.BinOp) and isinstance(child.op, (ast.Add, ast.Sub)):
return '(' + latex + ')'
return r'\left(' + latex + r'\right)'
return latex

reprs = {
ast.UAdd: (lambda: _wrap(node.operand)),
ast.USub: (lambda: '-' + _wrap(node.operand)),
ast.Not: (lambda: r'\lnot\left(' + _wrap(node.operand)+r'\right)')
}

if type(node.op) in reprs:
return reprs[type(node.op)]()
else:
return r'\operatorname{unknown\_uniop}(' + vstr + ')'
return r'\operatorname{unknown\_uniop}(' + self.visit(node.operand) + ')'

def visit_BinOp(self, node):
priority = {
Expand Down Expand Up @@ -164,9 +165,29 @@ def visit_Compare(self, node):

if isinstance(node.ops[0], ast.Eq):
return lstr + '=' + rstr
elif isinstance(node.ops[0], ast.Gt):
return lstr + '>' + rstr
elif isinstance(node.ops[0], ast.Lt):
return lstr + '<' + rstr
elif isinstance(node.ops[0], ast.GtE):
return lstr + r'\ge ' + rstr
elif isinstance(node.ops[0], ast.LtE):
return lstr + r'\le ' + rstr
elif isinstance(node.ops[0], ast.NotEq):
return lstr + r'\ne ' + rstr
elif isinstance(node.ops[0], ast.Is):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure simply printing is is good or not: could you remove is and is not for now? We need some discussion to make consensus.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for "is"&"is not":
I tested several cases-the layout, the view, the font, of "is" and "is not", and found that what I really need may actually be "\in" and "\notin". Because normal papers would almost never have statements like "$if x is int$", but "$if x \in Z$".
An approach is that the user manually define a variable like Z=type('Z',(int,),{}) and then use if x is Z to have the output "$if x is Z$". So maybe change "$is$" and "$is not$" to "$\in$" and "$\notin$" is better.
I cannot decide this alone, so it's great to make some discussion for this.

Copy link
Collaborator

@odashi odashi Aug 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this going?

For the use-cases like x \in Z, the equivalent Python functionality is isinstance(x, int). The Python's is is not a belongs-to (\in) operator: it checks if the two expressions represent the same instance, e.g., 1 is 1 is True. Also the Python's in is provided basically for collection types. If we want to implement the \in operator, I think we need to take two implementations: the Python's in and the pattern match replacement for isinstance. As the latter one may involve some garbages, it may be good to restrict acceptable types (e.g., number types only).

If we provide some representations of is, I think the nearest one is maybe isomorphic (\equiv) though there's some differences between them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I thought you mean not to change them at present.
OK, I will change the implementation of the ast.Is clause into \equiv and delete the ast.IsNot clause. As for isinstance, that couldn't be processed in visit_Compare so maybe that might be implemented in the future.

return lstr + r'\equiv' + rstr

else:
return r'\operatorname{unknown\_comparator}(' + lstr + ', ' + rstr + ')'

def visit_BoolOp(self, node):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function needs the same strategy to visit_BinOp to deal with parenthesis appropriately (generally, logical operators has priorities: not > and > or)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for "visit_BoolOp"
At the first version of visit_BoolOp, I actually wrote the not parts. But after testing a few cases I found it kind of silly to have output like "$if not x\equal 10$", it's not something would appear in anyone's paper. Instead what we actually want is "$if x\neq 10$", but that needs a new list or dict to detect and find proper negative statements. I will work on that after next commit.
About the parenthesis, it is true that parenthesis needs to be mentioned, but when it comes to LaTeX output it needs to be reader-friendly. One approach is to add brackets, another is to start a new line when indicating priority like:

if x < 1 \lor x > 10\\
\land x>0

for if (x<1 or x>10)and(x>0).
"$\lor$" and "$\land$" seem not so good at such case btw.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as the papers I read, most authors tend to use comma to connect statements have AND relationship, because OR relationship is quite rarely seen. ORs are likely to be clearly indicated when needed.
If you have a better output format for this plz reply me, and I will try to implement the brackets for parenthesis in next commit.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer single-line form with \neg, \lor and \land. It keeps the same syntax to Python except appearances of operators, and would eventually not surprise users. I think we have to keep minimality about extra conventions of LaTeX since any styles are basically not a common consensus of us (though there are already some strong assumptions introduced when I wrote this first).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the keeping minimality about extra conventions. I will try to implement the brackets for parenthesis in single-line form in the next commit.

logic_operator = r'\lor ' if isinstance(node.op, ast.Or) \
else r'\land ' if isinstance(node.op, ast.And) \
else r' \operatorname{unknown\_operator} '
# visit all the elements in the ast.If node recursively
return r'\left('+self.visit(node.values[0])+r'\right)'+logic_operator+r'\left('+self.visit(node.values[1])+r'\right)'

def visit_If(self, node):
latex = r'\left\{ \begin{array}{ll} '

Expand Down