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

Multiplication needs extra processing to reorder coefficient and variable #114

Open
Casper-Guo opened this issue Nov 16, 2022 · 7 comments
Assignees
Labels
Milestone

Comments

@Casper-Guo
Copy link
Contributor

Environment

If you used latexify in your own environment, fill at least the following items.
Feel free to add other items if you think they are useful.

  • OS: Windows 10
  • Python: 3.8
  • Package manager: pip 22.2.1
  • Latexify version: current dev branch

Description

The current implementation of visit_BinOp doesn't change the ordering of the operands. This is desirable or acceptable for most operations with multiplication being the exception. Numeric coefficients should always precede variable names. That is a*2 and 2*a should both produce 2a.

def visit_BinOp(self, node: ast.BinOp) -> str:
"""Visit a BinOp node."""
prec = _get_precedence(node)
rule = self._bin_op_rules[type(node.op)]
lhs = self._wrap_binop_operand(node.left, prec, rule.operand_left)
rhs = self._wrap_binop_operand(node.right, prec, rule.operand_right)
return f"{rule.latex_left}{lhs}{rule.latex_middle}{rhs}{rule.latex_right}"

Reproduction

@latexify.function
def solve(a, b, c):
    return (-b + math.sqrt(b**2 - 4*a*c)) / (a*2)

print(solve(1, 4, 3))
print(solve)
solve

produces

\mathrm{solve}(a, b, c) = \frac{-b + \sqrt{b^{{2}} - {4} a c}}{a {2}}

$\mathrm{solve}(a, b, c) = \frac{-b + \sqrt{b^{{2}} - {4} a c}}{a {2}}$

Expected behavior

The above should produce:

\mathrm{solve}(a, b, c) = \frac{-b + \sqrt{b^{{2}} - {4} a c}}{{2} a}

$\mathrm{solve}(a, b, c) = \frac{-b + \sqrt{b^{{2}} - {4} a c}}{{2} a}$

@odashi
Copy link
Collaborator

odashi commented Nov 16, 2022

To be clear: this reordering should be applicable only when the targeted node represents the exact number.

The algorithm can be implemented as a new transformer I think. We should also provide a boolean option to switch the behavior.

@odashi odashi removed the triage label Nov 16, 2022
@odashi odashi added this to the v0.3 milestone Nov 17, 2022
@ZibingZhang
Copy link
Contributor

What about in more complicated cases?

def solve(x):
    return 2 * x * 4

It's probably desirable to bubble constant numbers to the front of each product expression somehow, e.g. 2 * x * 4 + a * 3 -> 2 * 4 * x + 3 * a.

@odashi
Copy link
Collaborator

odashi commented Nov 20, 2022

I think we need to handle #89 before this issue. Adding \cdot ( $\cdot$ ) should be the default behavior of the multiplication, and in several cases we can avoid the symbol. As long as inserting \cdot, all expressions should be fine without any modification of AST (but the appearance becomes redundant)

@Casper-Guo
Copy link
Contributor Author

Now that #139 is done, the only times where \cdot shouldn't be used are between a number and a single-character variable or between two single-character variables. The ordering of these variables can be handled using default Python string ordering (e.g "2" < "a" = True)

What about in more complicated cases?

def solve(x):
    return 2 * x * 4

It's probably desirable to bubble constant numbers to the front of each product expression somehow, e.g. 2 * x * 4 + a * 3 -> 2 * 4 * x + 3 * a.

In such cases we can implement a simplification procedure similar to #115 and combine all the constant coefficients.

@ZibingZhang
Copy link
Contributor

ZibingZhang commented Dec 4, 2022

I'm not sure combining constant coeff is always desirable. It may be nice to keep separate for things like 4 * 3.14 / 3 r^3.

If the user wants them combined they can do it themselves, or maybe it can be introduced as a config option, defaulting to False.

@Casper-Guo
Copy link
Contributor Author

Good point, it should be configurable

On a related note, operations with brackets, such as (3*a)*(2*b) also shouldn't be modified. If IIRC, this is the default behavior anyways

@odashi
Copy link
Collaborator

odashi commented Dec 5, 2022

The ordering of these variables can be handled using default Python string ordering (e.g "2" < "a" = True)

Yeah that's true, but somewhat tricky. It looks it is desirable to check it in more declarative manner.

I'm not sure combining constant coeff is always desirable

It is generally good to keep the original syntax as-is. It would be good to make every option normally off if it is too intelligent (the good example is reduce_assignments: this is quite useful IMO, but may confuse users if it is enabled by default)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants