Skip to content

Commit 1421ddb

Browse files
author
Yusuke Oda
authored
Refactoring of the library (#58)
* refactoring * rename
1 parent 3a2cb70 commit 1421ddb

8 files changed

+314
-249
lines changed

src/integration_tests/regression_test.py

+48-60
Original file line numberDiff line numberDiff line change
@@ -70,24 +70,23 @@ def sum_with_limit_two_args(a, n):
7070
)
7171

7272

73-
func_and_latex_str_list = [
74-
(solve, solve_latex, None),
75-
(sinc, sinc_latex, None),
76-
(xtimesbeta, xtimesbeta_latex, True),
77-
(xtimesbeta, xtimesbeta_latex_no_symbols, False),
78-
(sum_with_limit, sum_with_limit_latex, None),
79-
(sum_with_limit_two_args, sum_with_limit_two_args_latex, None),
80-
]
81-
82-
83-
@pytest.mark.parametrize("func, expected_latex, math_symbol", func_and_latex_str_list)
84-
def test_with_latex_to_str(func, expected_latex, math_symbol):
73+
@pytest.mark.parametrize(
74+
"func, expected_latex, use_math_symbols",
75+
[
76+
(solve, solve_latex, None),
77+
(sinc, sinc_latex, None),
78+
(xtimesbeta, xtimesbeta_latex, True),
79+
(xtimesbeta, xtimesbeta_latex_no_symbols, False),
80+
(sum_with_limit, sum_with_limit_latex, None),
81+
(sum_with_limit_two_args, sum_with_limit_two_args_latex, None),
82+
],
83+
)
84+
def test_with_latex_to_str(func, expected_latex, use_math_symbols):
8585
"""Test with_latex to str."""
86-
# pylint: disable=protected-access
87-
if math_symbol is None:
86+
if use_math_symbols is None:
8887
latexified_function = with_latex(func)
8988
else:
90-
latexified_function = with_latex(math_symbol=math_symbol)(func)
89+
latexified_function = with_latex(use_math_symbols=use_math_symbols)(func)
9190
assert str(latexified_function) == expected_latex
9291
expected_repr = r"$$ \displaystyle %s $$" % expected_latex
9392
assert latexified_function._repr_latex_() == expected_repr
@@ -110,57 +109,46 @@ def inner(y):
110109
assert get_latex(nested(3)) == r"\mathrm{inner}(y) \triangleq xy"
111110

112111

113-
def test_assign_feature():
114-
@with_latex
115-
def f(x):
116-
return abs(x) * math.exp(math.sqrt(x))
117-
118-
@with_latex
119-
def g(x):
120-
a = abs(x)
121-
b = math.exp(math.sqrt(x))
122-
return a * b
123-
124-
@with_latex(reduce_assignments=False)
125-
def h(x):
126-
a = abs(x)
127-
b = math.exp(math.sqrt(x))
128-
return a * b
129-
130-
assert str(f) == (
131-
r"\mathrm{f}(x) \triangleq \left|{x}\right|\exp{\left({\sqrt{x}}\right)}"
132-
)
133-
assert str(g) == (
134-
r"\mathrm{g}(x) \triangleq "
135-
r"\left( "
136-
r"\left|{x}\right| \right)\left( \exp{\left({\sqrt{x}}\right)} "
137-
r"\right)"
112+
def test_use_raw_function_name():
113+
def foo_bar():
114+
return 42
115+
116+
assert str(with_latex(foo_bar)) == r"\mathrm{foo_bar}() \triangleq 42"
117+
assert (
118+
str(with_latex(foo_bar, use_raw_function_name=True))
119+
== r"\mathrm{foo\_bar}() \triangleq 42"
138120
)
139-
assert str(h) == (
140-
r"a \triangleq "
141-
r"\left|{x}\right| \\ "
142-
r"b \triangleq \exp{\left({\sqrt{x}}\right)} \\ "
143-
r"\mathrm{h}(x) \triangleq ab"
121+
assert (
122+
str(with_latex(use_raw_function_name=True)(foo_bar))
123+
== r"\mathrm{foo\_bar}() \triangleq 42"
144124
)
145125

146-
@with_latex(reduce_assignments=True)
126+
127+
def test_reduce_assignments():
147128
def f(x):
148-
a = math.sqrt(math.exp(x))
149-
return abs(x) * math.log10(a)
129+
a = x + x
130+
return 3 * a
131+
132+
assert str(with_latex(f)) == r"a \triangleq x + x \\ \mathrm{f}(x) \triangleq 3a"
133+
134+
latex_with_option = r"\mathrm{f}(x) \triangleq 3\left( x + x \right)"
135+
assert str(with_latex(f, reduce_assignments=True)) == latex_with_option
136+
assert str(with_latex(reduce_assignments=True)(f)) == latex_with_option
150137

151-
assert str(f) == (
152-
r"\mathrm{f}(x) \triangleq "
153-
r"\left|{x}\right|"
154-
r"\log_{10}{\left({\left( \sqrt{\exp{\left({x}\right)}} \right)}\right)}"
155-
)
156138

157-
@with_latex(reduce_assignments=False)
139+
def test_reduce_assignments_double():
158140
def f(x):
159-
a = math.sqrt(math.exp(x))
160-
return abs(x) * math.log10(a)
141+
a = x**2
142+
b = a + a
143+
return 3 * b
161144

162-
assert str(f) == (
163-
r"a \triangleq "
164-
r"\sqrt{\exp{\left({x}\right)}} \\ "
165-
r"\mathrm{f}(x) \triangleq \left|{x}\right|\log_{10}{\left({a}\right)}"
145+
assert str(with_latex(f)) == (
146+
r"a \triangleq x^{2} \\ b \triangleq a + a \\ \mathrm{f}(x) \triangleq 3b"
147+
)
148+
149+
latex_with_option = (
150+
r"\mathrm{f}(x) \triangleq "
151+
r"3\left( \left( x^{2} \right) + \left( x^{2} \right) \right)"
166152
)
153+
assert str(with_latex(f, reduce_assignments=True)) == latex_with_option
154+
assert str(with_latex(reduce_assignments=True)(f)) == latex_with_option

src/latexify/__init__.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414
"""Latexify toplevel module."""
1515

16-
from latexify import core
16+
from latexify import frontend
1717

18-
get_latex = core.get_latex
19-
with_latex = core.with_latex
18+
get_latex = frontend.get_latex
19+
with_latex = frontend.with_latex

src/latexify/constants.py

-48
Original file line numberDiff line numberDiff line change
@@ -25,54 +25,6 @@ class Actions(NamedTuple):
2525

2626
actions = Actions()
2727

28-
MATH_SYMBOLS = {
29-
"aleph",
30-
"alpha",
31-
"beta",
32-
"beth",
33-
"chi",
34-
"daleth",
35-
"delta",
36-
"digamma",
37-
"epsilon",
38-
"eta",
39-
"gamma",
40-
"gimel",
41-
"iota",
42-
"kappa",
43-
"lambda",
44-
"mu",
45-
"nu",
46-
"omega",
47-
"phi",
48-
"pi",
49-
"psi",
50-
"rho",
51-
"sigma",
52-
"tau",
53-
"theta",
54-
"upsilon",
55-
"varepsilon",
56-
"varkappa",
57-
"varphi",
58-
"varpi",
59-
"varrho",
60-
"varsigma",
61-
"vartheta",
62-
"xi",
63-
"zeta",
64-
"Delta",
65-
"Gamma",
66-
"Lambda",
67-
"Omega",
68-
"Phi",
69-
"Pi",
70-
"Sigma",
71-
"Theta",
72-
"Upsilon",
73-
"Xi",
74-
}
75-
7628
PREFIXES = ["math", "numpy", "np"]
7729

7830
BUILTIN_CALLEES = {

src/latexify/core_test.py

Whitespace-only changes.

src/latexify/frontend.py

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"""Frontend interfaces of latexify."""
2+
3+
from __future__ import annotations
4+
5+
import ast
6+
from collections.abc import Callable
7+
import inspect
8+
import textwrap
9+
from typing import Any
10+
11+
import dill
12+
13+
from latexify import latexify_visitor
14+
15+
16+
def get_latex(
17+
fn: Callable[..., Any],
18+
*,
19+
use_math_symbols: bool = False,
20+
use_raw_function_name: bool = False,
21+
reduce_assignments: bool = False,
22+
) -> str:
23+
"""Obtains LaTeX description from the function's source.
24+
25+
Args:
26+
fn: Reference to a function to analyze.
27+
use_math_symbols: Whether to convert identifiers with a math symbol surface
28+
(e.g., "alpha") to the LaTeX symbol (e.g., "\\alpha").
29+
use_raw_function_name: Whether to keep underscores "_" in the function name,
30+
or convert it to subscript.
31+
reduce_assignments: If True, assignment statements are used to synthesize
32+
the final expression.
33+
34+
Returns:
35+
Generatee LaTeX description.
36+
"""
37+
try:
38+
source = inspect.getsource(fn)
39+
except Exception:
40+
# Maybe running on console.
41+
source = dill.source.getsource(fn)
42+
43+
# Remove extra indentation so that ast.parse runs correctly.
44+
source = textwrap.dedent(source)
45+
46+
tree = ast.parse(source)
47+
48+
visitor = latexify_visitor.LatexifyVisitor(
49+
use_math_symbols=use_math_symbols,
50+
use_raw_function_name=use_raw_function_name,
51+
reduce_assignments=reduce_assignments,
52+
)
53+
54+
return visitor.visit(tree)
55+
56+
57+
class LatexifiedFunction:
58+
"""Function with latex representation."""
59+
60+
def __init__(self, fn, **kwargs):
61+
self._fn = fn
62+
self._str = get_latex(fn, **kwargs)
63+
64+
@property
65+
def __doc__(self):
66+
return self._fn.__doc__
67+
68+
@__doc__.setter
69+
def __doc__(self, val):
70+
self._fn.__doc__ = val
71+
72+
@property
73+
def __name__(self):
74+
return self._fn.__name__
75+
76+
@__name__.setter
77+
def __name__(self, val):
78+
self._fn.__name__ = val
79+
80+
def __call__(self, *args):
81+
return self._fn(*args)
82+
83+
def __str__(self):
84+
return self._str
85+
86+
def _repr_latex_(self):
87+
"""IPython hook to display LaTeX visualization."""
88+
return r"$$ \displaystyle " + self._str + " $$"
89+
90+
91+
def with_latex(*args, **kwargs) -> Callable[[Callable[..., Any]], LatexifiedFunction]:
92+
"""Translate a function with latex representation.
93+
94+
This function works with or without specifying the target function as the positional
95+
argument. The following two syntaxes works similarly.
96+
- with_latex(fn, **kwargs)
97+
- with_latex(**kwargs)(fn)
98+
99+
Args:
100+
*args: No argument, or a callable.
101+
**kwargs: Arguments to control behavior. See also get_latex().
102+
103+
Returns:
104+
- If the target function is passed directly, returns the wrapped function.
105+
- Otherwise, returns the wrapper function with given settings.
106+
"""
107+
if len(args) == 1 and isinstance(args[0], Callable):
108+
return LatexifiedFunction(args[0], **kwargs)
109+
110+
def wrapper(fn):
111+
return LatexifiedFunction(fn, **kwargs)
112+
113+
return wrapper

0 commit comments

Comments
 (0)