chore: 添加虚拟环境到仓库

- 添加 backend_service/venv 虚拟环境
- 包含所有Python依赖包
- 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
2025-12-03 10:19:25 +08:00
parent a6c2027caa
commit c4f851d387
12655 changed files with 3009376 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
"""Used for translating a string into a SymPy expression. """
__all__ = ['parse_expr']
from .sympy_parser import parse_expr

View File

@@ -0,0 +1,79 @@
"""
This module implements the functionality to take any Python expression as a
string and fix all numbers and other things before evaluating it,
thus
1/2
returns
Integer(1)/Integer(2)
We use the ast module for this. It is well documented at docs.python.org.
Some tips to understand how this works: use dump() to get a nice
representation of any node. Then write a string of what you want to get,
e.g. "Integer(1)", parse it, dump it and you'll see that you need to do
"Call(Name('Integer', Load()), [node], [], None, None)". You do not need
to bother with lineno and col_offset, just call fix_missing_locations()
before returning the node.
"""
from sympy.core.basic import Basic
from sympy.core.sympify import SympifyError
from ast import parse, NodeTransformer, Call, Name, Load, \
fix_missing_locations, Constant, Tuple
class Transform(NodeTransformer):
def __init__(self, local_dict, global_dict):
NodeTransformer.__init__(self)
self.local_dict = local_dict
self.global_dict = global_dict
def visit_Constant(self, node):
if isinstance(node.value, int):
return fix_missing_locations(Call(func=Name('Integer', Load()),
args=[node], keywords=[]))
elif isinstance(node.value, float):
return fix_missing_locations(Call(func=Name('Float', Load()),
args=[node], keywords=[]))
return node
def visit_Name(self, node):
if node.id in self.local_dict:
return node
elif node.id in self.global_dict:
name_obj = self.global_dict[node.id]
if isinstance(name_obj, (Basic, type)) or callable(name_obj):
return node
elif node.id in ['True', 'False']:
return node
return fix_missing_locations(Call(func=Name('Symbol', Load()),
args=[Constant(node.id)], keywords=[]))
def visit_Lambda(self, node):
args = [self.visit(arg) for arg in node.args.args]
body = self.visit(node.body)
n = Call(func=Name('Lambda', Load()),
args=[Tuple(args, Load()), body], keywords=[])
return fix_missing_locations(n)
def parse_expr(s, local_dict):
"""
Converts the string "s" to a SymPy expression, in local_dict.
It converts all numbers to Integers before feeding it to Python and
automatically creates Symbols.
"""
global_dict = {}
exec('from sympy import *', global_dict)
try:
a = parse(s.strip(), mode="eval")
except SyntaxError:
raise SympifyError("Cannot parse %s." % repr(s))
a = Transform(local_dict, global_dict).visit(a)
e = compile(a, "<string>", "eval")
return eval(e, global_dict, local_dict)

View File

@@ -0,0 +1,118 @@
grammar Autolev;
options {
language = Python3;
}
prog: stat+;
stat: varDecl
| functionCall
| codeCommands
| massDecl
| inertiaDecl
| assignment
| settings
;
assignment: vec equals expr #vecAssign
| ID '[' index ']' equals expr #indexAssign
| ID diff? equals expr #regularAssign;
equals: ('='|'+='|'-='|':='|'*='|'/='|'^=');
index: expr (',' expr)* ;
diff: ('\'')+;
functionCall: ID '(' (expr (',' expr)*)? ')'
| (Mass|Inertia) '(' (ID (',' ID)*)? ')';
varDecl: varType varDecl2 (',' varDecl2)*;
varType: Newtonian|Frames|Bodies|Particles|Points|Constants
| Specifieds|Imaginary|Variables ('\'')*|MotionVariables ('\'')*;
varDecl2: ID ('{' INT ',' INT '}')? (('{' INT ':' INT (',' INT ':' INT)* '}'))? ('{' INT '}')? ('+'|'-')? ('\'')* ('=' expr)?;
ranges: ('{' INT ':' INT (',' INT ':' INT)* '}');
massDecl: Mass massDecl2 (',' massDecl2)*;
massDecl2: ID '=' expr;
inertiaDecl: Inertia ID ('(' ID ')')? (',' expr)+;
matrix: '[' expr ((','|';') expr)* ']';
matrixInOutput: (ID (ID '=' (FLOAT|INT)?))|FLOAT|INT;
codeCommands: units
| inputs
| outputs
| codegen
| commands;
settings: ID (EXP|ID|FLOAT|INT)?;
units: UnitSystem ID (',' ID)*;
inputs: Input inputs2 (',' inputs2)*;
id_diff: ID diff?;
inputs2: id_diff '=' expr expr?;
outputs: Output outputs2 (',' outputs2)*;
outputs2: expr expr?;
codegen: ID functionCall ('['matrixInOutput (',' matrixInOutput)*']')? ID'.'ID;
commands: Save ID'.'ID
| Encode ID (',' ID)*;
vec: ID ('>')+
| '0>'
| '1>>';
expr: expr '^'<assoc=right> expr # Exponent
| expr ('*'|'/') expr # MulDiv
| expr ('+'|'-') expr # AddSub
| EXP # exp
| '-' expr # negativeOne
| FLOAT # float
| INT # int
| ID('\'')* # id
| vec # VectorOrDyadic
| ID '['expr (',' expr)* ']' # Indexing
| functionCall # function
| matrix # matrices
| '(' expr ')' # parens
| expr '=' expr # idEqualsExpr
| expr ':' expr # colon
| ID? ranges ('\'')* # rangess
;
// These are to take care of the case insensitivity of Autolev.
Mass: ('M'|'m')('A'|'a')('S'|'s')('S'|'s');
Inertia: ('I'|'i')('N'|'n')('E'|'e')('R'|'r')('T'|'t')('I'|'i')('A'|'a');
Input: ('I'|'i')('N'|'n')('P'|'p')('U'|'u')('T'|'t')('S'|'s')?;
Output: ('O'|'o')('U'|'u')('T'|'t')('P'|'p')('U'|'u')('T'|'t');
Save: ('S'|'s')('A'|'a')('V'|'v')('E'|'e');
UnitSystem: ('U'|'u')('N'|'n')('I'|'i')('T'|'t')('S'|'s')('Y'|'y')('S'|'s')('T'|'t')('E'|'e')('M'|'m');
Encode: ('E'|'e')('N'|'n')('C'|'c')('O'|'o')('D'|'d')('E'|'e');
Newtonian: ('N'|'n')('E'|'e')('W'|'w')('T'|'t')('O'|'o')('N'|'n')('I'|'i')('A'|'a')('N'|'n');
Frames: ('F'|'f')('R'|'r')('A'|'a')('M'|'m')('E'|'e')('S'|'s')?;
Bodies: ('B'|'b')('O'|'o')('D'|'d')('I'|'i')('E'|'e')('S'|'s')?;
Particles: ('P'|'p')('A'|'a')('R'|'r')('T'|'t')('I'|'i')('C'|'c')('L'|'l')('E'|'e')('S'|'s')?;
Points: ('P'|'p')('O'|'o')('I'|'i')('N'|'n')('T'|'t')('S'|'s')?;
Constants: ('C'|'c')('O'|'o')('N'|'n')('S'|'s')('T'|'t')('A'|'a')('N'|'n')('T'|'t')('S'|'s')?;
Specifieds: ('S'|'s')('P'|'p')('E'|'e')('C'|'c')('I'|'i')('F'|'f')('I'|'i')('E'|'e')('D'|'d')('S'|'s')?;
Imaginary: ('I'|'i')('M'|'m')('A'|'a')('G'|'g')('I'|'i')('N'|'n')('A'|'a')('R'|'r')('Y'|'y');
Variables: ('V'|'v')('A'|'a')('R'|'r')('I'|'i')('A'|'a')('B'|'b')('L'|'l')('E'|'e')('S'|'s')?;
MotionVariables: ('M'|'m')('O'|'o')('T'|'t')('I'|'i')('O'|'o')('N'|'n')('V'|'v')('A'|'a')('R'|'r')('I'|'i')('A'|'a')('B'|'b')('L'|'l')('E'|'e')('S'|'s')?;
fragment DIFF: ('\'')*;
fragment DIGIT: [0-9];
INT: [0-9]+ ; // match integers
FLOAT: DIGIT+ '.' DIGIT*
| '.' DIGIT+;
EXP: FLOAT 'E' INT
| FLOAT 'E' '-' INT;
LINE_COMMENT : '%' .*? '\r'? '\n' -> skip ;
ID: [a-zA-Z][a-zA-Z0-9_]*;
WS: [ \t\r\n&]+ -> skip ; // toss out whitespace

View File

@@ -0,0 +1,97 @@
from sympy.external import import_module
from sympy.utilities.decorator import doctest_depends_on
@doctest_depends_on(modules=('antlr4',))
def parse_autolev(autolev_code, include_numeric=False):
"""Parses Autolev code (version 4.1) to SymPy code.
Parameters
=========
autolev_code : Can be an str or any object with a readlines() method (such as a file handle or StringIO).
include_numeric : boolean, optional
If True NumPy, PyDy, or other numeric code is included for numeric evaluation lines in the Autolev code.
Returns
=======
sympy_code : str
Equivalent SymPy and/or numpy/pydy code as the input code.
Example (Double Pendulum)
=========================
>>> my_al_text = ("MOTIONVARIABLES' Q{2}', U{2}'",
... "CONSTANTS L,M,G",
... "NEWTONIAN N",
... "FRAMES A,B",
... "SIMPROT(N, A, 3, Q1)",
... "SIMPROT(N, B, 3, Q2)",
... "W_A_N>=U1*N3>",
... "W_B_N>=U2*N3>",
... "POINT O",
... "PARTICLES P,R",
... "P_O_P> = L*A1>",
... "P_P_R> = L*B1>",
... "V_O_N> = 0>",
... "V2PTS(N, A, O, P)",
... "V2PTS(N, B, P, R)",
... "MASS P=M, R=M",
... "Q1' = U1",
... "Q2' = U2",
... "GRAVITY(G*N1>)",
... "ZERO = FR() + FRSTAR()",
... "KANE()",
... "INPUT M=1,G=9.81,L=1",
... "INPUT Q1=.1,Q2=.2,U1=0,U2=0",
... "INPUT TFINAL=10, INTEGSTP=.01",
... "CODE DYNAMICS() some_filename.c")
>>> my_al_text = '\\n'.join(my_al_text)
>>> from sympy.parsing.autolev import parse_autolev
>>> print(parse_autolev(my_al_text, include_numeric=True))
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
<BLANKLINE>
q1, q2, u1, u2 = _me.dynamicsymbols('q1 q2 u1 u2')
q1_d, q2_d, u1_d, u2_d = _me.dynamicsymbols('q1_ q2_ u1_ u2_', 1)
l, m, g = _sm.symbols('l m g', real=True)
frame_n = _me.ReferenceFrame('n')
frame_a = _me.ReferenceFrame('a')
frame_b = _me.ReferenceFrame('b')
frame_a.orient(frame_n, 'Axis', [q1, frame_n.z])
frame_b.orient(frame_n, 'Axis', [q2, frame_n.z])
frame_a.set_ang_vel(frame_n, u1*frame_n.z)
frame_b.set_ang_vel(frame_n, u2*frame_n.z)
point_o = _me.Point('o')
particle_p = _me.Particle('p', _me.Point('p_pt'), _sm.Symbol('m'))
particle_r = _me.Particle('r', _me.Point('r_pt'), _sm.Symbol('m'))
particle_p.point.set_pos(point_o, l*frame_a.x)
particle_r.point.set_pos(particle_p.point, l*frame_b.x)
point_o.set_vel(frame_n, 0)
particle_p.point.v2pt_theory(point_o,frame_n,frame_a)
particle_r.point.v2pt_theory(particle_p.point,frame_n,frame_b)
particle_p.mass = m
particle_r.mass = m
force_p = particle_p.mass*(g*frame_n.x)
force_r = particle_r.mass*(g*frame_n.x)
kd_eqs = [q1_d - u1, q2_d - u2]
forceList = [(particle_p.point,particle_p.mass*(g*frame_n.x)), (particle_r.point,particle_r.mass*(g*frame_n.x))]
kane = _me.KanesMethod(frame_n, q_ind=[q1,q2], u_ind=[u1, u2], kd_eqs = kd_eqs)
fr, frstar = kane.kanes_equations([particle_p, particle_r], forceList)
zero = fr+frstar
from pydy.system import System
sys = System(kane, constants = {l:1, m:1, g:9.81},
specifieds={},
initial_conditions={q1:.1, q2:.2, u1:0, u2:0},
times = _np.linspace(0.0, 10, 10/.01))
<BLANKLINE>
y=sys.integrate()
<BLANKLINE>
"""
_autolev = import_module(
'sympy.parsing.autolev._parse_autolev_antlr',
import_kwargs={'fromlist': ['X']})
if _autolev is not None:
return _autolev.parse_autolev(autolev_code, include_numeric)

View File

@@ -0,0 +1,5 @@
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
#
# Generated with antlr4
# antlr4 is licensed under the BSD-3-Clause License
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt

View File

@@ -0,0 +1,253 @@
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
#
# Generated with antlr4
# antlr4 is licensed under the BSD-3-Clause License
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt
from antlr4 import *
from io import StringIO
import sys
if sys.version_info[1] > 5:
from typing import TextIO
else:
from typing.io import TextIO
def serializedATN():
return [
4,0,49,393,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,
2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,
13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,
19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,
26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,
32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,2,
39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7,
45,2,46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,1,0,1,0,1,1,
1,1,1,2,1,2,1,3,1,3,1,3,1,4,1,4,1,4,1,5,1,5,1,5,1,6,1,6,1,6,1,7,
1,7,1,7,1,8,1,8,1,8,1,9,1,9,1,10,1,10,1,11,1,11,1,12,1,12,1,13,1,
13,1,14,1,14,1,15,1,15,1,16,1,16,1,17,1,17,1,18,1,18,1,19,1,19,1,
20,1,20,1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,23,1,23,1,24,1,24,1,
25,1,25,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,
27,1,27,1,28,1,28,1,28,1,28,1,28,1,28,3,28,184,8,28,1,29,1,29,1,
29,1,29,1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,31,1,31,1,31,1,
31,1,31,1,31,1,31,1,31,1,31,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,
32,1,32,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,34,1,
34,1,34,1,34,1,34,1,34,3,34,232,8,34,1,35,1,35,1,35,1,35,1,35,1,
35,3,35,240,8,35,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,36,3,
36,251,8,36,1,37,1,37,1,37,1,37,1,37,1,37,3,37,259,8,37,1,38,1,38,
1,38,1,38,1,38,1,38,1,38,1,38,1,38,3,38,270,8,38,1,39,1,39,1,39,
1,39,1,39,1,39,1,39,1,39,1,39,1,39,3,39,282,8,39,1,40,1,40,1,40,
1,40,1,40,1,40,1,40,1,40,1,40,1,40,1,41,1,41,1,41,1,41,1,41,1,41,
1,41,1,41,1,41,3,41,303,8,41,1,42,1,42,1,42,1,42,1,42,1,42,1,42,
1,42,1,42,1,42,1,42,1,42,1,42,1,42,1,42,3,42,320,8,42,1,43,5,43,
323,8,43,10,43,12,43,326,9,43,1,44,1,44,1,45,4,45,331,8,45,11,45,
12,45,332,1,46,4,46,336,8,46,11,46,12,46,337,1,46,1,46,5,46,342,
8,46,10,46,12,46,345,9,46,1,46,1,46,4,46,349,8,46,11,46,12,46,350,
3,46,353,8,46,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,3,47,
364,8,47,1,48,1,48,5,48,368,8,48,10,48,12,48,371,9,48,1,48,3,48,
374,8,48,1,48,1,48,1,48,1,48,1,49,1,49,5,49,382,8,49,10,49,12,49,
385,9,49,1,50,4,50,388,8,50,11,50,12,50,389,1,50,1,50,1,369,0,51,
1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,
27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,
49,25,51,26,53,27,55,28,57,29,59,30,61,31,63,32,65,33,67,34,69,35,
71,36,73,37,75,38,77,39,79,40,81,41,83,42,85,43,87,0,89,0,91,44,
93,45,95,46,97,47,99,48,101,49,1,0,24,2,0,77,77,109,109,2,0,65,65,
97,97,2,0,83,83,115,115,2,0,73,73,105,105,2,0,78,78,110,110,2,0,
69,69,101,101,2,0,82,82,114,114,2,0,84,84,116,116,2,0,80,80,112,
112,2,0,85,85,117,117,2,0,79,79,111,111,2,0,86,86,118,118,2,0,89,
89,121,121,2,0,67,67,99,99,2,0,68,68,100,100,2,0,87,87,119,119,2,
0,70,70,102,102,2,0,66,66,98,98,2,0,76,76,108,108,2,0,71,71,103,
103,1,0,48,57,2,0,65,90,97,122,4,0,48,57,65,90,95,95,97,122,4,0,
9,10,13,13,32,32,38,38,410,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,0,
7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,17,
1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,27,
1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,
1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,
1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,
1,0,0,0,0,59,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,
1,0,0,0,0,69,1,0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,
1,0,0,0,0,79,1,0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,91,
1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0,0,101,
1,0,0,0,1,103,1,0,0,0,3,105,1,0,0,0,5,107,1,0,0,0,7,109,1,0,0,0,
9,112,1,0,0,0,11,115,1,0,0,0,13,118,1,0,0,0,15,121,1,0,0,0,17,124,
1,0,0,0,19,127,1,0,0,0,21,129,1,0,0,0,23,131,1,0,0,0,25,133,1,0,
0,0,27,135,1,0,0,0,29,137,1,0,0,0,31,139,1,0,0,0,33,141,1,0,0,0,
35,143,1,0,0,0,37,145,1,0,0,0,39,147,1,0,0,0,41,149,1,0,0,0,43,151,
1,0,0,0,45,154,1,0,0,0,47,158,1,0,0,0,49,160,1,0,0,0,51,162,1,0,
0,0,53,164,1,0,0,0,55,169,1,0,0,0,57,177,1,0,0,0,59,185,1,0,0,0,
61,192,1,0,0,0,63,197,1,0,0,0,65,208,1,0,0,0,67,215,1,0,0,0,69,225,
1,0,0,0,71,233,1,0,0,0,73,241,1,0,0,0,75,252,1,0,0,0,77,260,1,0,
0,0,79,271,1,0,0,0,81,283,1,0,0,0,83,293,1,0,0,0,85,304,1,0,0,0,
87,324,1,0,0,0,89,327,1,0,0,0,91,330,1,0,0,0,93,352,1,0,0,0,95,363,
1,0,0,0,97,365,1,0,0,0,99,379,1,0,0,0,101,387,1,0,0,0,103,104,5,
91,0,0,104,2,1,0,0,0,105,106,5,93,0,0,106,4,1,0,0,0,107,108,5,61,
0,0,108,6,1,0,0,0,109,110,5,43,0,0,110,111,5,61,0,0,111,8,1,0,0,
0,112,113,5,45,0,0,113,114,5,61,0,0,114,10,1,0,0,0,115,116,5,58,
0,0,116,117,5,61,0,0,117,12,1,0,0,0,118,119,5,42,0,0,119,120,5,61,
0,0,120,14,1,0,0,0,121,122,5,47,0,0,122,123,5,61,0,0,123,16,1,0,
0,0,124,125,5,94,0,0,125,126,5,61,0,0,126,18,1,0,0,0,127,128,5,44,
0,0,128,20,1,0,0,0,129,130,5,39,0,0,130,22,1,0,0,0,131,132,5,40,
0,0,132,24,1,0,0,0,133,134,5,41,0,0,134,26,1,0,0,0,135,136,5,123,
0,0,136,28,1,0,0,0,137,138,5,125,0,0,138,30,1,0,0,0,139,140,5,58,
0,0,140,32,1,0,0,0,141,142,5,43,0,0,142,34,1,0,0,0,143,144,5,45,
0,0,144,36,1,0,0,0,145,146,5,59,0,0,146,38,1,0,0,0,147,148,5,46,
0,0,148,40,1,0,0,0,149,150,5,62,0,0,150,42,1,0,0,0,151,152,5,48,
0,0,152,153,5,62,0,0,153,44,1,0,0,0,154,155,5,49,0,0,155,156,5,62,
0,0,156,157,5,62,0,0,157,46,1,0,0,0,158,159,5,94,0,0,159,48,1,0,
0,0,160,161,5,42,0,0,161,50,1,0,0,0,162,163,5,47,0,0,163,52,1,0,
0,0,164,165,7,0,0,0,165,166,7,1,0,0,166,167,7,2,0,0,167,168,7,2,
0,0,168,54,1,0,0,0,169,170,7,3,0,0,170,171,7,4,0,0,171,172,7,5,0,
0,172,173,7,6,0,0,173,174,7,7,0,0,174,175,7,3,0,0,175,176,7,1,0,
0,176,56,1,0,0,0,177,178,7,3,0,0,178,179,7,4,0,0,179,180,7,8,0,0,
180,181,7,9,0,0,181,183,7,7,0,0,182,184,7,2,0,0,183,182,1,0,0,0,
183,184,1,0,0,0,184,58,1,0,0,0,185,186,7,10,0,0,186,187,7,9,0,0,
187,188,7,7,0,0,188,189,7,8,0,0,189,190,7,9,0,0,190,191,7,7,0,0,
191,60,1,0,0,0,192,193,7,2,0,0,193,194,7,1,0,0,194,195,7,11,0,0,
195,196,7,5,0,0,196,62,1,0,0,0,197,198,7,9,0,0,198,199,7,4,0,0,199,
200,7,3,0,0,200,201,7,7,0,0,201,202,7,2,0,0,202,203,7,12,0,0,203,
204,7,2,0,0,204,205,7,7,0,0,205,206,7,5,0,0,206,207,7,0,0,0,207,
64,1,0,0,0,208,209,7,5,0,0,209,210,7,4,0,0,210,211,7,13,0,0,211,
212,7,10,0,0,212,213,7,14,0,0,213,214,7,5,0,0,214,66,1,0,0,0,215,
216,7,4,0,0,216,217,7,5,0,0,217,218,7,15,0,0,218,219,7,7,0,0,219,
220,7,10,0,0,220,221,7,4,0,0,221,222,7,3,0,0,222,223,7,1,0,0,223,
224,7,4,0,0,224,68,1,0,0,0,225,226,7,16,0,0,226,227,7,6,0,0,227,
228,7,1,0,0,228,229,7,0,0,0,229,231,7,5,0,0,230,232,7,2,0,0,231,
230,1,0,0,0,231,232,1,0,0,0,232,70,1,0,0,0,233,234,7,17,0,0,234,
235,7,10,0,0,235,236,7,14,0,0,236,237,7,3,0,0,237,239,7,5,0,0,238,
240,7,2,0,0,239,238,1,0,0,0,239,240,1,0,0,0,240,72,1,0,0,0,241,242,
7,8,0,0,242,243,7,1,0,0,243,244,7,6,0,0,244,245,7,7,0,0,245,246,
7,3,0,0,246,247,7,13,0,0,247,248,7,18,0,0,248,250,7,5,0,0,249,251,
7,2,0,0,250,249,1,0,0,0,250,251,1,0,0,0,251,74,1,0,0,0,252,253,7,
8,0,0,253,254,7,10,0,0,254,255,7,3,0,0,255,256,7,4,0,0,256,258,7,
7,0,0,257,259,7,2,0,0,258,257,1,0,0,0,258,259,1,0,0,0,259,76,1,0,
0,0,260,261,7,13,0,0,261,262,7,10,0,0,262,263,7,4,0,0,263,264,7,
2,0,0,264,265,7,7,0,0,265,266,7,1,0,0,266,267,7,4,0,0,267,269,7,
7,0,0,268,270,7,2,0,0,269,268,1,0,0,0,269,270,1,0,0,0,270,78,1,0,
0,0,271,272,7,2,0,0,272,273,7,8,0,0,273,274,7,5,0,0,274,275,7,13,
0,0,275,276,7,3,0,0,276,277,7,16,0,0,277,278,7,3,0,0,278,279,7,5,
0,0,279,281,7,14,0,0,280,282,7,2,0,0,281,280,1,0,0,0,281,282,1,0,
0,0,282,80,1,0,0,0,283,284,7,3,0,0,284,285,7,0,0,0,285,286,7,1,0,
0,286,287,7,19,0,0,287,288,7,3,0,0,288,289,7,4,0,0,289,290,7,1,0,
0,290,291,7,6,0,0,291,292,7,12,0,0,292,82,1,0,0,0,293,294,7,11,0,
0,294,295,7,1,0,0,295,296,7,6,0,0,296,297,7,3,0,0,297,298,7,1,0,
0,298,299,7,17,0,0,299,300,7,18,0,0,300,302,7,5,0,0,301,303,7,2,
0,0,302,301,1,0,0,0,302,303,1,0,0,0,303,84,1,0,0,0,304,305,7,0,0,
0,305,306,7,10,0,0,306,307,7,7,0,0,307,308,7,3,0,0,308,309,7,10,
0,0,309,310,7,4,0,0,310,311,7,11,0,0,311,312,7,1,0,0,312,313,7,6,
0,0,313,314,7,3,0,0,314,315,7,1,0,0,315,316,7,17,0,0,316,317,7,18,
0,0,317,319,7,5,0,0,318,320,7,2,0,0,319,318,1,0,0,0,319,320,1,0,
0,0,320,86,1,0,0,0,321,323,5,39,0,0,322,321,1,0,0,0,323,326,1,0,
0,0,324,322,1,0,0,0,324,325,1,0,0,0,325,88,1,0,0,0,326,324,1,0,0,
0,327,328,7,20,0,0,328,90,1,0,0,0,329,331,7,20,0,0,330,329,1,0,0,
0,331,332,1,0,0,0,332,330,1,0,0,0,332,333,1,0,0,0,333,92,1,0,0,0,
334,336,3,89,44,0,335,334,1,0,0,0,336,337,1,0,0,0,337,335,1,0,0,
0,337,338,1,0,0,0,338,339,1,0,0,0,339,343,5,46,0,0,340,342,3,89,
44,0,341,340,1,0,0,0,342,345,1,0,0,0,343,341,1,0,0,0,343,344,1,0,
0,0,344,353,1,0,0,0,345,343,1,0,0,0,346,348,5,46,0,0,347,349,3,89,
44,0,348,347,1,0,0,0,349,350,1,0,0,0,350,348,1,0,0,0,350,351,1,0,
0,0,351,353,1,0,0,0,352,335,1,0,0,0,352,346,1,0,0,0,353,94,1,0,0,
0,354,355,3,93,46,0,355,356,5,69,0,0,356,357,3,91,45,0,357,364,1,
0,0,0,358,359,3,93,46,0,359,360,5,69,0,0,360,361,5,45,0,0,361,362,
3,91,45,0,362,364,1,0,0,0,363,354,1,0,0,0,363,358,1,0,0,0,364,96,
1,0,0,0,365,369,5,37,0,0,366,368,9,0,0,0,367,366,1,0,0,0,368,371,
1,0,0,0,369,370,1,0,0,0,369,367,1,0,0,0,370,373,1,0,0,0,371,369,
1,0,0,0,372,374,5,13,0,0,373,372,1,0,0,0,373,374,1,0,0,0,374,375,
1,0,0,0,375,376,5,10,0,0,376,377,1,0,0,0,377,378,6,48,0,0,378,98,
1,0,0,0,379,383,7,21,0,0,380,382,7,22,0,0,381,380,1,0,0,0,382,385,
1,0,0,0,383,381,1,0,0,0,383,384,1,0,0,0,384,100,1,0,0,0,385,383,
1,0,0,0,386,388,7,23,0,0,387,386,1,0,0,0,388,389,1,0,0,0,389,387,
1,0,0,0,389,390,1,0,0,0,390,391,1,0,0,0,391,392,6,50,0,0,392,102,
1,0,0,0,21,0,183,231,239,250,258,269,281,302,319,324,332,337,343,
350,352,363,369,373,383,389,1,6,0,0
]
class AutolevLexer(Lexer):
atn = ATNDeserializer().deserialize(serializedATN())
decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ]
T__0 = 1
T__1 = 2
T__2 = 3
T__3 = 4
T__4 = 5
T__5 = 6
T__6 = 7
T__7 = 8
T__8 = 9
T__9 = 10
T__10 = 11
T__11 = 12
T__12 = 13
T__13 = 14
T__14 = 15
T__15 = 16
T__16 = 17
T__17 = 18
T__18 = 19
T__19 = 20
T__20 = 21
T__21 = 22
T__22 = 23
T__23 = 24
T__24 = 25
T__25 = 26
Mass = 27
Inertia = 28
Input = 29
Output = 30
Save = 31
UnitSystem = 32
Encode = 33
Newtonian = 34
Frames = 35
Bodies = 36
Particles = 37
Points = 38
Constants = 39
Specifieds = 40
Imaginary = 41
Variables = 42
MotionVariables = 43
INT = 44
FLOAT = 45
EXP = 46
LINE_COMMENT = 47
ID = 48
WS = 49
channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ]
modeNames = [ "DEFAULT_MODE" ]
literalNames = [ "<INVALID>",
"'['", "']'", "'='", "'+='", "'-='", "':='", "'*='", "'/='",
"'^='", "','", "'''", "'('", "')'", "'{'", "'}'", "':'", "'+'",
"'-'", "';'", "'.'", "'>'", "'0>'", "'1>>'", "'^'", "'*'", "'/'" ]
symbolicNames = [ "<INVALID>",
"Mass", "Inertia", "Input", "Output", "Save", "UnitSystem",
"Encode", "Newtonian", "Frames", "Bodies", "Particles", "Points",
"Constants", "Specifieds", "Imaginary", "Variables", "MotionVariables",
"INT", "FLOAT", "EXP", "LINE_COMMENT", "ID", "WS" ]
ruleNames = [ "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6",
"T__7", "T__8", "T__9", "T__10", "T__11", "T__12", "T__13",
"T__14", "T__15", "T__16", "T__17", "T__18", "T__19",
"T__20", "T__21", "T__22", "T__23", "T__24", "T__25",
"Mass", "Inertia", "Input", "Output", "Save", "UnitSystem",
"Encode", "Newtonian", "Frames", "Bodies", "Particles",
"Points", "Constants", "Specifieds", "Imaginary", "Variables",
"MotionVariables", "DIFF", "DIGIT", "INT", "FLOAT", "EXP",
"LINE_COMMENT", "ID", "WS" ]
grammarFileName = "Autolev.g4"
def __init__(self, input=None, output:TextIO = sys.stdout):
super().__init__(input, output)
self.checkVersion("4.11.1")
self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache())
self._actions = None
self._predicates = None

View File

@@ -0,0 +1,421 @@
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
#
# Generated with antlr4
# antlr4 is licensed under the BSD-3-Clause License
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt
from antlr4 import *
if __name__ is not None and "." in __name__:
from .autolevparser import AutolevParser
else:
from autolevparser import AutolevParser
# This class defines a complete listener for a parse tree produced by AutolevParser.
class AutolevListener(ParseTreeListener):
# Enter a parse tree produced by AutolevParser#prog.
def enterProg(self, ctx:AutolevParser.ProgContext):
pass
# Exit a parse tree produced by AutolevParser#prog.
def exitProg(self, ctx:AutolevParser.ProgContext):
pass
# Enter a parse tree produced by AutolevParser#stat.
def enterStat(self, ctx:AutolevParser.StatContext):
pass
# Exit a parse tree produced by AutolevParser#stat.
def exitStat(self, ctx:AutolevParser.StatContext):
pass
# Enter a parse tree produced by AutolevParser#vecAssign.
def enterVecAssign(self, ctx:AutolevParser.VecAssignContext):
pass
# Exit a parse tree produced by AutolevParser#vecAssign.
def exitVecAssign(self, ctx:AutolevParser.VecAssignContext):
pass
# Enter a parse tree produced by AutolevParser#indexAssign.
def enterIndexAssign(self, ctx:AutolevParser.IndexAssignContext):
pass
# Exit a parse tree produced by AutolevParser#indexAssign.
def exitIndexAssign(self, ctx:AutolevParser.IndexAssignContext):
pass
# Enter a parse tree produced by AutolevParser#regularAssign.
def enterRegularAssign(self, ctx:AutolevParser.RegularAssignContext):
pass
# Exit a parse tree produced by AutolevParser#regularAssign.
def exitRegularAssign(self, ctx:AutolevParser.RegularAssignContext):
pass
# Enter a parse tree produced by AutolevParser#equals.
def enterEquals(self, ctx:AutolevParser.EqualsContext):
pass
# Exit a parse tree produced by AutolevParser#equals.
def exitEquals(self, ctx:AutolevParser.EqualsContext):
pass
# Enter a parse tree produced by AutolevParser#index.
def enterIndex(self, ctx:AutolevParser.IndexContext):
pass
# Exit a parse tree produced by AutolevParser#index.
def exitIndex(self, ctx:AutolevParser.IndexContext):
pass
# Enter a parse tree produced by AutolevParser#diff.
def enterDiff(self, ctx:AutolevParser.DiffContext):
pass
# Exit a parse tree produced by AutolevParser#diff.
def exitDiff(self, ctx:AutolevParser.DiffContext):
pass
# Enter a parse tree produced by AutolevParser#functionCall.
def enterFunctionCall(self, ctx:AutolevParser.FunctionCallContext):
pass
# Exit a parse tree produced by AutolevParser#functionCall.
def exitFunctionCall(self, ctx:AutolevParser.FunctionCallContext):
pass
# Enter a parse tree produced by AutolevParser#varDecl.
def enterVarDecl(self, ctx:AutolevParser.VarDeclContext):
pass
# Exit a parse tree produced by AutolevParser#varDecl.
def exitVarDecl(self, ctx:AutolevParser.VarDeclContext):
pass
# Enter a parse tree produced by AutolevParser#varType.
def enterVarType(self, ctx:AutolevParser.VarTypeContext):
pass
# Exit a parse tree produced by AutolevParser#varType.
def exitVarType(self, ctx:AutolevParser.VarTypeContext):
pass
# Enter a parse tree produced by AutolevParser#varDecl2.
def enterVarDecl2(self, ctx:AutolevParser.VarDecl2Context):
pass
# Exit a parse tree produced by AutolevParser#varDecl2.
def exitVarDecl2(self, ctx:AutolevParser.VarDecl2Context):
pass
# Enter a parse tree produced by AutolevParser#ranges.
def enterRanges(self, ctx:AutolevParser.RangesContext):
pass
# Exit a parse tree produced by AutolevParser#ranges.
def exitRanges(self, ctx:AutolevParser.RangesContext):
pass
# Enter a parse tree produced by AutolevParser#massDecl.
def enterMassDecl(self, ctx:AutolevParser.MassDeclContext):
pass
# Exit a parse tree produced by AutolevParser#massDecl.
def exitMassDecl(self, ctx:AutolevParser.MassDeclContext):
pass
# Enter a parse tree produced by AutolevParser#massDecl2.
def enterMassDecl2(self, ctx:AutolevParser.MassDecl2Context):
pass
# Exit a parse tree produced by AutolevParser#massDecl2.
def exitMassDecl2(self, ctx:AutolevParser.MassDecl2Context):
pass
# Enter a parse tree produced by AutolevParser#inertiaDecl.
def enterInertiaDecl(self, ctx:AutolevParser.InertiaDeclContext):
pass
# Exit a parse tree produced by AutolevParser#inertiaDecl.
def exitInertiaDecl(self, ctx:AutolevParser.InertiaDeclContext):
pass
# Enter a parse tree produced by AutolevParser#matrix.
def enterMatrix(self, ctx:AutolevParser.MatrixContext):
pass
# Exit a parse tree produced by AutolevParser#matrix.
def exitMatrix(self, ctx:AutolevParser.MatrixContext):
pass
# Enter a parse tree produced by AutolevParser#matrixInOutput.
def enterMatrixInOutput(self, ctx:AutolevParser.MatrixInOutputContext):
pass
# Exit a parse tree produced by AutolevParser#matrixInOutput.
def exitMatrixInOutput(self, ctx:AutolevParser.MatrixInOutputContext):
pass
# Enter a parse tree produced by AutolevParser#codeCommands.
def enterCodeCommands(self, ctx:AutolevParser.CodeCommandsContext):
pass
# Exit a parse tree produced by AutolevParser#codeCommands.
def exitCodeCommands(self, ctx:AutolevParser.CodeCommandsContext):
pass
# Enter a parse tree produced by AutolevParser#settings.
def enterSettings(self, ctx:AutolevParser.SettingsContext):
pass
# Exit a parse tree produced by AutolevParser#settings.
def exitSettings(self, ctx:AutolevParser.SettingsContext):
pass
# Enter a parse tree produced by AutolevParser#units.
def enterUnits(self, ctx:AutolevParser.UnitsContext):
pass
# Exit a parse tree produced by AutolevParser#units.
def exitUnits(self, ctx:AutolevParser.UnitsContext):
pass
# Enter a parse tree produced by AutolevParser#inputs.
def enterInputs(self, ctx:AutolevParser.InputsContext):
pass
# Exit a parse tree produced by AutolevParser#inputs.
def exitInputs(self, ctx:AutolevParser.InputsContext):
pass
# Enter a parse tree produced by AutolevParser#id_diff.
def enterId_diff(self, ctx:AutolevParser.Id_diffContext):
pass
# Exit a parse tree produced by AutolevParser#id_diff.
def exitId_diff(self, ctx:AutolevParser.Id_diffContext):
pass
# Enter a parse tree produced by AutolevParser#inputs2.
def enterInputs2(self, ctx:AutolevParser.Inputs2Context):
pass
# Exit a parse tree produced by AutolevParser#inputs2.
def exitInputs2(self, ctx:AutolevParser.Inputs2Context):
pass
# Enter a parse tree produced by AutolevParser#outputs.
def enterOutputs(self, ctx:AutolevParser.OutputsContext):
pass
# Exit a parse tree produced by AutolevParser#outputs.
def exitOutputs(self, ctx:AutolevParser.OutputsContext):
pass
# Enter a parse tree produced by AutolevParser#outputs2.
def enterOutputs2(self, ctx:AutolevParser.Outputs2Context):
pass
# Exit a parse tree produced by AutolevParser#outputs2.
def exitOutputs2(self, ctx:AutolevParser.Outputs2Context):
pass
# Enter a parse tree produced by AutolevParser#codegen.
def enterCodegen(self, ctx:AutolevParser.CodegenContext):
pass
# Exit a parse tree produced by AutolevParser#codegen.
def exitCodegen(self, ctx:AutolevParser.CodegenContext):
pass
# Enter a parse tree produced by AutolevParser#commands.
def enterCommands(self, ctx:AutolevParser.CommandsContext):
pass
# Exit a parse tree produced by AutolevParser#commands.
def exitCommands(self, ctx:AutolevParser.CommandsContext):
pass
# Enter a parse tree produced by AutolevParser#vec.
def enterVec(self, ctx:AutolevParser.VecContext):
pass
# Exit a parse tree produced by AutolevParser#vec.
def exitVec(self, ctx:AutolevParser.VecContext):
pass
# Enter a parse tree produced by AutolevParser#parens.
def enterParens(self, ctx:AutolevParser.ParensContext):
pass
# Exit a parse tree produced by AutolevParser#parens.
def exitParens(self, ctx:AutolevParser.ParensContext):
pass
# Enter a parse tree produced by AutolevParser#VectorOrDyadic.
def enterVectorOrDyadic(self, ctx:AutolevParser.VectorOrDyadicContext):
pass
# Exit a parse tree produced by AutolevParser#VectorOrDyadic.
def exitVectorOrDyadic(self, ctx:AutolevParser.VectorOrDyadicContext):
pass
# Enter a parse tree produced by AutolevParser#Exponent.
def enterExponent(self, ctx:AutolevParser.ExponentContext):
pass
# Exit a parse tree produced by AutolevParser#Exponent.
def exitExponent(self, ctx:AutolevParser.ExponentContext):
pass
# Enter a parse tree produced by AutolevParser#MulDiv.
def enterMulDiv(self, ctx:AutolevParser.MulDivContext):
pass
# Exit a parse tree produced by AutolevParser#MulDiv.
def exitMulDiv(self, ctx:AutolevParser.MulDivContext):
pass
# Enter a parse tree produced by AutolevParser#AddSub.
def enterAddSub(self, ctx:AutolevParser.AddSubContext):
pass
# Exit a parse tree produced by AutolevParser#AddSub.
def exitAddSub(self, ctx:AutolevParser.AddSubContext):
pass
# Enter a parse tree produced by AutolevParser#float.
def enterFloat(self, ctx:AutolevParser.FloatContext):
pass
# Exit a parse tree produced by AutolevParser#float.
def exitFloat(self, ctx:AutolevParser.FloatContext):
pass
# Enter a parse tree produced by AutolevParser#int.
def enterInt(self, ctx:AutolevParser.IntContext):
pass
# Exit a parse tree produced by AutolevParser#int.
def exitInt(self, ctx:AutolevParser.IntContext):
pass
# Enter a parse tree produced by AutolevParser#idEqualsExpr.
def enterIdEqualsExpr(self, ctx:AutolevParser.IdEqualsExprContext):
pass
# Exit a parse tree produced by AutolevParser#idEqualsExpr.
def exitIdEqualsExpr(self, ctx:AutolevParser.IdEqualsExprContext):
pass
# Enter a parse tree produced by AutolevParser#negativeOne.
def enterNegativeOne(self, ctx:AutolevParser.NegativeOneContext):
pass
# Exit a parse tree produced by AutolevParser#negativeOne.
def exitNegativeOne(self, ctx:AutolevParser.NegativeOneContext):
pass
# Enter a parse tree produced by AutolevParser#function.
def enterFunction(self, ctx:AutolevParser.FunctionContext):
pass
# Exit a parse tree produced by AutolevParser#function.
def exitFunction(self, ctx:AutolevParser.FunctionContext):
pass
# Enter a parse tree produced by AutolevParser#rangess.
def enterRangess(self, ctx:AutolevParser.RangessContext):
pass
# Exit a parse tree produced by AutolevParser#rangess.
def exitRangess(self, ctx:AutolevParser.RangessContext):
pass
# Enter a parse tree produced by AutolevParser#colon.
def enterColon(self, ctx:AutolevParser.ColonContext):
pass
# Exit a parse tree produced by AutolevParser#colon.
def exitColon(self, ctx:AutolevParser.ColonContext):
pass
# Enter a parse tree produced by AutolevParser#id.
def enterId(self, ctx:AutolevParser.IdContext):
pass
# Exit a parse tree produced by AutolevParser#id.
def exitId(self, ctx:AutolevParser.IdContext):
pass
# Enter a parse tree produced by AutolevParser#exp.
def enterExp(self, ctx:AutolevParser.ExpContext):
pass
# Exit a parse tree produced by AutolevParser#exp.
def exitExp(self, ctx:AutolevParser.ExpContext):
pass
# Enter a parse tree produced by AutolevParser#matrices.
def enterMatrices(self, ctx:AutolevParser.MatricesContext):
pass
# Exit a parse tree produced by AutolevParser#matrices.
def exitMatrices(self, ctx:AutolevParser.MatricesContext):
pass
# Enter a parse tree produced by AutolevParser#Indexing.
def enterIndexing(self, ctx:AutolevParser.IndexingContext):
pass
# Exit a parse tree produced by AutolevParser#Indexing.
def exitIndexing(self, ctx:AutolevParser.IndexingContext):
pass
del AutolevParser

View File

@@ -0,0 +1,86 @@
import os
import subprocess
import glob
from sympy.utilities.misc import debug
here = os.path.dirname(__file__)
grammar_file = os.path.abspath(os.path.join(here, "Autolev.g4"))
dir_autolev_antlr = os.path.join(here, "_antlr")
header = '''\
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
#
# Generated with antlr4
# antlr4 is licensed under the BSD-3-Clause License
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt
'''
def check_antlr_version():
debug("Checking antlr4 version...")
try:
debug(subprocess.check_output(["antlr4"])
.decode('utf-8').split("\n")[0])
return True
except (subprocess.CalledProcessError, FileNotFoundError):
debug("The 'antlr4' command line tool is not installed, "
"or not on your PATH.\n"
"> Please refer to the README.md file for more information.")
return False
def build_parser(output_dir=dir_autolev_antlr):
check_antlr_version()
debug("Updating ANTLR-generated code in {}".format(output_dir))
if not os.path.exists(output_dir):
os.makedirs(output_dir)
with open(os.path.join(output_dir, "__init__.py"), "w+") as fp:
fp.write(header)
args = [
"antlr4",
grammar_file,
"-o", output_dir,
"-no-visitor",
]
debug("Running code generation...\n\t$ {}".format(" ".join(args)))
subprocess.check_output(args, cwd=output_dir)
debug("Applying headers, removing unnecessary files and renaming...")
# Handle case insensitive file systems. If the files are already
# generated, they will be written to autolev* but Autolev*.* won't match them.
for path in (glob.glob(os.path.join(output_dir, "Autolev*.*")) or
glob.glob(os.path.join(output_dir, "autolev*.*"))):
# Remove files ending in .interp or .tokens as they are not needed.
if not path.endswith(".py"):
os.unlink(path)
continue
new_path = os.path.join(output_dir, os.path.basename(path).lower())
with open(path, 'r') as f:
lines = [line.rstrip().replace('AutolevParser import', 'autolevparser import') +'\n'
for line in f]
os.unlink(path)
with open(new_path, "w") as out_file:
offset = 0
while lines[offset].startswith('#'):
offset += 1
out_file.write(header)
out_file.writelines(lines[offset:])
debug("\t{}".format(new_path))
return True
if __name__ == "__main__":
build_parser()

View File

@@ -0,0 +1,38 @@
from importlib.metadata import version
from sympy.external import import_module
autolevparser = import_module('sympy.parsing.autolev._antlr.autolevparser',
import_kwargs={'fromlist': ['AutolevParser']})
autolevlexer = import_module('sympy.parsing.autolev._antlr.autolevlexer',
import_kwargs={'fromlist': ['AutolevLexer']})
autolevlistener = import_module('sympy.parsing.autolev._antlr.autolevlistener',
import_kwargs={'fromlist': ['AutolevListener']})
AutolevParser = getattr(autolevparser, 'AutolevParser', None)
AutolevLexer = getattr(autolevlexer, 'AutolevLexer', None)
AutolevListener = getattr(autolevlistener, 'AutolevListener', None)
def parse_autolev(autolev_code, include_numeric):
antlr4 = import_module('antlr4')
if not antlr4 or not version('antlr4-python3-runtime').startswith('4.11'):
raise ImportError("Autolev parsing requires the antlr4 Python package,"
" provided by pip (antlr4-python3-runtime)"
" conda (antlr-python-runtime), version 4.11")
try:
l = autolev_code.readlines()
input_stream = antlr4.InputStream("".join(l))
except Exception:
input_stream = antlr4.InputStream(autolev_code)
if AutolevListener:
from ._listener_autolev_antlr import MyListener
lexer = AutolevLexer(input_stream)
token_stream = antlr4.CommonTokenStream(lexer)
parser = AutolevParser(token_stream)
tree = parser.prog()
my_listener = MyListener(include_numeric)
walker = antlr4.ParseTreeWalker()
walker.walk(my_listener, tree)
return "".join(my_listener.output_code)

View File

@@ -0,0 +1,9 @@
# parsing/tests/test_autolev.py uses the .al files in this directory as inputs and checks
# the equivalence of the parser generated codes and the respective .py files.
# By default, this directory contains tests for all rules of the parser.
# Additional tests consisting of full physics examples shall be made available soon in
# the form of another repository. One shall be able to copy the contents of that repo
# to this folder and use those tests after uncommenting the respective code in
# parsing/tests/test_autolev.py.

View File

@@ -0,0 +1,33 @@
CONSTANTS G,LB,W,H
MOTIONVARIABLES' THETA'',PHI'',OMEGA',ALPHA'
NEWTONIAN N
BODIES A,B
SIMPROT(N,A,2,THETA)
SIMPROT(A,B,3,PHI)
POINT O
LA = (LB-H/2)/2
P_O_AO> = LA*A3>
P_O_BO> = LB*A3>
OMEGA = THETA'
ALPHA = PHI'
W_A_N> = OMEGA*N2>
W_B_A> = ALPHA*A3>
V_O_N> = 0>
V2PTS(N, A, O, AO)
V2PTS(N, A, O, BO)
MASS A=MA, B=MB
IAXX = 1/12*MA*(2*LA)^2
IAYY = IAXX
IAZZ = 0
IBXX = 1/12*MB*H^2
IBYY = 1/12*MB*(W^2+H^2)
IBZZ = 1/12*MB*W^2
INERTIA A, IAXX, IAYY, IAZZ
INERTIA B, IBXX, IBYY, IBZZ
GRAVITY(G*N3>)
ZERO = FR() + FRSTAR()
KANE()
INPUT LB=0.2,H=0.1,W=0.2,MA=0.01,MB=0.1,G=9.81
INPUT THETA = 90 DEG, PHI = 0.5 DEG, OMEGA=0, ALPHA=0
INPUT TFINAL=10, INTEGSTP=0.02
CODE DYNAMICS() some_filename.c

View File

@@ -0,0 +1,55 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
g, lb, w, h = _sm.symbols('g lb w h', real=True)
theta, phi, omega, alpha = _me.dynamicsymbols('theta phi omega alpha')
theta_d, phi_d, omega_d, alpha_d = _me.dynamicsymbols('theta_ phi_ omega_ alpha_', 1)
theta_dd, phi_dd = _me.dynamicsymbols('theta_ phi_', 2)
frame_n = _me.ReferenceFrame('n')
body_a_cm = _me.Point('a_cm')
body_a_cm.set_vel(frame_n, 0)
body_a_f = _me.ReferenceFrame('a_f')
body_a = _me.RigidBody('a', body_a_cm, body_a_f, _sm.symbols('m'), (_me.outer(body_a_f.x,body_a_f.x),body_a_cm))
body_b_cm = _me.Point('b_cm')
body_b_cm.set_vel(frame_n, 0)
body_b_f = _me.ReferenceFrame('b_f')
body_b = _me.RigidBody('b', body_b_cm, body_b_f, _sm.symbols('m'), (_me.outer(body_b_f.x,body_b_f.x),body_b_cm))
body_a_f.orient(frame_n, 'Axis', [theta, frame_n.y])
body_b_f.orient(body_a_f, 'Axis', [phi, body_a_f.z])
point_o = _me.Point('o')
la = (lb-h/2)/2
body_a_cm.set_pos(point_o, la*body_a_f.z)
body_b_cm.set_pos(point_o, lb*body_a_f.z)
body_a_f.set_ang_vel(frame_n, omega*frame_n.y)
body_b_f.set_ang_vel(body_a_f, alpha*body_a_f.z)
point_o.set_vel(frame_n, 0)
body_a_cm.v2pt_theory(point_o,frame_n,body_a_f)
body_b_cm.v2pt_theory(point_o,frame_n,body_a_f)
ma = _sm.symbols('ma')
body_a.mass = ma
mb = _sm.symbols('mb')
body_b.mass = mb
iaxx = 1/12*ma*(2*la)**2
iayy = iaxx
iazz = 0
ibxx = 1/12*mb*h**2
ibyy = 1/12*mb*(w**2+h**2)
ibzz = 1/12*mb*w**2
body_a.inertia = (_me.inertia(body_a_f, iaxx, iayy, iazz, 0, 0, 0), body_a_cm)
body_b.inertia = (_me.inertia(body_b_f, ibxx, ibyy, ibzz, 0, 0, 0), body_b_cm)
force_a = body_a.mass*(g*frame_n.z)
force_b = body_b.mass*(g*frame_n.z)
kd_eqs = [theta_d - omega, phi_d - alpha]
forceList = [(body_a.masscenter,body_a.mass*(g*frame_n.z)), (body_b.masscenter,body_b.mass*(g*frame_n.z))]
kane = _me.KanesMethod(frame_n, q_ind=[theta,phi], u_ind=[omega, alpha], kd_eqs = kd_eqs)
fr, frstar = kane.kanes_equations([body_a, body_b], forceList)
zero = fr+frstar
from pydy.system import System
sys = System(kane, constants = {g:9.81, lb:0.2, w:0.2, h:0.1, ma:0.01, mb:0.1},
specifieds={},
initial_conditions={theta:_np.deg2rad(90), phi:_np.deg2rad(0.5), omega:0, alpha:0},
times = _np.linspace(0.0, 10, 10/0.02))
y=sys.integrate()

View File

@@ -0,0 +1,25 @@
MOTIONVARIABLES' Q{2}', U{2}'
CONSTANTS L,M,G
NEWTONIAN N
FRAMES A,B
SIMPROT(N, A, 3, Q1)
SIMPROT(N, B, 3, Q2)
W_A_N>=U1*N3>
W_B_N>=U2*N3>
POINT O
PARTICLES P,R
P_O_P> = L*A1>
P_P_R> = L*B1>
V_O_N> = 0>
V2PTS(N, A, O, P)
V2PTS(N, B, P, R)
MASS P=M, R=M
Q1' = U1
Q2' = U2
GRAVITY(G*N1>)
ZERO = FR() + FRSTAR()
KANE()
INPUT M=1,G=9.81,L=1
INPUT Q1=.1,Q2=.2,U1=0,U2=0
INPUT TFINAL=10, INTEGSTP=.01
CODE DYNAMICS() some_filename.c

View File

@@ -0,0 +1,39 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
q1, q2, u1, u2 = _me.dynamicsymbols('q1 q2 u1 u2')
q1_d, q2_d, u1_d, u2_d = _me.dynamicsymbols('q1_ q2_ u1_ u2_', 1)
l, m, g = _sm.symbols('l m g', real=True)
frame_n = _me.ReferenceFrame('n')
frame_a = _me.ReferenceFrame('a')
frame_b = _me.ReferenceFrame('b')
frame_a.orient(frame_n, 'Axis', [q1, frame_n.z])
frame_b.orient(frame_n, 'Axis', [q2, frame_n.z])
frame_a.set_ang_vel(frame_n, u1*frame_n.z)
frame_b.set_ang_vel(frame_n, u2*frame_n.z)
point_o = _me.Point('o')
particle_p = _me.Particle('p', _me.Point('p_pt'), _sm.Symbol('m'))
particle_r = _me.Particle('r', _me.Point('r_pt'), _sm.Symbol('m'))
particle_p.point.set_pos(point_o, l*frame_a.x)
particle_r.point.set_pos(particle_p.point, l*frame_b.x)
point_o.set_vel(frame_n, 0)
particle_p.point.v2pt_theory(point_o,frame_n,frame_a)
particle_r.point.v2pt_theory(particle_p.point,frame_n,frame_b)
particle_p.mass = m
particle_r.mass = m
force_p = particle_p.mass*(g*frame_n.x)
force_r = particle_r.mass*(g*frame_n.x)
kd_eqs = [q1_d - u1, q2_d - u2]
forceList = [(particle_p.point,particle_p.mass*(g*frame_n.x)), (particle_r.point,particle_r.mass*(g*frame_n.x))]
kane = _me.KanesMethod(frame_n, q_ind=[q1,q2], u_ind=[u1, u2], kd_eqs = kd_eqs)
fr, frstar = kane.kanes_equations([particle_p, particle_r], forceList)
zero = fr+frstar
from pydy.system import System
sys = System(kane, constants = {l:1, m:1, g:9.81},
specifieds={},
initial_conditions={q1:.1, q2:.2, u1:0, u2:0},
times = _np.linspace(0.0, 10, 10/.01))
y=sys.integrate()

View File

@@ -0,0 +1,19 @@
CONSTANTS M,K,B,G
MOTIONVARIABLES' POSITION',SPEED'
VARIABLES O
FORCE = O*SIN(T)
NEWTONIAN CEILING
POINTS ORIGIN
V_ORIGIN_CEILING> = 0>
PARTICLES BLOCK
P_ORIGIN_BLOCK> = POSITION*CEILING1>
MASS BLOCK=M
V_BLOCK_CEILING>=SPEED*CEILING1>
POSITION' = SPEED
FORCE_MAGNITUDE = M*G-K*POSITION-B*SPEED+FORCE
FORCE_BLOCK>=EXPLICIT(FORCE_MAGNITUDE*CEILING1>)
ZERO = FR() + FRSTAR()
KANE()
INPUT TFINAL=10.0, INTEGSTP=0.01
INPUT M=1.0, K=1.0, B=0.2, G=9.8, POSITION=0.1, SPEED=-1.0, O=2
CODE DYNAMICS() dummy_file.c

View File

@@ -0,0 +1,31 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
m, k, b, g = _sm.symbols('m k b g', real=True)
position, speed = _me.dynamicsymbols('position speed')
position_d, speed_d = _me.dynamicsymbols('position_ speed_', 1)
o = _me.dynamicsymbols('o')
force = o*_sm.sin(_me.dynamicsymbols._t)
frame_ceiling = _me.ReferenceFrame('ceiling')
point_origin = _me.Point('origin')
point_origin.set_vel(frame_ceiling, 0)
particle_block = _me.Particle('block', _me.Point('block_pt'), _sm.Symbol('m'))
particle_block.point.set_pos(point_origin, position*frame_ceiling.x)
particle_block.mass = m
particle_block.point.set_vel(frame_ceiling, speed*frame_ceiling.x)
force_magnitude = m*g-k*position-b*speed+force
force_block = (force_magnitude*frame_ceiling.x).subs({position_d:speed})
kd_eqs = [position_d - speed]
forceList = [(particle_block.point,(force_magnitude*frame_ceiling.x).subs({position_d:speed}))]
kane = _me.KanesMethod(frame_ceiling, q_ind=[position], u_ind=[speed], kd_eqs = kd_eqs)
fr, frstar = kane.kanes_equations([particle_block], forceList)
zero = fr+frstar
from pydy.system import System
sys = System(kane, constants = {m:1.0, k:1.0, b:0.2, g:9.8},
specifieds={_me.dynamicsymbols('t'):lambda x, t: t, o:2},
initial_conditions={position:0.1, speed:-1*1.0},
times = _np.linspace(0.0, 10.0, 10.0/0.01))
y=sys.integrate()

View File

@@ -0,0 +1,20 @@
MOTIONVARIABLES' Q{2}''
CONSTANTS L,M,G
NEWTONIAN N
POINT PN
V_PN_N> = 0>
THETA1 = ATAN(Q2/Q1)
FRAMES A
SIMPROT(N, A, 3, THETA1)
PARTICLES P
P_PN_P> = Q1*N1>+Q2*N2>
MASS P=M
V_P_N>=DT(P_P_PN>, N)
F_V = DOT(EXPRESS(V_P_N>,A), A1>)
GRAVITY(G*N1>)
DEPENDENT[1] = F_V
CONSTRAIN(DEPENDENT[Q1'])
ZERO=FR()+FRSTAR()
F_C = MAG(P_P_PN>)-L
CONFIG[1]=F_C
ZERO[2]=CONFIG[1]

View File

@@ -0,0 +1,36 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
q1, q2 = _me.dynamicsymbols('q1 q2')
q1_d, q2_d = _me.dynamicsymbols('q1_ q2_', 1)
q1_dd, q2_dd = _me.dynamicsymbols('q1_ q2_', 2)
l, m, g = _sm.symbols('l m g', real=True)
frame_n = _me.ReferenceFrame('n')
point_pn = _me.Point('pn')
point_pn.set_vel(frame_n, 0)
theta1 = _sm.atan(q2/q1)
frame_a = _me.ReferenceFrame('a')
frame_a.orient(frame_n, 'Axis', [theta1, frame_n.z])
particle_p = _me.Particle('p', _me.Point('p_pt'), _sm.Symbol('m'))
particle_p.point.set_pos(point_pn, q1*frame_n.x+q2*frame_n.y)
particle_p.mass = m
particle_p.point.set_vel(frame_n, (point_pn.pos_from(particle_p.point)).dt(frame_n))
f_v = _me.dot((particle_p.point.vel(frame_n)).express(frame_a), frame_a.x)
force_p = particle_p.mass*(g*frame_n.x)
dependent = _sm.Matrix([[0]])
dependent[0] = f_v
velocity_constraints = [i for i in dependent]
u_q1_d = _me.dynamicsymbols('u_q1_d')
u_q2_d = _me.dynamicsymbols('u_q2_d')
kd_eqs = [q1_d-u_q1_d, q2_d-u_q2_d]
forceList = [(particle_p.point,particle_p.mass*(g*frame_n.x))]
kane = _me.KanesMethod(frame_n, q_ind=[q1,q2], u_ind=[u_q2_d], u_dependent=[u_q1_d], kd_eqs = kd_eqs, velocity_constraints = velocity_constraints)
fr, frstar = kane.kanes_equations([particle_p], forceList)
zero = fr+frstar
f_c = point_pn.pos_from(particle_p.point).magnitude()-l
config = _sm.Matrix([[0]])
config[0] = f_c
zero = zero.row_insert(zero.shape[0], _sm.Matrix([[0]]))
zero[zero.shape[0]-1] = config[0]

View File

@@ -0,0 +1,8 @@
% ruletest1.al
CONSTANTS F = 3, G = 9.81
CONSTANTS A, B
CONSTANTS S, S1, S2+, S3+, S4-
CONSTANTS K{4}, L{1:3}, P{1:2,1:3}
CONSTANTS C{2,3}
E1 = A*F + S2 - G
E2 = F^2 + K3*K2*G

View File

@@ -0,0 +1,15 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
f = _sm.S(3)
g = _sm.S(9.81)
a, b = _sm.symbols('a b', real=True)
s, s1 = _sm.symbols('s s1', real=True)
s2, s3 = _sm.symbols('s2 s3', real=True, nonnegative=True)
s4 = _sm.symbols('s4', real=True, nonpositive=True)
k1, k2, k3, k4, l1, l2, l3, p11, p12, p13, p21, p22, p23 = _sm.symbols('k1 k2 k3 k4 l1 l2 l3 p11 p12 p13 p21 p22 p23', real=True)
c11, c12, c13, c21, c22, c23 = _sm.symbols('c11 c12 c13 c21 c22 c23', real=True)
e1 = a*f+s2-g
e2 = f**2+k3*k2*g

View File

@@ -0,0 +1,58 @@
% ruletest10.al
VARIABLES X,Y
COMPLEX ON
CONSTANTS A,B
E = A*(B*X+Y)^2
M = [E;E]
EXPAND(E)
EXPAND(M)
FACTOR(E,X)
FACTOR(M,X)
EQN[1] = A*X + B*Y
EQN[2] = 2*A*X - 3*B*Y
SOLVE(EQN, X, Y)
RHS_Y = RHS(Y)
E = (X+Y)^2 + 2*X^2
ARRANGE(E, 2, X)
CONSTANTS A,B,C
M = [A,B;C,0]
M2 = EVALUATE(M,A=1,B=2,C=3)
EIG(M2, EIGVALUE, EIGVEC)
NEWTONIAN N
FRAMES A
SIMPROT(N, A, N1>, X)
DEGREES OFF
SIMPROT(N, A, N1>, PI/2)
CONSTANTS C{3}
V> = C1*A1> + C2*A2> + C3*A3>
POINTS O, P
P_P_O> = C1*A1>
EXPRESS(V>,N)
EXPRESS(P_P_O>,N)
W_A_N> = C3*A3>
ANGVEL(A,N)
V2PTS(N,A,O,P)
PARTICLES P{2}
V2PTS(N,A,P1,P2)
A2PTS(N,A,P1,P)
BODIES B{2}
CONSTANT G
GRAVITY(G*N1>)
VARIABLE Z
V> = X*A1> + Y*A3>
P_P_O> = X*A1> + Y*A2>
X = 2*Z
Y = Z
EXPLICIT(V>)
EXPLICIT(P_P_O>)
FORCE(O/P1, X*Y*A1>)
FORCE(P2, X*Y*A1>)

View File

@@ -0,0 +1,64 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
x, y = _me.dynamicsymbols('x y')
a, b = _sm.symbols('a b', real=True)
e = a*(b*x+y)**2
m = _sm.Matrix([e,e]).reshape(2, 1)
e = e.expand()
m = _sm.Matrix([i.expand() for i in m]).reshape((m).shape[0], (m).shape[1])
e = _sm.factor(e, x)
m = _sm.Matrix([_sm.factor(i,x) for i in m]).reshape((m).shape[0], (m).shape[1])
eqn = _sm.Matrix([[0]])
eqn[0] = a*x+b*y
eqn = eqn.row_insert(eqn.shape[0], _sm.Matrix([[0]]))
eqn[eqn.shape[0]-1] = 2*a*x-3*b*y
print(_sm.solve(eqn,x,y))
rhs_y = _sm.solve(eqn,x,y)[y]
e = (x+y)**2+2*x**2
e.collect(x)
a, b, c = _sm.symbols('a b c', real=True)
m = _sm.Matrix([a,b,c,0]).reshape(2, 2)
m2 = _sm.Matrix([i.subs({a:1,b:2,c:3}) for i in m]).reshape((m).shape[0], (m).shape[1])
eigvalue = _sm.Matrix([i.evalf() for i in (m2).eigenvals().keys()])
eigvec = _sm.Matrix([i[2][0].evalf() for i in (m2).eigenvects()]).reshape(m2.shape[0], m2.shape[1])
frame_n = _me.ReferenceFrame('n')
frame_a = _me.ReferenceFrame('a')
frame_a.orient(frame_n, 'Axis', [x, frame_n.x])
frame_a.orient(frame_n, 'Axis', [_sm.pi/2, frame_n.x])
c1, c2, c3 = _sm.symbols('c1 c2 c3', real=True)
v = c1*frame_a.x+c2*frame_a.y+c3*frame_a.z
point_o = _me.Point('o')
point_p = _me.Point('p')
point_o.set_pos(point_p, c1*frame_a.x)
v = (v).express(frame_n)
point_o.set_pos(point_p, (point_o.pos_from(point_p)).express(frame_n))
frame_a.set_ang_vel(frame_n, c3*frame_a.z)
print(frame_n.ang_vel_in(frame_a))
point_p.v2pt_theory(point_o,frame_n,frame_a)
particle_p1 = _me.Particle('p1', _me.Point('p1_pt'), _sm.Symbol('m'))
particle_p2 = _me.Particle('p2', _me.Point('p2_pt'), _sm.Symbol('m'))
particle_p2.point.v2pt_theory(particle_p1.point,frame_n,frame_a)
point_p.a2pt_theory(particle_p1.point,frame_n,frame_a)
body_b1_cm = _me.Point('b1_cm')
body_b1_cm.set_vel(frame_n, 0)
body_b1_f = _me.ReferenceFrame('b1_f')
body_b1 = _me.RigidBody('b1', body_b1_cm, body_b1_f, _sm.symbols('m'), (_me.outer(body_b1_f.x,body_b1_f.x),body_b1_cm))
body_b2_cm = _me.Point('b2_cm')
body_b2_cm.set_vel(frame_n, 0)
body_b2_f = _me.ReferenceFrame('b2_f')
body_b2 = _me.RigidBody('b2', body_b2_cm, body_b2_f, _sm.symbols('m'), (_me.outer(body_b2_f.x,body_b2_f.x),body_b2_cm))
g = _sm.symbols('g', real=True)
force_p1 = particle_p1.mass*(g*frame_n.x)
force_p2 = particle_p2.mass*(g*frame_n.x)
force_b1 = body_b1.mass*(g*frame_n.x)
force_b2 = body_b2.mass*(g*frame_n.x)
z = _me.dynamicsymbols('z')
v = x*frame_a.x+y*frame_a.z
point_o.set_pos(point_p, x*frame_a.x+y*frame_a.y)
v = (v).subs({x:2*z, y:z})
point_o.set_pos(point_p, (point_o.pos_from(point_p)).subs({x:2*z, y:z}))
force_o = -1*(x*y*frame_a.x)
force_p1 = particle_p1.mass*(g*frame_n.x)+ x*y*frame_a.x

View File

@@ -0,0 +1,6 @@
VARIABLES X, Y
CONSTANTS A{1:2, 1:2}, B{1:2}
EQN[1] = A11*x + A12*y - B1
EQN[2] = A21*x + A22*y - B2
INPUT A11=2, A12=5, A21=3, A22=4, B1=7, B2=6
CODE ALGEBRAIC(EQN, X, Y) some_filename.c

View File

@@ -0,0 +1,14 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
x, y = _me.dynamicsymbols('x y')
a11, a12, a21, a22, b1, b2 = _sm.symbols('a11 a12 a21 a22 b1 b2', real=True)
eqn = _sm.Matrix([[0]])
eqn[0] = a11*x+a12*y-b1
eqn = eqn.row_insert(eqn.shape[0], _sm.Matrix([[0]]))
eqn[eqn.shape[0]-1] = a21*x+a22*y-b2
eqn_list = []
for i in eqn: eqn_list.append(i.subs({a11:2, a12:5, a21:3, a22:4, b1:7, b2:6}))
print(_sm.linsolve(eqn_list, x,y))

View File

@@ -0,0 +1,7 @@
VARIABLES X,Y
CONSTANTS A,B,R
EQN[1] = A*X^3+B*Y^2-R
EQN[2] = A*SIN(X)^2 + B*COS(2*Y) - R^2
INPUT A=2.0, B=3.0, R=1.0
INPUT X = 30 DEG, Y = 3.14
CODE NONLINEAR(EQN,X,Y) some_filename.c

View File

@@ -0,0 +1,14 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
x, y = _me.dynamicsymbols('x y')
a, b, r = _sm.symbols('a b r', real=True)
eqn = _sm.Matrix([[0]])
eqn[0] = a*x**3+b*y**2-r
eqn = eqn.row_insert(eqn.shape[0], _sm.Matrix([[0]]))
eqn[eqn.shape[0]-1] = a*_sm.sin(x)**2+b*_sm.cos(2*y)-r**2
matrix_list = []
for i in eqn:matrix_list.append(i.subs({a:2.0, b:3.0, r:1.0}))
print(_sm.nsolve(matrix_list,(x,y),(_np.deg2rad(30),3.14)))

View File

@@ -0,0 +1,12 @@
% ruletest2.al
VARIABLES X1,X2
SPECIFIED F1 = X1*X2 + 3*X1^2
SPECIFIED F2=X1*T+X2*T^2
VARIABLE X', Y''
MOTIONVARIABLES Q{3}, U{2}
VARIABLES P{2}'
VARIABLE W{3}', R{2}''
VARIABLES C{1:2, 1:2}
VARIABLES D{1,3}
VARIABLES J{1:2}
IMAGINARY N

View File

@@ -0,0 +1,22 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
x1, x2 = _me.dynamicsymbols('x1 x2')
f1 = x1*x2+3*x1**2
f2 = x1*_me.dynamicsymbols._t+x2*_me.dynamicsymbols._t**2
x, y = _me.dynamicsymbols('x y')
x_d, y_d = _me.dynamicsymbols('x_ y_', 1)
y_dd = _me.dynamicsymbols('y_', 2)
q1, q2, q3, u1, u2 = _me.dynamicsymbols('q1 q2 q3 u1 u2')
p1, p2 = _me.dynamicsymbols('p1 p2')
p1_d, p2_d = _me.dynamicsymbols('p1_ p2_', 1)
w1, w2, w3, r1, r2 = _me.dynamicsymbols('w1 w2 w3 r1 r2')
w1_d, w2_d, w3_d, r1_d, r2_d = _me.dynamicsymbols('w1_ w2_ w3_ r1_ r2_', 1)
r1_dd, r2_dd = _me.dynamicsymbols('r1_ r2_', 2)
c11, c12, c21, c22 = _me.dynamicsymbols('c11 c12 c21 c22')
d11, d12, d13 = _me.dynamicsymbols('d11 d12 d13')
j1, j2 = _me.dynamicsymbols('j1 j2')
n = _sm.symbols('n')
n = _sm.I

View File

@@ -0,0 +1,25 @@
% ruletest3.al
FRAMES A, B
NEWTONIAN N
VARIABLES X{3}
CONSTANTS L
V1> = X1*A1> + X2*A2> + X3*A3>
V2> = X1*B1> + X2*B2> + X3*B3>
V3> = X1*N1> + X2*N2> + X3*N3>
V> = V1> + V2> + V3>
POINTS C, D
POINTS PO{3}
PARTICLES L
PARTICLES P{3}
BODIES S
BODIES R{2}
V4> = X1*S1> + X2*S2> + X3*S3>
P_C_SO> = L*N1>

View File

@@ -0,0 +1,37 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
frame_a = _me.ReferenceFrame('a')
frame_b = _me.ReferenceFrame('b')
frame_n = _me.ReferenceFrame('n')
x1, x2, x3 = _me.dynamicsymbols('x1 x2 x3')
l = _sm.symbols('l', real=True)
v1 = x1*frame_a.x+x2*frame_a.y+x3*frame_a.z
v2 = x1*frame_b.x+x2*frame_b.y+x3*frame_b.z
v3 = x1*frame_n.x+x2*frame_n.y+x3*frame_n.z
v = v1+v2+v3
point_c = _me.Point('c')
point_d = _me.Point('d')
point_po1 = _me.Point('po1')
point_po2 = _me.Point('po2')
point_po3 = _me.Point('po3')
particle_l = _me.Particle('l', _me.Point('l_pt'), _sm.Symbol('m'))
particle_p1 = _me.Particle('p1', _me.Point('p1_pt'), _sm.Symbol('m'))
particle_p2 = _me.Particle('p2', _me.Point('p2_pt'), _sm.Symbol('m'))
particle_p3 = _me.Particle('p3', _me.Point('p3_pt'), _sm.Symbol('m'))
body_s_cm = _me.Point('s_cm')
body_s_cm.set_vel(frame_n, 0)
body_s_f = _me.ReferenceFrame('s_f')
body_s = _me.RigidBody('s', body_s_cm, body_s_f, _sm.symbols('m'), (_me.outer(body_s_f.x,body_s_f.x),body_s_cm))
body_r1_cm = _me.Point('r1_cm')
body_r1_cm.set_vel(frame_n, 0)
body_r1_f = _me.ReferenceFrame('r1_f')
body_r1 = _me.RigidBody('r1', body_r1_cm, body_r1_f, _sm.symbols('m'), (_me.outer(body_r1_f.x,body_r1_f.x),body_r1_cm))
body_r2_cm = _me.Point('r2_cm')
body_r2_cm.set_vel(frame_n, 0)
body_r2_f = _me.ReferenceFrame('r2_f')
body_r2 = _me.RigidBody('r2', body_r2_cm, body_r2_f, _sm.symbols('m'), (_me.outer(body_r2_f.x,body_r2_f.x),body_r2_cm))
v4 = x1*body_s_f.x+x2*body_s_f.y+x3*body_s_f.z
body_s_cm.set_pos(point_c, l*frame_n.x)

View File

@@ -0,0 +1,20 @@
% ruletest4.al
FRAMES A, B
MOTIONVARIABLES Q{3}
SIMPROT(A, B, 1, Q3)
DCM = A_B
M = DCM*3 - A_B
VARIABLES R
CIRCLE_AREA = PI*R^2
VARIABLES U, A
VARIABLES X, Y
S = U*T - 1/2*A*T^2
EXPR1 = 2*A*0.5 - 1.25 + 0.25
EXPR2 = -X^2 + Y^2 + 0.25*(X+Y)^2
EXPR3 = 0.5E-10
DYADIC>> = A1>*A1> + A2>*A2> + A3>*A3>

View File

@@ -0,0 +1,20 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
frame_a = _me.ReferenceFrame('a')
frame_b = _me.ReferenceFrame('b')
q1, q2, q3 = _me.dynamicsymbols('q1 q2 q3')
frame_b.orient(frame_a, 'Axis', [q3, frame_a.x])
dcm = frame_a.dcm(frame_b)
m = dcm*3-frame_a.dcm(frame_b)
r = _me.dynamicsymbols('r')
circle_area = _sm.pi*r**2
u, a = _me.dynamicsymbols('u a')
x, y = _me.dynamicsymbols('x y')
s = u*_me.dynamicsymbols._t-1/2*a*_me.dynamicsymbols._t**2
expr1 = 2*a*0.5-1.25+0.25
expr2 = -1*x**2+y**2+0.25*(x+y)**2
expr3 = 0.5*10**(-10)
dyadic = _me.outer(frame_a.x, frame_a.x)+_me.outer(frame_a.y, frame_a.y)+_me.outer(frame_a.z, frame_a.z)

View File

@@ -0,0 +1,32 @@
% ruletest5.al
VARIABLES X', Y'
E1 = (X+Y)^2 + (X-Y)^3
E2 = (X-Y)^2
E3 = X^2 + Y^2 + 2*X*Y
M1 = [E1;E2]
M2 = [(X+Y)^2,(X-Y)^2]
M3 = M1 + [X;Y]
AM = EXPAND(M1)
CM = EXPAND([(X+Y)^2,(X-Y)^2])
EM = EXPAND(M1 + [X;Y])
F = EXPAND(E1)
G = EXPAND(E2)
A = FACTOR(E3, X)
BM = FACTOR(M1, X)
CM = FACTOR(M1 + [X;Y], X)
A = D(E3, X)
B = D(E3, Y)
CM = D(M2, X)
DM = D(M1 + [X;Y], X)
FRAMES A, B
A_B = [1,0,0;1,0,0;1,0,0]
V1> = X*A1> + Y*A2> + X*Y*A3>
E> = D(V1>, X, B)
FM = DT(M1)
GM = DT([(X+Y)^2,(X-Y)^2])
H> = DT(V1>, B)

View File

@@ -0,0 +1,33 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
x, y = _me.dynamicsymbols('x y')
x_d, y_d = _me.dynamicsymbols('x_ y_', 1)
e1 = (x+y)**2+(x-y)**3
e2 = (x-y)**2
e3 = x**2+y**2+2*x*y
m1 = _sm.Matrix([e1,e2]).reshape(2, 1)
m2 = _sm.Matrix([(x+y)**2,(x-y)**2]).reshape(1, 2)
m3 = m1+_sm.Matrix([x,y]).reshape(2, 1)
am = _sm.Matrix([i.expand() for i in m1]).reshape((m1).shape[0], (m1).shape[1])
cm = _sm.Matrix([i.expand() for i in _sm.Matrix([(x+y)**2,(x-y)**2]).reshape(1, 2)]).reshape((_sm.Matrix([(x+y)**2,(x-y)**2]).reshape(1, 2)).shape[0], (_sm.Matrix([(x+y)**2,(x-y)**2]).reshape(1, 2)).shape[1])
em = _sm.Matrix([i.expand() for i in m1+_sm.Matrix([x,y]).reshape(2, 1)]).reshape((m1+_sm.Matrix([x,y]).reshape(2, 1)).shape[0], (m1+_sm.Matrix([x,y]).reshape(2, 1)).shape[1])
f = (e1).expand()
g = (e2).expand()
a = _sm.factor((e3), x)
bm = _sm.Matrix([_sm.factor(i, x) for i in m1]).reshape((m1).shape[0], (m1).shape[1])
cm = _sm.Matrix([_sm.factor(i, x) for i in m1+_sm.Matrix([x,y]).reshape(2, 1)]).reshape((m1+_sm.Matrix([x,y]).reshape(2, 1)).shape[0], (m1+_sm.Matrix([x,y]).reshape(2, 1)).shape[1])
a = (e3).diff(x)
b = (e3).diff(y)
cm = _sm.Matrix([i.diff(x) for i in m2]).reshape((m2).shape[0], (m2).shape[1])
dm = _sm.Matrix([i.diff(x) for i in m1+_sm.Matrix([x,y]).reshape(2, 1)]).reshape((m1+_sm.Matrix([x,y]).reshape(2, 1)).shape[0], (m1+_sm.Matrix([x,y]).reshape(2, 1)).shape[1])
frame_a = _me.ReferenceFrame('a')
frame_b = _me.ReferenceFrame('b')
frame_b.orient(frame_a, 'DCM', _sm.Matrix([1,0,0,1,0,0,1,0,0]).reshape(3, 3))
v1 = x*frame_a.x+y*frame_a.y+x*y*frame_a.z
e = (v1).diff(x, frame_b)
fm = _sm.Matrix([i.diff(_sm.Symbol('t')) for i in m1]).reshape((m1).shape[0], (m1).shape[1])
gm = _sm.Matrix([i.diff(_sm.Symbol('t')) for i in _sm.Matrix([(x+y)**2,(x-y)**2]).reshape(1, 2)]).reshape((_sm.Matrix([(x+y)**2,(x-y)**2]).reshape(1, 2)).shape[0], (_sm.Matrix([(x+y)**2,(x-y)**2]).reshape(1, 2)).shape[1])
h = (v1).dt(frame_b)

View File

@@ -0,0 +1,41 @@
% ruletest6.al
VARIABLES Q{2}
VARIABLES X,Y,Z
Q1 = X^2 + Y^2
Q2 = X-Y
E = Q1 + Q2
A = EXPLICIT(E)
E2 = COS(X)
E3 = COS(X*Y)
A = TAYLOR(E2, 0:2, X=0)
B = TAYLOR(E3, 0:2, X=0, Y=0)
E = EXPAND((X+Y)^2)
A = EVALUATE(E, X=1, Y=Z)
BM = EVALUATE([E;2*E], X=1, Y=Z)
E = Q1 + Q2
A = EVALUATE(E, X=2, Y=Z^2)
CONSTANTS J,K,L
P1 = POLYNOMIAL([J,K,L],X)
P2 = POLYNOMIAL(J*X+K,X,1)
ROOT1 = ROOTS(P1, X, 2)
ROOT2 = ROOTS([1;2;3])
M = [1,2,3,4;5,6,7,8;9,10,11,12;13,14,15,16]
AM = TRANSPOSE(M) + M
BM = EIG(M)
C1 = DIAGMAT(4, 1)
C2 = DIAGMAT(3, 4, 2)
DM = INV(M+C1)
E = DET(M+C1) + TRACE([1,0;0,1])
F = ELEMENT(M, 2, 3)
A = COLS(M)
BM = COLS(M, 1)
CM = COLS(M, 1, 2:4, 3)
DM = ROWS(M, 1)
EM = ROWS(M, 1, 2:4, 3)

View File

@@ -0,0 +1,36 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
q1, q2 = _me.dynamicsymbols('q1 q2')
x, y, z = _me.dynamicsymbols('x y z')
e = q1+q2
a = (e).subs({q1:x**2+y**2, q2:x-y})
e2 = _sm.cos(x)
e3 = _sm.cos(x*y)
a = (e2).series(x, 0, 2).removeO()
b = (e3).series(x, 0, 2).removeO().series(y, 0, 2).removeO()
e = ((x+y)**2).expand()
a = (e).subs({q1:x**2+y**2,q2:x-y}).subs({x:1,y:z})
bm = _sm.Matrix([i.subs({x:1,y:z}) for i in _sm.Matrix([e,2*e]).reshape(2, 1)]).reshape((_sm.Matrix([e,2*e]).reshape(2, 1)).shape[0], (_sm.Matrix([e,2*e]).reshape(2, 1)).shape[1])
e = q1+q2
a = (e).subs({q1:x**2+y**2,q2:x-y}).subs({x:2,y:z**2})
j, k, l = _sm.symbols('j k l', real=True)
p1 = _sm.Poly(_sm.Matrix([j,k,l]).reshape(1, 3), x)
p2 = _sm.Poly(j*x+k, x)
root1 = [i.evalf() for i in _sm.solve(p1, x)]
root2 = [i.evalf() for i in _sm.solve(_sm.Poly(_sm.Matrix([1,2,3]).reshape(3, 1), x),x)]
m = _sm.Matrix([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]).reshape(4, 4)
am = (m).T+m
bm = _sm.Matrix([i.evalf() for i in (m).eigenvals().keys()])
c1 = _sm.diag(1,1,1,1)
c2 = _sm.Matrix([2 if i==j else 0 for i in range(3) for j in range(4)]).reshape(3, 4)
dm = (m+c1)**(-1)
e = (m+c1).det()+(_sm.Matrix([1,0,0,1]).reshape(2, 2)).trace()
f = (m)[1,2]
a = (m).cols
bm = (m).col(0)
cm = _sm.Matrix([(m).T.row(0),(m).T.row(1),(m).T.row(2),(m).T.row(3),(m).T.row(2)])
dm = (m).row(0)
em = _sm.Matrix([(m).row(0),(m).row(1),(m).row(2),(m).row(3),(m).row(2)])

View File

@@ -0,0 +1,39 @@
% ruletest7.al
VARIABLES X', Y'
E = COS(X) + SIN(X) + TAN(X)&
+ COSH(X) + SINH(X) + TANH(X)&
+ ACOS(X) + ASIN(X) + ATAN(X)&
+ LOG(X) + EXP(X) + SQRT(X)&
+ FACTORIAL(X) + CEIL(X) +&
FLOOR(X) + SIGN(X)
E = SQR(X) + LOG10(X)
A = ABS(-1) + INT(1.5) + ROUND(1.9)
E1 = 2*X + 3*Y
E2 = X + Y
AM = COEF([E1;E2], [X,Y])
B = COEF(E1, X)
C = COEF(E2, Y)
D1 = EXCLUDE(E1, X)
D2 = INCLUDE(E1, X)
FM = ARRANGE([E1,E2],2,X)
F = ARRANGE(E1, 2, Y)
G = REPLACE(E1, X=2*X)
GM = REPLACE([E1;E2], X=3)
FRAMES A, B
VARIABLES THETA
SIMPROT(A,B,3,THETA)
V1> = 2*A1> - 3*A2> + A3>
V2> = B1> + B2> + B3>
A = DOT(V1>, V2>)
BM = DOT(V1>, [V2>;2*V2>])
C> = CROSS(V1>,V2>)
D = MAG(2*V1>) + MAG(3*V1>)
DYADIC>> = 3*A1>*A1> + A2>*A2> + 2*A3>*A3>
AM = MATRIX(B, DYADIC>>)
M = [1;2;3]
V> = VECTOR(A, M)

View File

@@ -0,0 +1,35 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
x, y = _me.dynamicsymbols('x y')
x_d, y_d = _me.dynamicsymbols('x_ y_', 1)
e = _sm.cos(x)+_sm.sin(x)+_sm.tan(x)+_sm.cosh(x)+_sm.sinh(x)+_sm.tanh(x)+_sm.acos(x)+_sm.asin(x)+_sm.atan(x)+_sm.log(x)+_sm.exp(x)+_sm.sqrt(x)+_sm.factorial(x)+_sm.ceiling(x)+_sm.floor(x)+_sm.sign(x)
e = (x)**2+_sm.log(x, 10)
a = _sm.Abs(-1*1)+int(1.5)+round(1.9)
e1 = 2*x+3*y
e2 = x+y
am = _sm.Matrix([e1.expand().coeff(x), e1.expand().coeff(y), e2.expand().coeff(x), e2.expand().coeff(y)]).reshape(2, 2)
b = (e1).expand().coeff(x)
c = (e2).expand().coeff(y)
d1 = (e1).collect(x).coeff(x,0)
d2 = (e1).collect(x).coeff(x,1)
fm = _sm.Matrix([i.collect(x)for i in _sm.Matrix([e1,e2]).reshape(1, 2)]).reshape((_sm.Matrix([e1,e2]).reshape(1, 2)).shape[0], (_sm.Matrix([e1,e2]).reshape(1, 2)).shape[1])
f = (e1).collect(y)
g = (e1).subs({x:2*x})
gm = _sm.Matrix([i.subs({x:3}) for i in _sm.Matrix([e1,e2]).reshape(2, 1)]).reshape((_sm.Matrix([e1,e2]).reshape(2, 1)).shape[0], (_sm.Matrix([e1,e2]).reshape(2, 1)).shape[1])
frame_a = _me.ReferenceFrame('a')
frame_b = _me.ReferenceFrame('b')
theta = _me.dynamicsymbols('theta')
frame_b.orient(frame_a, 'Axis', [theta, frame_a.z])
v1 = 2*frame_a.x-3*frame_a.y+frame_a.z
v2 = frame_b.x+frame_b.y+frame_b.z
a = _me.dot(v1, v2)
bm = _sm.Matrix([_me.dot(v1, v2),_me.dot(v1, 2*v2)]).reshape(2, 1)
c = _me.cross(v1, v2)
d = 2*v1.magnitude()+3*v1.magnitude()
dyadic = _me.outer(3*frame_a.x, frame_a.x)+_me.outer(frame_a.y, frame_a.y)+_me.outer(2*frame_a.z, frame_a.z)
am = (dyadic).to_matrix(frame_b)
m = _sm.Matrix([1,2,3]).reshape(3, 1)
v = m[0]*frame_a.x +m[1]*frame_a.y +m[2]*frame_a.z

View File

@@ -0,0 +1,38 @@
% ruletest8.al
FRAMES A
CONSTANTS C{3}
A>> = EXPRESS(1>>,A)
PARTICLES P1, P2
BODIES R
R_A = [1,1,1;1,1,0;0,0,1]
POINT O
MASS P1=M1, P2=M2, R=MR
INERTIA R, I1, I2, I3
P_P1_O> = C1*A1>
P_P2_O> = C2*A2>
P_RO_O> = C3*A3>
A>> = EXPRESS(I_P1_O>>, A)
A>> = EXPRESS(I_P2_O>>, A)
A>> = EXPRESS(I_R_O>>, A)
A>> = EXPRESS(INERTIA(O), A)
A>> = EXPRESS(INERTIA(O, P1, R), A)
A>> = EXPRESS(I_R_O>>, A)
A>> = EXPRESS(I_R_RO>>, A)
P_P1_P2> = C1*A1> + C2*A2>
P_P1_RO> = C3*A1>
P_P2_RO> = C3*A2>
B> = CM(O)
B> = CM(O, P1, R)
B> = CM(P1)
MOTIONVARIABLES U{3}
V> = U1*A1> + U2*A2> + U3*A3>
U> = UNITVEC(V> + C1*A1>)
V_P1_A> = U1*A1>
A> = PARTIALS(V_P1_A>, U1)
M = MASS(P1,R)
M = MASS(P2)
M = MASS()

View File

@@ -0,0 +1,49 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
frame_a = _me.ReferenceFrame('a')
c1, c2, c3 = _sm.symbols('c1 c2 c3', real=True)
a = _me.inertia(frame_a, 1, 1, 1)
particle_p1 = _me.Particle('p1', _me.Point('p1_pt'), _sm.Symbol('m'))
particle_p2 = _me.Particle('p2', _me.Point('p2_pt'), _sm.Symbol('m'))
body_r_cm = _me.Point('r_cm')
body_r_f = _me.ReferenceFrame('r_f')
body_r = _me.RigidBody('r', body_r_cm, body_r_f, _sm.symbols('m'), (_me.outer(body_r_f.x,body_r_f.x),body_r_cm))
frame_a.orient(body_r_f, 'DCM', _sm.Matrix([1,1,1,1,1,0,0,0,1]).reshape(3, 3))
point_o = _me.Point('o')
m1 = _sm.symbols('m1')
particle_p1.mass = m1
m2 = _sm.symbols('m2')
particle_p2.mass = m2
mr = _sm.symbols('mr')
body_r.mass = mr
i1 = _sm.symbols('i1')
i2 = _sm.symbols('i2')
i3 = _sm.symbols('i3')
body_r.inertia = (_me.inertia(body_r_f, i1, i2, i3, 0, 0, 0), body_r_cm)
point_o.set_pos(particle_p1.point, c1*frame_a.x)
point_o.set_pos(particle_p2.point, c2*frame_a.y)
point_o.set_pos(body_r_cm, c3*frame_a.z)
a = _me.inertia_of_point_mass(particle_p1.mass, particle_p1.point.pos_from(point_o), frame_a)
a = _me.inertia_of_point_mass(particle_p2.mass, particle_p2.point.pos_from(point_o), frame_a)
a = body_r.inertia[0] + _me.inertia_of_point_mass(body_r.mass, body_r.masscenter.pos_from(point_o), frame_a)
a = _me.inertia_of_point_mass(particle_p1.mass, particle_p1.point.pos_from(point_o), frame_a) + _me.inertia_of_point_mass(particle_p2.mass, particle_p2.point.pos_from(point_o), frame_a) + body_r.inertia[0] + _me.inertia_of_point_mass(body_r.mass, body_r.masscenter.pos_from(point_o), frame_a)
a = _me.inertia_of_point_mass(particle_p1.mass, particle_p1.point.pos_from(point_o), frame_a) + body_r.inertia[0] + _me.inertia_of_point_mass(body_r.mass, body_r.masscenter.pos_from(point_o), frame_a)
a = body_r.inertia[0] + _me.inertia_of_point_mass(body_r.mass, body_r.masscenter.pos_from(point_o), frame_a)
a = body_r.inertia[0]
particle_p2.point.set_pos(particle_p1.point, c1*frame_a.x+c2*frame_a.y)
body_r_cm.set_pos(particle_p1.point, c3*frame_a.x)
body_r_cm.set_pos(particle_p2.point, c3*frame_a.y)
b = _me.functions.center_of_mass(point_o,particle_p1, particle_p2, body_r)
b = _me.functions.center_of_mass(point_o,particle_p1, body_r)
b = _me.functions.center_of_mass(particle_p1.point,particle_p1, particle_p2, body_r)
u1, u2, u3 = _me.dynamicsymbols('u1 u2 u3')
v = u1*frame_a.x+u2*frame_a.y+u3*frame_a.z
u = (v+c1*frame_a.x).normalize()
particle_p1.point.set_vel(frame_a, u1*frame_a.x)
a = particle_p1.point.partial_velocity(frame_a, u1)
m = particle_p1.mass+body_r.mass
m = particle_p2.mass
m = particle_p1.mass+particle_p2.mass+body_r.mass

View File

@@ -0,0 +1,54 @@
% ruletest9.al
NEWTONIAN N
FRAMES A
A> = 0>
D>> = EXPRESS(1>>, A)
POINTS PO{2}
PARTICLES P{2}
MOTIONVARIABLES' C{3}'
BODIES R
P_P1_PO2> = C1*A1>
V> = 2*P_P1_PO2> + C2*A2>
W_A_N> = C3*A3>
V> = 2*W_A_N> + C2*A2>
W_R_N> = C3*A3>
V> = 2*W_R_N> + C2*A2>
ALF_A_N> = DT(W_A_N>, A)
V> = 2*ALF_A_N> + C2*A2>
V_P1_A> = C1*A1> + C3*A2>
A_RO_N> = C2*A2>
V_A> = CROSS(A_RO_N>, V_P1_A>)
X_B_C> = V_A>
X_B_D> = 2*X_B_C>
A_B_C_D_E> = X_B_D>*2
A_B_C = 2*C1*C2*C3
A_B_C += 2*C1
A_B_C := 3*C1
MOTIONVARIABLES' Q{2}', U{2}'
Q1' = U1
Q2' = U2
VARIABLES X'', Y''
SPECIFIED YY
Y'' = X*X'^2 + 1
YY = X*X'^2 + 1
M[1] = 2*X
M[2] = 2*Y
A = 2*M[1]
M = [1,2,3;4,5,6;7,8,9]
M[1, 2] = 5
A = M[1, 2]*2
FORCE_RO> = Q1*N1>
TORQUE_A> = Q2*N3>
FORCE_RO> = Q2*N2>
F> = FORCE_RO>*2

View File

@@ -0,0 +1,55 @@
import sympy.physics.mechanics as _me
import sympy as _sm
import math as m
import numpy as _np
frame_n = _me.ReferenceFrame('n')
frame_a = _me.ReferenceFrame('a')
a = 0
d = _me.inertia(frame_a, 1, 1, 1)
point_po1 = _me.Point('po1')
point_po2 = _me.Point('po2')
particle_p1 = _me.Particle('p1', _me.Point('p1_pt'), _sm.Symbol('m'))
particle_p2 = _me.Particle('p2', _me.Point('p2_pt'), _sm.Symbol('m'))
c1, c2, c3 = _me.dynamicsymbols('c1 c2 c3')
c1_d, c2_d, c3_d = _me.dynamicsymbols('c1_ c2_ c3_', 1)
body_r_cm = _me.Point('r_cm')
body_r_cm.set_vel(frame_n, 0)
body_r_f = _me.ReferenceFrame('r_f')
body_r = _me.RigidBody('r', body_r_cm, body_r_f, _sm.symbols('m'), (_me.outer(body_r_f.x,body_r_f.x),body_r_cm))
point_po2.set_pos(particle_p1.point, c1*frame_a.x)
v = 2*point_po2.pos_from(particle_p1.point)+c2*frame_a.y
frame_a.set_ang_vel(frame_n, c3*frame_a.z)
v = 2*frame_a.ang_vel_in(frame_n)+c2*frame_a.y
body_r_f.set_ang_vel(frame_n, c3*frame_a.z)
v = 2*body_r_f.ang_vel_in(frame_n)+c2*frame_a.y
frame_a.set_ang_acc(frame_n, (frame_a.ang_vel_in(frame_n)).dt(frame_a))
v = 2*frame_a.ang_acc_in(frame_n)+c2*frame_a.y
particle_p1.point.set_vel(frame_a, c1*frame_a.x+c3*frame_a.y)
body_r_cm.set_acc(frame_n, c2*frame_a.y)
v_a = _me.cross(body_r_cm.acc(frame_n), particle_p1.point.vel(frame_a))
x_b_c = v_a
x_b_d = 2*x_b_c
a_b_c_d_e = x_b_d*2
a_b_c = 2*c1*c2*c3
a_b_c += 2*c1
a_b_c = 3*c1
q1, q2, u1, u2 = _me.dynamicsymbols('q1 q2 u1 u2')
q1_d, q2_d, u1_d, u2_d = _me.dynamicsymbols('q1_ q2_ u1_ u2_', 1)
x, y = _me.dynamicsymbols('x y')
x_d, y_d = _me.dynamicsymbols('x_ y_', 1)
x_dd, y_dd = _me.dynamicsymbols('x_ y_', 2)
yy = _me.dynamicsymbols('yy')
yy = x*x_d**2+1
m = _sm.Matrix([[0]])
m[0] = 2*x
m = m.row_insert(m.shape[0], _sm.Matrix([[0]]))
m[m.shape[0]-1] = 2*y
a = 2*m[0]
m = _sm.Matrix([1,2,3,4,5,6,7,8,9]).reshape(3, 3)
m[0,1] = 5
a = m[0, 1]*2
force_ro = q1*frame_n.x
torque_a = q2*frame_n.z
force_ro = q1*frame_n.x + q2*frame_n.y
f = force_ro*2

View File

@@ -0,0 +1 @@
"""Used for translating C source code into a SymPy expression"""

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
"""Used for translating Fortran source code into a SymPy expression. """

View File

@@ -0,0 +1,347 @@
from sympy.external import import_module
lfortran = import_module('lfortran')
if lfortran:
from sympy.codegen.ast import (Variable, IntBaseType, FloatBaseType, String,
Return, FunctionDefinition, Assignment)
from sympy.core import Add, Mul, Integer, Float
from sympy.core.symbol import Symbol
asr_mod = lfortran.asr
asr = lfortran.asr.asr
src_to_ast = lfortran.ast.src_to_ast
ast_to_asr = lfortran.semantic.ast_to_asr.ast_to_asr
"""
This module contains all the necessary Classes and Function used to Parse
Fortran code into SymPy expression
The module and its API are currently under development and experimental.
It is also dependent on LFortran for the ASR that is converted to SymPy syntax
which is also under development.
The module only supports the features currently supported by the LFortran ASR
which will be updated as the development of LFortran and this module progresses
You might find unexpected bugs and exceptions while using the module, feel free
to report them to the SymPy Issue Tracker
The API for the module might also change while in development if better and
more effective ways are discovered for the process
Features Supported
==================
- Variable Declarations (integers and reals)
- Function Definitions
- Assignments and Basic Binary Operations
Notes
=====
The module depends on an external dependency
LFortran : Required to parse Fortran source code into ASR
References
==========
.. [1] https://github.com/sympy/sympy/issues
.. [2] https://gitlab.com/lfortran/lfortran
.. [3] https://docs.lfortran.org/
"""
class ASR2PyVisitor(asr.ASTVisitor): # type: ignore
"""
Visitor Class for LFortran ASR
It is a Visitor class derived from asr.ASRVisitor which visits all the
nodes of the LFortran ASR and creates corresponding AST node for each
ASR node
"""
def __init__(self):
"""Initialize the Parser"""
self._py_ast = []
def visit_TranslationUnit(self, node):
"""
Function to visit all the elements of the Translation Unit
created by LFortran ASR
"""
for s in node.global_scope.symbols:
sym = node.global_scope.symbols[s]
self.visit(sym)
for item in node.items:
self.visit(item)
def visit_Assignment(self, node):
"""Visitor Function for Assignment
Visits each Assignment is the LFortran ASR and creates corresponding
assignment for SymPy.
Notes
=====
The function currently only supports variable assignment and binary
operation assignments of varying multitudes. Any type of numberS or
array is not supported.
Raises
======
NotImplementedError() when called for Numeric assignments or Arrays
"""
# TODO: Arithmetic Assignment
if isinstance(node.target, asr.Variable):
target = node.target
value = node.value
if isinstance(value, asr.Variable):
new_node = Assignment(
Variable(
target.name
),
Variable(
value.name
)
)
elif (type(value) == asr.BinOp):
exp_ast = call_visitor(value)
for expr in exp_ast:
new_node = Assignment(
Variable(target.name),
expr
)
else:
raise NotImplementedError("Numeric assignments not supported")
else:
raise NotImplementedError("Arrays not supported")
self._py_ast.append(new_node)
def visit_BinOp(self, node):
"""Visitor Function for Binary Operations
Visits each binary operation present in the LFortran ASR like addition,
subtraction, multiplication, division and creates the corresponding
operation node in SymPy's AST
In case of more than one binary operations, the function calls the
call_visitor() function on the child nodes of the binary operations
recursively until all the operations have been processed.
Notes
=====
The function currently only supports binary operations with Variables
or other binary operations. Numerics are not supported as of yet.
Raises
======
NotImplementedError() when called for Numeric assignments
"""
# TODO: Integer Binary Operations
op = node.op
lhs = node.left
rhs = node.right
if (type(lhs) == asr.Variable):
left_value = Symbol(lhs.name)
elif(type(lhs) == asr.BinOp):
l_exp_ast = call_visitor(lhs)
for exp in l_exp_ast:
left_value = exp
else:
raise NotImplementedError("Numbers Currently not supported")
if (type(rhs) == asr.Variable):
right_value = Symbol(rhs.name)
elif(type(rhs) == asr.BinOp):
r_exp_ast = call_visitor(rhs)
for exp in r_exp_ast:
right_value = exp
else:
raise NotImplementedError("Numbers Currently not supported")
if isinstance(op, asr.Add):
new_node = Add(left_value, right_value)
elif isinstance(op, asr.Sub):
new_node = Add(left_value, -right_value)
elif isinstance(op, asr.Div):
new_node = Mul(left_value, 1/right_value)
elif isinstance(op, asr.Mul):
new_node = Mul(left_value, right_value)
self._py_ast.append(new_node)
def visit_Variable(self, node):
"""Visitor Function for Variable Declaration
Visits each variable declaration present in the ASR and creates a
Symbol declaration for each variable
Notes
=====
The functions currently only support declaration of integer and
real variables. Other data types are still under development.
Raises
======
NotImplementedError() when called for unsupported data types
"""
if isinstance(node.type, asr.Integer):
var_type = IntBaseType(String('integer'))
value = Integer(0)
elif isinstance(node.type, asr.Real):
var_type = FloatBaseType(String('real'))
value = Float(0.0)
else:
raise NotImplementedError("Data type not supported")
if not (node.intent == 'in'):
new_node = Variable(
node.name
).as_Declaration(
type = var_type,
value = value
)
self._py_ast.append(new_node)
def visit_Sequence(self, seq):
"""Visitor Function for code sequence
Visits a code sequence/ block and calls the visitor function on all the
children of the code block to create corresponding code in python
"""
if seq is not None:
for node in seq:
self._py_ast.append(call_visitor(node))
def visit_Num(self, node):
"""Visitor Function for Numbers in ASR
This function is currently under development and will be updated
with improvements in the LFortran ASR
"""
# TODO:Numbers when the LFortran ASR is updated
# self._py_ast.append(Integer(node.n))
pass
def visit_Function(self, node):
"""Visitor Function for function Definitions
Visits each function definition present in the ASR and creates a
function definition node in the Python AST with all the elements of the
given function
The functions declare all the variables required as SymPy symbols in
the function before the function definition
This function also the call_visior_function to parse the contents of
the function body
"""
# TODO: Return statement, variable declaration
fn_args = [Variable(arg_iter.name) for arg_iter in node.args]
fn_body = []
fn_name = node.name
for i in node.body:
fn_ast = call_visitor(i)
try:
fn_body_expr = fn_ast
except UnboundLocalError:
fn_body_expr = []
for sym in node.symtab.symbols:
decl = call_visitor(node.symtab.symbols[sym])
for symbols in decl:
fn_body.append(symbols)
for elem in fn_body_expr:
fn_body.append(elem)
fn_body.append(
Return(
Variable(
node.return_var.name
)
)
)
if isinstance(node.return_var.type, asr.Integer):
ret_type = IntBaseType(String('integer'))
elif isinstance(node.return_var.type, asr.Real):
ret_type = FloatBaseType(String('real'))
else:
raise NotImplementedError("Data type not supported")
new_node = FunctionDefinition(
return_type = ret_type,
name = fn_name,
parameters = fn_args,
body = fn_body
)
self._py_ast.append(new_node)
def ret_ast(self):
"""Returns the AST nodes"""
return self._py_ast
else:
class ASR2PyVisitor(): # type: ignore
def __init__(self, *args, **kwargs):
raise ImportError('lfortran not available')
def call_visitor(fort_node):
"""Calls the AST Visitor on the Module
This function is used to call the AST visitor for a program or module
It imports all the required modules and calls the visit() function
on the given node
Parameters
==========
fort_node : LFortran ASR object
Node for the operation for which the NodeVisitor is called
Returns
=======
res_ast : list
list of SymPy AST Nodes
"""
v = ASR2PyVisitor()
v.visit(fort_node)
res_ast = v.ret_ast()
return res_ast
def src_to_sympy(src):
"""Wrapper function to convert the given Fortran source code to SymPy Expressions
Parameters
==========
src : string
A string with the Fortran source code
Returns
=======
py_src : string
A string with the Python source code compatible with SymPy
"""
a_ast = src_to_ast(src, translation_unit=False)
a = ast_to_asr(a_ast)
py_src = call_visitor(a)
return py_src

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright 2016, latex2sympy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,312 @@
/*
ANTLR4 LaTeX Math Grammar
Ported from latex2sympy by @augustt198 https://github.com/augustt198/latex2sympy See license in
LICENSE.txt
*/
/*
After changing this file, it is necessary to run `python setup.py antlr` in the root directory of
the repository. This will regenerate the code in `sympy/parsing/latex/_antlr/*.py`.
*/
grammar LaTeX;
options {
language = Python3;
}
WS: [ \t\r\n]+ -> skip;
THINSPACE: ('\\,' | '\\thinspace') -> skip;
MEDSPACE: ('\\:' | '\\medspace') -> skip;
THICKSPACE: ('\\;' | '\\thickspace') -> skip;
QUAD: '\\quad' -> skip;
QQUAD: '\\qquad' -> skip;
NEGTHINSPACE: ('\\!' | '\\negthinspace') -> skip;
NEGMEDSPACE: '\\negmedspace' -> skip;
NEGTHICKSPACE: '\\negthickspace' -> skip;
CMD_LEFT: '\\left' -> skip;
CMD_RIGHT: '\\right' -> skip;
IGNORE:
(
'\\vrule'
| '\\vcenter'
| '\\vbox'
| '\\vskip'
| '\\vspace'
| '\\hfil'
| '\\*'
| '\\-'
| '\\.'
| '\\/'
| '\\"'
| '\\('
| '\\='
) -> skip;
ADD: '+';
SUB: '-';
MUL: '*';
DIV: '/';
L_PAREN: '(';
R_PAREN: ')';
L_BRACE: '{';
R_BRACE: '}';
L_BRACE_LITERAL: '\\{';
R_BRACE_LITERAL: '\\}';
L_BRACKET: '[';
R_BRACKET: ']';
BAR: '|';
R_BAR: '\\right|';
L_BAR: '\\left|';
L_ANGLE: '\\langle';
R_ANGLE: '\\rangle';
FUNC_LIM: '\\lim';
LIM_APPROACH_SYM:
'\\to'
| '\\rightarrow'
| '\\Rightarrow'
| '\\longrightarrow'
| '\\Longrightarrow';
FUNC_INT:
'\\int'
| '\\int\\limits';
FUNC_SUM: '\\sum';
FUNC_PROD: '\\prod';
FUNC_EXP: '\\exp';
FUNC_LOG: '\\log';
FUNC_LG: '\\lg';
FUNC_LN: '\\ln';
FUNC_SIN: '\\sin';
FUNC_COS: '\\cos';
FUNC_TAN: '\\tan';
FUNC_CSC: '\\csc';
FUNC_SEC: '\\sec';
FUNC_COT: '\\cot';
FUNC_ARCSIN: '\\arcsin';
FUNC_ARCCOS: '\\arccos';
FUNC_ARCTAN: '\\arctan';
FUNC_ARCCSC: '\\arccsc';
FUNC_ARCSEC: '\\arcsec';
FUNC_ARCCOT: '\\arccot';
FUNC_SINH: '\\sinh';
FUNC_COSH: '\\cosh';
FUNC_TANH: '\\tanh';
FUNC_ARSINH: '\\arsinh';
FUNC_ARCOSH: '\\arcosh';
FUNC_ARTANH: '\\artanh';
L_FLOOR: '\\lfloor';
R_FLOOR: '\\rfloor';
L_CEIL: '\\lceil';
R_CEIL: '\\rceil';
FUNC_SQRT: '\\sqrt';
FUNC_OVERLINE: '\\overline';
CMD_TIMES: '\\times';
CMD_CDOT: '\\cdot';
CMD_DIV: '\\div';
CMD_FRAC:
'\\frac'
| '\\dfrac'
| '\\tfrac';
CMD_BINOM: '\\binom';
CMD_DBINOM: '\\dbinom';
CMD_TBINOM: '\\tbinom';
CMD_MATHIT: '\\mathit';
UNDERSCORE: '_';
CARET: '^';
COLON: ':';
fragment WS_CHAR: [ \t\r\n];
DIFFERENTIAL: 'd' WS_CHAR*? ([a-zA-Z] | '\\' [a-zA-Z]+);
LETTER: [a-zA-Z];
DIGIT: [0-9];
EQUAL: (('&' WS_CHAR*?)? '=') | ('=' (WS_CHAR*? '&')?);
NEQ: '\\neq';
LT: '<';
LTE: ('\\leq' | '\\le' | LTE_Q | LTE_S);
LTE_Q: '\\leqq';
LTE_S: '\\leqslant';
GT: '>';
GTE: ('\\geq' | '\\ge' | GTE_Q | GTE_S);
GTE_Q: '\\geqq';
GTE_S: '\\geqslant';
BANG: '!';
SINGLE_QUOTES: '\''+;
SYMBOL: '\\' [a-zA-Z]+;
math: relation;
relation:
relation (EQUAL | LT | LTE | GT | GTE | NEQ) relation
| expr;
equality: expr EQUAL expr;
expr: additive;
additive: additive (ADD | SUB) additive | mp;
// mult part
mp:
mp (MUL | CMD_TIMES | CMD_CDOT | DIV | CMD_DIV | COLON) mp
| unary;
mp_nofunc:
mp_nofunc (
MUL
| CMD_TIMES
| CMD_CDOT
| DIV
| CMD_DIV
| COLON
) mp_nofunc
| unary_nofunc;
unary: (ADD | SUB) unary | postfix+;
unary_nofunc:
(ADD | SUB) unary_nofunc
| postfix postfix_nofunc*;
postfix: exp postfix_op*;
postfix_nofunc: exp_nofunc postfix_op*;
postfix_op: BANG | eval_at;
eval_at:
BAR (eval_at_sup | eval_at_sub | eval_at_sup eval_at_sub);
eval_at_sub: UNDERSCORE L_BRACE (expr | equality) R_BRACE;
eval_at_sup: CARET L_BRACE (expr | equality) R_BRACE;
exp: exp CARET (atom | L_BRACE expr R_BRACE) subexpr? | comp;
exp_nofunc:
exp_nofunc CARET (atom | L_BRACE expr R_BRACE) subexpr?
| comp_nofunc;
comp:
group
| abs_group
| func
| atom
| floor
| ceil;
comp_nofunc:
group
| abs_group
| atom
| floor
| ceil;
group:
L_PAREN expr R_PAREN
| L_BRACKET expr R_BRACKET
| L_BRACE expr R_BRACE
| L_BRACE_LITERAL expr R_BRACE_LITERAL;
abs_group: BAR expr BAR;
number: DIGIT+ (',' DIGIT DIGIT DIGIT)* ('.' DIGIT+)?;
atom: (LETTER | SYMBOL) (subexpr? SINGLE_QUOTES? | SINGLE_QUOTES? subexpr?)
| number
| DIFFERENTIAL
| mathit
| frac
| binom
| bra
| ket;
bra: L_ANGLE expr (R_BAR | BAR);
ket: (L_BAR | BAR) expr R_ANGLE;
mathit: CMD_MATHIT L_BRACE mathit_text R_BRACE;
mathit_text: LETTER*;
frac: CMD_FRAC (upperd = DIGIT | L_BRACE upper = expr R_BRACE)
(lowerd = DIGIT | L_BRACE lower = expr R_BRACE);
binom:
(CMD_BINOM | CMD_DBINOM | CMD_TBINOM) L_BRACE n = expr R_BRACE L_BRACE k = expr R_BRACE;
floor: L_FLOOR val = expr R_FLOOR;
ceil: L_CEIL val = expr R_CEIL;
func_normal:
FUNC_EXP
| FUNC_LOG
| FUNC_LG
| FUNC_LN
| FUNC_SIN
| FUNC_COS
| FUNC_TAN
| FUNC_CSC
| FUNC_SEC
| FUNC_COT
| FUNC_ARCSIN
| FUNC_ARCCOS
| FUNC_ARCTAN
| FUNC_ARCCSC
| FUNC_ARCSEC
| FUNC_ARCCOT
| FUNC_SINH
| FUNC_COSH
| FUNC_TANH
| FUNC_ARSINH
| FUNC_ARCOSH
| FUNC_ARTANH;
func:
func_normal (subexpr? supexpr? | supexpr? subexpr?) (
L_PAREN func_arg R_PAREN
| func_arg_noparens
)
| (LETTER | SYMBOL) (subexpr? SINGLE_QUOTES? | SINGLE_QUOTES? subexpr?) // e.g. f(x), f_1'(x)
L_PAREN args R_PAREN
| FUNC_INT (subexpr supexpr | supexpr subexpr)? (
additive? DIFFERENTIAL
| frac
| additive
)
| FUNC_SQRT (L_BRACKET root = expr R_BRACKET)? L_BRACE base = expr R_BRACE
| FUNC_OVERLINE L_BRACE base = expr R_BRACE
| (FUNC_SUM | FUNC_PROD) (subeq supexpr | supexpr subeq) mp
| FUNC_LIM limit_sub mp;
args: (expr ',' args) | expr;
limit_sub:
UNDERSCORE L_BRACE (LETTER | SYMBOL) LIM_APPROACH_SYM expr (
CARET ((L_BRACE (ADD | SUB) R_BRACE) | ADD | SUB)
)? R_BRACE;
func_arg: expr | (expr ',' func_arg);
func_arg_noparens: mp_nofunc;
subexpr: UNDERSCORE (atom | L_BRACE expr R_BRACE);
supexpr: CARET (atom | L_BRACE expr R_BRACE);
subeq: UNDERSCORE L_BRACE equality R_BRACE;
supeq: UNDERSCORE L_BRACE equality R_BRACE;

View File

@@ -0,0 +1,204 @@
from sympy.external import import_module
from sympy.utilities.decorator import doctest_depends_on
from re import compile as rcompile
from sympy.parsing.latex.lark import LarkLaTeXParser, TransformToSymPyExpr, parse_latex_lark # noqa
from .errors import LaTeXParsingError # noqa
IGNORE_L = r"\s*[{]*\s*"
IGNORE_R = r"\s*[}]*\s*"
NO_LEFT = r"(?<!\\left)"
BEGIN_AMS_MAT = r"\\begin{matrix}"
END_AMS_MAT = r"\\end{matrix}"
BEGIN_ARR = r"\\begin{array}{.*?}"
END_ARR = r"\\end{array}"
# begin_delim_regex: end_delim_regex
MATRIX_DELIMS = {fr"\\left\({IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\\right\)",
fr"{NO_LEFT}\({IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\)",
fr"\\left\[{IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\\right\]",
fr"{NO_LEFT}\[{IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\]",
fr"\\left\|{IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\\right\|",
fr"{NO_LEFT}\|{IGNORE_L}{BEGIN_AMS_MAT}": fr"{END_AMS_MAT}{IGNORE_R}\|",
r"\\begin{pmatrix}": r"\\end{pmatrix}",
r"\\begin{bmatrix}": r"\\end{bmatrix}",
r"\\begin{vmatrix}": r"\\end{vmatrix}",
fr"\\left\({IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\\right\)",
fr"{NO_LEFT}\({IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\)",
fr"\\left\[{IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\\right\]",
fr"{NO_LEFT}\[{IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\]",
fr"\\left\|{IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\\right\|",
fr"{NO_LEFT}\|{IGNORE_L}{BEGIN_ARR}": fr"{END_ARR}{IGNORE_R}\|"
}
MATRIX_DELIMS_INV = {v: k for k, v in MATRIX_DELIMS.items()}
# begin_delim_regex: ideal_begin_delim_representative
BEGIN_DELIM_REPR = {fr"\\left\({IGNORE_L}{BEGIN_AMS_MAT}": "\\left(\\begin{matrix}",
fr"{NO_LEFT}\({IGNORE_L}{BEGIN_AMS_MAT}": "(\\begin{matrix}",
fr"\\left\[{IGNORE_L}{BEGIN_AMS_MAT}": "\\left[\\begin{matrix}",
fr"{NO_LEFT}\[{IGNORE_L}{BEGIN_AMS_MAT}": "[\\begin{matrix}",
fr"\\left\|{IGNORE_L}{BEGIN_AMS_MAT}": "\\left|\\begin{matrix}",
fr"{NO_LEFT}\|{IGNORE_L}{BEGIN_AMS_MAT}": "|\\begin{matrix}",
r"\\begin{pmatrix}": "\\begin{pmatrix}",
r"\\begin{bmatrix}": "\\begin{bmatrix}",
r"\\begin{vmatrix}": "\\begin{vmatrix}",
fr"\\left\({IGNORE_L}{BEGIN_ARR}": "\\left(\\begin{array}{COLUMN_SPECIFIERS}",
fr"{NO_LEFT}\({IGNORE_L}{BEGIN_ARR}": "(\\begin{array}{COLUMN_SPECIFIERS}",
fr"\\left\[{IGNORE_L}{BEGIN_ARR}": "\\left[\\begin{array}{COLUMN_SPECIFIERS}",
fr"{NO_LEFT}\[{IGNORE_L}{BEGIN_ARR}": "[\\begin{array}{COLUMN_SPECIFIERS}",
fr"\\left\|{IGNORE_L}{BEGIN_ARR}": "\\left|\\begin{array}{COLUMN_SPECIFIERS}",
fr"{NO_LEFT}\|{IGNORE_L}{BEGIN_ARR}": "|\\begin{array}{COLUMN_SPECIFIERS}"
}
# end_delim_regex: ideal_end_delim_representative
END_DELIM_REPR = {fr"{END_AMS_MAT}{IGNORE_R}\\right\)": "\\end{matrix}\\right)",
fr"{END_AMS_MAT}{IGNORE_R}\)": "\\end{matrix})",
fr"{END_AMS_MAT}{IGNORE_R}\\right\]": "\\end{matrix}\\right]",
fr"{END_AMS_MAT}{IGNORE_R}\]": "\\end{matrix}]",
fr"{END_AMS_MAT}{IGNORE_R}\\right\|": "\\end{matrix}\\right|",
fr"{END_AMS_MAT}{IGNORE_R}\|": "\\end{matrix}|",
r"\\end{pmatrix}": "\\end{pmatrix}",
r"\\end{bmatrix}": "\\end{bmatrix}",
r"\\end{vmatrix}": "\\end{vmatrix}",
fr"{END_ARR}{IGNORE_R}\\right\)": "\\end{array}\\right)",
fr"{END_ARR}{IGNORE_R}\)": "\\end{array})",
fr"{END_ARR}{IGNORE_R}\\right\]": "\\end{array}\\right]",
fr"{END_ARR}{IGNORE_R}\]": "\\end{array}]",
fr"{END_ARR}{IGNORE_R}\\right\|": "\\end{array}\\right|",
fr"{END_ARR}{IGNORE_R}\|": "\\end{array}|"
}
def check_matrix_delimiters(latex_str):
"""Report mismatched, excess, or missing matrix delimiters."""
spans = []
for begin_delim in MATRIX_DELIMS:
end_delim = MATRIX_DELIMS[begin_delim]
p = rcompile(begin_delim)
q = rcompile(end_delim)
spans.extend([(*m.span(), m.group(),
begin_delim) for m in p.finditer(latex_str)])
spans.extend([(*m.span(), m.group(),
end_delim) for m in q.finditer(latex_str)])
spans.sort(key=(lambda x: x[0]))
if len(spans) % 2 == 1:
# Odd number of delimiters; therefore something
# is wrong. We do not complain yet; let's see if
# we can pinpoint the actual error.
spans.append((None, None, None, None))
spans = [(*x, *y) for (x, y) in zip(spans[::2], spans[1::2])]
for x in spans:
# x is supposed to be an 8-tuple of the following form:
#
# (begin_delim_span_start, begin_delim_span_end,
# begin_delim_match, begin_delim_regex,
# end_delim_span_start, end_delim_span_end,
# end_delim_match, end_delim_regex)
sellipsis = "..."
s = x[0] - 10
if s < 0:
s = 0
sellipsis = ""
eellipsis = "..."
e = x[1] + 10
if e > len(latex_str):
e = len(latex_str)
eellipsis = ""
if x[3] in END_DELIM_REPR:
err = (f"Extra '{x[2]}' at index {x[0]} or "
"missing corresponding "
f"'{BEGIN_DELIM_REPR[MATRIX_DELIMS_INV[x[3]]]}' "
f"in LaTeX string: {sellipsis}{latex_str[s:e]}"
f"{eellipsis}")
raise LaTeXParsingError(err)
if x[7] is None:
err = (f"Extra '{x[2]}' at index {x[0]} or "
"missing corresponding "
f"'{END_DELIM_REPR[MATRIX_DELIMS[x[3]]]}' "
f"in LaTeX string: {sellipsis}{latex_str[s:e]}"
f"{eellipsis}")
raise LaTeXParsingError(err)
correct_end_regex = MATRIX_DELIMS[x[3]]
sellipsis = "..." if x[0] > 0 else ""
eellipsis = "..." if x[5] < len(latex_str) else ""
if x[7] != correct_end_regex:
err = ("Expected "
f"'{END_DELIM_REPR[correct_end_regex]}' "
f"to close the '{x[2]}' at index {x[0]} but "
f"found '{x[6]}' at index {x[4]} of LaTeX "
f"string instead: {sellipsis}{latex_str[x[0]:x[5]]}"
f"{eellipsis}")
raise LaTeXParsingError(err)
__doctest_requires__ = {('parse_latex',): ['antlr4', 'lark']}
@doctest_depends_on(modules=('antlr4', 'lark'))
def parse_latex(s, strict=False, backend="antlr"):
r"""Converts the input LaTeX string ``s`` to a SymPy ``Expr``.
Parameters
==========
s : str
The LaTeX string to parse. In Python source containing LaTeX,
*raw strings* (denoted with ``r"``, like this one) are preferred,
as LaTeX makes liberal use of the ``\`` character, which would
trigger escaping in normal Python strings.
backend : str, optional
Currently, there are two backends supported: ANTLR, and Lark.
The default setting is to use the ANTLR backend, which can be
changed to Lark if preferred.
Use ``backend="antlr"`` for the ANTLR-based parser, and
``backend="lark"`` for the Lark-based parser.
The ``backend`` option is case-sensitive, and must be in
all lowercase.
strict : bool, optional
This option is only available with the ANTLR backend.
If True, raise an exception if the string cannot be parsed as
valid LaTeX. If False, try to recover gracefully from common
mistakes.
Examples
========
>>> from sympy.parsing.latex import parse_latex
>>> expr = parse_latex(r"\frac {1 + \sqrt {\a}} {\b}")
>>> expr
(sqrt(a) + 1)/b
>>> expr.evalf(4, subs=dict(a=5, b=2))
1.618
>>> func = parse_latex(r"\int_1^\alpha \dfrac{\mathrm{d}t}{t}", backend="lark")
>>> func.evalf(subs={"alpha": 2})
0.693147180559945
"""
check_matrix_delimiters(s)
if backend == "antlr":
_latex = import_module(
'sympy.parsing.latex._parse_latex_antlr',
import_kwargs={'fromlist': ['X']})
if _latex is not None:
return _latex.parse_latex(s, strict)
elif backend == "lark":
return parse_latex_lark(s)
else:
raise NotImplementedError(f"Using the '{backend}' backend in the LaTeX" \
" parser is not supported.")

View File

@@ -0,0 +1,9 @@
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
#
# Generated from ../LaTeX.g4, derived from latex2sympy
# latex2sympy is licensed under the MIT license
# https://github.com/augustt198/latex2sympy/blob/master/LICENSE.txt
#
# Generated with antlr4
# antlr4 is licensed under the BSD-3-Clause License
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt

View File

@@ -0,0 +1,512 @@
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
#
# Generated from ../LaTeX.g4, derived from latex2sympy
# latex2sympy is licensed under the MIT license
# https://github.com/augustt198/latex2sympy/blob/master/LICENSE.txt
#
# Generated with antlr4
# antlr4 is licensed under the BSD-3-Clause License
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt
from antlr4 import *
from io import StringIO
import sys
if sys.version_info[1] > 5:
from typing import TextIO
else:
from typing.io import TextIO
def serializedATN():
return [
4,0,91,911,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,
2,6,7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,
13,7,13,2,14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,
19,2,20,7,20,2,21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,
26,7,26,2,27,7,27,2,28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,
32,2,33,7,33,2,34,7,34,2,35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,2,
39,7,39,2,40,7,40,2,41,7,41,2,42,7,42,2,43,7,43,2,44,7,44,2,45,7,
45,2,46,7,46,2,47,7,47,2,48,7,48,2,49,7,49,2,50,7,50,2,51,7,51,2,
52,7,52,2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56,2,57,7,57,2,58,7,
58,2,59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63,7,63,2,64,7,64,2,
65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70,7,70,2,71,7,
71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77,7,77,2,
78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84,7,
84,2,85,7,85,2,86,7,86,2,87,7,87,2,88,7,88,2,89,7,89,2,90,7,90,2,
91,7,91,1,0,1,0,1,1,1,1,1,2,4,2,191,8,2,11,2,12,2,192,1,2,1,2,1,
3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,1,3,3,3,209,8,3,1,3,1,
3,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,3,4,224,8,4,1,4,1,
4,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,1,5,3,5,241,8,
5,1,5,1,5,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,7,1,
7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,8,1,
8,1,8,1,8,3,8,277,8,8,1,8,1,8,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,9,1,
9,1,9,1,9,1,9,1,9,1,9,1,9,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,
1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,10,1,11,1,11,1,11,1,11,
1,11,1,11,1,11,1,11,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,1,12,
1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,
1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,
1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,
1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,1,13,3,13,
381,8,13,1,13,1,13,1,14,1,14,1,15,1,15,1,16,1,16,1,17,1,17,1,18,
1,18,1,19,1,19,1,20,1,20,1,21,1,21,1,22,1,22,1,22,1,23,1,23,1,23,
1,24,1,24,1,25,1,25,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,27,
1,27,1,28,1,28,1,28,1,28,1,28,1,28,1,28,1,29,1,29,1,29,1,29,1,29,
1,29,1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31,
1,31,1,31,1,31,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,
1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,
1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,
1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,
1,32,1,32,1,32,1,32,1,32,1,32,3,32,504,8,32,1,33,1,33,1,33,1,33,
1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,1,33,3,33,521,
8,33,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1,35,1,35,1,35,1,35,1,36,
1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,38,1,38,1,38,1,38,
1,39,1,39,1,39,1,39,1,40,1,40,1,40,1,40,1,40,1,41,1,41,1,41,1,41,
1,41,1,42,1,42,1,42,1,42,1,42,1,43,1,43,1,43,1,43,1,43,1,44,1,44,
1,44,1,44,1,44,1,45,1,45,1,45,1,45,1,45,1,46,1,46,1,46,1,46,1,46,
1,46,1,46,1,46,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,48,1,48,
1,48,1,48,1,48,1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,49,1,49,1,49,
1,49,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,50,1,51,1,51,1,51,1,51,
1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1,52,1,53,1,53,1,53,
1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,54,1,55,1,55,1,55,1,55,
1,55,1,55,1,55,1,55,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,57,
1,57,1,57,1,57,1,57,1,57,1,57,1,57,1,58,1,58,1,58,1,58,1,58,1,58,
1,58,1,58,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,59,1,60,1,60,1,60,
1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1,61,1,61,1,62,1,62,
1,62,1,62,1,62,1,62,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,1,63,
1,63,1,64,1,64,1,64,1,64,1,64,1,64,1,64,1,65,1,65,1,65,1,65,1,65,
1,65,1,66,1,66,1,66,1,66,1,66,1,67,1,67,1,67,1,67,1,67,1,67,1,67,
1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,67,3,67,753,8,67,
1,68,1,68,1,68,1,68,1,68,1,68,1,68,1,69,1,69,1,69,1,69,1,69,1,69,
1,69,1,69,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,70,1,71,1,71,1,71,
1,71,1,71,1,71,1,71,1,71,1,72,1,72,1,73,1,73,1,74,1,74,1,75,1,75,
1,76,1,76,5,76,796,8,76,10,76,12,76,799,9,76,1,76,1,76,1,76,4,76,
804,8,76,11,76,12,76,805,3,76,808,8,76,1,77,1,77,1,78,1,78,1,79,
1,79,5,79,816,8,79,10,79,12,79,819,9,79,3,79,821,8,79,1,79,1,79,
1,79,5,79,826,8,79,10,79,12,79,829,9,79,1,79,3,79,832,8,79,3,79,
834,8,79,1,80,1,80,1,80,1,80,1,80,1,81,1,81,1,82,1,82,1,82,1,82,
1,82,1,82,1,82,1,82,1,82,3,82,852,8,82,1,83,1,83,1,83,1,83,1,83,
1,83,1,84,1,84,1,84,1,84,1,84,1,84,1,84,1,84,1,84,1,84,1,85,1,85,
1,86,1,86,1,86,1,86,1,86,1,86,1,86,1,86,1,86,3,86,881,8,86,1,87,
1,87,1,87,1,87,1,87,1,87,1,88,1,88,1,88,1,88,1,88,1,88,1,88,1,88,
1,88,1,88,1,89,1,89,1,90,4,90,902,8,90,11,90,12,90,903,1,91,1,91,
4,91,908,8,91,11,91,12,91,909,3,797,817,827,0,92,1,1,3,2,5,3,7,4,
9,5,11,6,13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14,29,15,31,16,
33,17,35,18,37,19,39,20,41,21,43,22,45,23,47,24,49,25,51,26,53,27,
55,28,57,29,59,30,61,31,63,32,65,33,67,34,69,35,71,36,73,37,75,38,
77,39,79,40,81,41,83,42,85,43,87,44,89,45,91,46,93,47,95,48,97,49,
99,50,101,51,103,52,105,53,107,54,109,55,111,56,113,57,115,58,117,
59,119,60,121,61,123,62,125,63,127,64,129,65,131,66,133,67,135,68,
137,69,139,70,141,71,143,72,145,73,147,74,149,75,151,0,153,76,155,
77,157,78,159,79,161,80,163,81,165,82,167,83,169,84,171,85,173,86,
175,87,177,88,179,89,181,90,183,91,1,0,3,3,0,9,10,13,13,32,32,2,
0,65,90,97,122,1,0,48,57,949,0,1,1,0,0,0,0,3,1,0,0,0,0,5,1,0,0,0,
0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0,0,0,0,
17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0,
27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,
37,1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,
47,1,0,0,0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,
57,1,0,0,0,0,59,1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,
67,1,0,0,0,0,69,1,0,0,0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,
77,1,0,0,0,0,79,1,0,0,0,0,81,1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,
87,1,0,0,0,0,89,1,0,0,0,0,91,1,0,0,0,0,93,1,0,0,0,0,95,1,0,0,0,0,
97,1,0,0,0,0,99,1,0,0,0,0,101,1,0,0,0,0,103,1,0,0,0,0,105,1,0,0,
0,0,107,1,0,0,0,0,109,1,0,0,0,0,111,1,0,0,0,0,113,1,0,0,0,0,115,
1,0,0,0,0,117,1,0,0,0,0,119,1,0,0,0,0,121,1,0,0,0,0,123,1,0,0,0,
0,125,1,0,0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0,133,1,
0,0,0,0,135,1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0,
143,1,0,0,0,0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,153,1,0,
0,0,0,155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,163,
1,0,0,0,0,165,1,0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,1,0,0,0,
0,173,1,0,0,0,0,175,1,0,0,0,0,177,1,0,0,0,0,179,1,0,0,0,0,181,1,
0,0,0,0,183,1,0,0,0,1,185,1,0,0,0,3,187,1,0,0,0,5,190,1,0,0,0,7,
208,1,0,0,0,9,223,1,0,0,0,11,240,1,0,0,0,13,244,1,0,0,0,15,252,1,
0,0,0,17,276,1,0,0,0,19,280,1,0,0,0,21,295,1,0,0,0,23,312,1,0,0,
0,25,320,1,0,0,0,27,380,1,0,0,0,29,384,1,0,0,0,31,386,1,0,0,0,33,
388,1,0,0,0,35,390,1,0,0,0,37,392,1,0,0,0,39,394,1,0,0,0,41,396,
1,0,0,0,43,398,1,0,0,0,45,400,1,0,0,0,47,403,1,0,0,0,49,406,1,0,
0,0,51,408,1,0,0,0,53,410,1,0,0,0,55,412,1,0,0,0,57,420,1,0,0,0,
59,427,1,0,0,0,61,435,1,0,0,0,63,443,1,0,0,0,65,503,1,0,0,0,67,520,
1,0,0,0,69,522,1,0,0,0,71,527,1,0,0,0,73,533,1,0,0,0,75,538,1,0,
0,0,77,543,1,0,0,0,79,547,1,0,0,0,81,551,1,0,0,0,83,556,1,0,0,0,
85,561,1,0,0,0,87,566,1,0,0,0,89,571,1,0,0,0,91,576,1,0,0,0,93,581,
1,0,0,0,95,589,1,0,0,0,97,597,1,0,0,0,99,605,1,0,0,0,101,613,1,0,
0,0,103,621,1,0,0,0,105,629,1,0,0,0,107,635,1,0,0,0,109,641,1,0,
0,0,111,647,1,0,0,0,113,655,1,0,0,0,115,663,1,0,0,0,117,671,1,0,
0,0,119,679,1,0,0,0,121,687,1,0,0,0,123,694,1,0,0,0,125,701,1,0,
0,0,127,707,1,0,0,0,129,717,1,0,0,0,131,724,1,0,0,0,133,730,1,0,
0,0,135,752,1,0,0,0,137,754,1,0,0,0,139,761,1,0,0,0,141,769,1,0,
0,0,143,777,1,0,0,0,145,785,1,0,0,0,147,787,1,0,0,0,149,789,1,0,
0,0,151,791,1,0,0,0,153,793,1,0,0,0,155,809,1,0,0,0,157,811,1,0,
0,0,159,833,1,0,0,0,161,835,1,0,0,0,163,840,1,0,0,0,165,851,1,0,
0,0,167,853,1,0,0,0,169,859,1,0,0,0,171,869,1,0,0,0,173,880,1,0,
0,0,175,882,1,0,0,0,177,888,1,0,0,0,179,898,1,0,0,0,181,901,1,0,
0,0,183,905,1,0,0,0,185,186,5,44,0,0,186,2,1,0,0,0,187,188,5,46,
0,0,188,4,1,0,0,0,189,191,7,0,0,0,190,189,1,0,0,0,191,192,1,0,0,
0,192,190,1,0,0,0,192,193,1,0,0,0,193,194,1,0,0,0,194,195,6,2,0,
0,195,6,1,0,0,0,196,197,5,92,0,0,197,209,5,44,0,0,198,199,5,92,0,
0,199,200,5,116,0,0,200,201,5,104,0,0,201,202,5,105,0,0,202,203,
5,110,0,0,203,204,5,115,0,0,204,205,5,112,0,0,205,206,5,97,0,0,206,
207,5,99,0,0,207,209,5,101,0,0,208,196,1,0,0,0,208,198,1,0,0,0,209,
210,1,0,0,0,210,211,6,3,0,0,211,8,1,0,0,0,212,213,5,92,0,0,213,224,
5,58,0,0,214,215,5,92,0,0,215,216,5,109,0,0,216,217,5,101,0,0,217,
218,5,100,0,0,218,219,5,115,0,0,219,220,5,112,0,0,220,221,5,97,0,
0,221,222,5,99,0,0,222,224,5,101,0,0,223,212,1,0,0,0,223,214,1,0,
0,0,224,225,1,0,0,0,225,226,6,4,0,0,226,10,1,0,0,0,227,228,5,92,
0,0,228,241,5,59,0,0,229,230,5,92,0,0,230,231,5,116,0,0,231,232,
5,104,0,0,232,233,5,105,0,0,233,234,5,99,0,0,234,235,5,107,0,0,235,
236,5,115,0,0,236,237,5,112,0,0,237,238,5,97,0,0,238,239,5,99,0,
0,239,241,5,101,0,0,240,227,1,0,0,0,240,229,1,0,0,0,241,242,1,0,
0,0,242,243,6,5,0,0,243,12,1,0,0,0,244,245,5,92,0,0,245,246,5,113,
0,0,246,247,5,117,0,0,247,248,5,97,0,0,248,249,5,100,0,0,249,250,
1,0,0,0,250,251,6,6,0,0,251,14,1,0,0,0,252,253,5,92,0,0,253,254,
5,113,0,0,254,255,5,113,0,0,255,256,5,117,0,0,256,257,5,97,0,0,257,
258,5,100,0,0,258,259,1,0,0,0,259,260,6,7,0,0,260,16,1,0,0,0,261,
262,5,92,0,0,262,277,5,33,0,0,263,264,5,92,0,0,264,265,5,110,0,0,
265,266,5,101,0,0,266,267,5,103,0,0,267,268,5,116,0,0,268,269,5,
104,0,0,269,270,5,105,0,0,270,271,5,110,0,0,271,272,5,115,0,0,272,
273,5,112,0,0,273,274,5,97,0,0,274,275,5,99,0,0,275,277,5,101,0,
0,276,261,1,0,0,0,276,263,1,0,0,0,277,278,1,0,0,0,278,279,6,8,0,
0,279,18,1,0,0,0,280,281,5,92,0,0,281,282,5,110,0,0,282,283,5,101,
0,0,283,284,5,103,0,0,284,285,5,109,0,0,285,286,5,101,0,0,286,287,
5,100,0,0,287,288,5,115,0,0,288,289,5,112,0,0,289,290,5,97,0,0,290,
291,5,99,0,0,291,292,5,101,0,0,292,293,1,0,0,0,293,294,6,9,0,0,294,
20,1,0,0,0,295,296,5,92,0,0,296,297,5,110,0,0,297,298,5,101,0,0,
298,299,5,103,0,0,299,300,5,116,0,0,300,301,5,104,0,0,301,302,5,
105,0,0,302,303,5,99,0,0,303,304,5,107,0,0,304,305,5,115,0,0,305,
306,5,112,0,0,306,307,5,97,0,0,307,308,5,99,0,0,308,309,5,101,0,
0,309,310,1,0,0,0,310,311,6,10,0,0,311,22,1,0,0,0,312,313,5,92,0,
0,313,314,5,108,0,0,314,315,5,101,0,0,315,316,5,102,0,0,316,317,
5,116,0,0,317,318,1,0,0,0,318,319,6,11,0,0,319,24,1,0,0,0,320,321,
5,92,0,0,321,322,5,114,0,0,322,323,5,105,0,0,323,324,5,103,0,0,324,
325,5,104,0,0,325,326,5,116,0,0,326,327,1,0,0,0,327,328,6,12,0,0,
328,26,1,0,0,0,329,330,5,92,0,0,330,331,5,118,0,0,331,332,5,114,
0,0,332,333,5,117,0,0,333,334,5,108,0,0,334,381,5,101,0,0,335,336,
5,92,0,0,336,337,5,118,0,0,337,338,5,99,0,0,338,339,5,101,0,0,339,
340,5,110,0,0,340,341,5,116,0,0,341,342,5,101,0,0,342,381,5,114,
0,0,343,344,5,92,0,0,344,345,5,118,0,0,345,346,5,98,0,0,346,347,
5,111,0,0,347,381,5,120,0,0,348,349,5,92,0,0,349,350,5,118,0,0,350,
351,5,115,0,0,351,352,5,107,0,0,352,353,5,105,0,0,353,381,5,112,
0,0,354,355,5,92,0,0,355,356,5,118,0,0,356,357,5,115,0,0,357,358,
5,112,0,0,358,359,5,97,0,0,359,360,5,99,0,0,360,381,5,101,0,0,361,
362,5,92,0,0,362,363,5,104,0,0,363,364,5,102,0,0,364,365,5,105,0,
0,365,381,5,108,0,0,366,367,5,92,0,0,367,381,5,42,0,0,368,369,5,
92,0,0,369,381,5,45,0,0,370,371,5,92,0,0,371,381,5,46,0,0,372,373,
5,92,0,0,373,381,5,47,0,0,374,375,5,92,0,0,375,381,5,34,0,0,376,
377,5,92,0,0,377,381,5,40,0,0,378,379,5,92,0,0,379,381,5,61,0,0,
380,329,1,0,0,0,380,335,1,0,0,0,380,343,1,0,0,0,380,348,1,0,0,0,
380,354,1,0,0,0,380,361,1,0,0,0,380,366,1,0,0,0,380,368,1,0,0,0,
380,370,1,0,0,0,380,372,1,0,0,0,380,374,1,0,0,0,380,376,1,0,0,0,
380,378,1,0,0,0,381,382,1,0,0,0,382,383,6,13,0,0,383,28,1,0,0,0,
384,385,5,43,0,0,385,30,1,0,0,0,386,387,5,45,0,0,387,32,1,0,0,0,
388,389,5,42,0,0,389,34,1,0,0,0,390,391,5,47,0,0,391,36,1,0,0,0,
392,393,5,40,0,0,393,38,1,0,0,0,394,395,5,41,0,0,395,40,1,0,0,0,
396,397,5,123,0,0,397,42,1,0,0,0,398,399,5,125,0,0,399,44,1,0,0,
0,400,401,5,92,0,0,401,402,5,123,0,0,402,46,1,0,0,0,403,404,5,92,
0,0,404,405,5,125,0,0,405,48,1,0,0,0,406,407,5,91,0,0,407,50,1,0,
0,0,408,409,5,93,0,0,409,52,1,0,0,0,410,411,5,124,0,0,411,54,1,0,
0,0,412,413,5,92,0,0,413,414,5,114,0,0,414,415,5,105,0,0,415,416,
5,103,0,0,416,417,5,104,0,0,417,418,5,116,0,0,418,419,5,124,0,0,
419,56,1,0,0,0,420,421,5,92,0,0,421,422,5,108,0,0,422,423,5,101,
0,0,423,424,5,102,0,0,424,425,5,116,0,0,425,426,5,124,0,0,426,58,
1,0,0,0,427,428,5,92,0,0,428,429,5,108,0,0,429,430,5,97,0,0,430,
431,5,110,0,0,431,432,5,103,0,0,432,433,5,108,0,0,433,434,5,101,
0,0,434,60,1,0,0,0,435,436,5,92,0,0,436,437,5,114,0,0,437,438,5,
97,0,0,438,439,5,110,0,0,439,440,5,103,0,0,440,441,5,108,0,0,441,
442,5,101,0,0,442,62,1,0,0,0,443,444,5,92,0,0,444,445,5,108,0,0,
445,446,5,105,0,0,446,447,5,109,0,0,447,64,1,0,0,0,448,449,5,92,
0,0,449,450,5,116,0,0,450,504,5,111,0,0,451,452,5,92,0,0,452,453,
5,114,0,0,453,454,5,105,0,0,454,455,5,103,0,0,455,456,5,104,0,0,
456,457,5,116,0,0,457,458,5,97,0,0,458,459,5,114,0,0,459,460,5,114,
0,0,460,461,5,111,0,0,461,504,5,119,0,0,462,463,5,92,0,0,463,464,
5,82,0,0,464,465,5,105,0,0,465,466,5,103,0,0,466,467,5,104,0,0,467,
468,5,116,0,0,468,469,5,97,0,0,469,470,5,114,0,0,470,471,5,114,0,
0,471,472,5,111,0,0,472,504,5,119,0,0,473,474,5,92,0,0,474,475,5,
108,0,0,475,476,5,111,0,0,476,477,5,110,0,0,477,478,5,103,0,0,478,
479,5,114,0,0,479,480,5,105,0,0,480,481,5,103,0,0,481,482,5,104,
0,0,482,483,5,116,0,0,483,484,5,97,0,0,484,485,5,114,0,0,485,486,
5,114,0,0,486,487,5,111,0,0,487,504,5,119,0,0,488,489,5,92,0,0,489,
490,5,76,0,0,490,491,5,111,0,0,491,492,5,110,0,0,492,493,5,103,0,
0,493,494,5,114,0,0,494,495,5,105,0,0,495,496,5,103,0,0,496,497,
5,104,0,0,497,498,5,116,0,0,498,499,5,97,0,0,499,500,5,114,0,0,500,
501,5,114,0,0,501,502,5,111,0,0,502,504,5,119,0,0,503,448,1,0,0,
0,503,451,1,0,0,0,503,462,1,0,0,0,503,473,1,0,0,0,503,488,1,0,0,
0,504,66,1,0,0,0,505,506,5,92,0,0,506,507,5,105,0,0,507,508,5,110,
0,0,508,521,5,116,0,0,509,510,5,92,0,0,510,511,5,105,0,0,511,512,
5,110,0,0,512,513,5,116,0,0,513,514,5,92,0,0,514,515,5,108,0,0,515,
516,5,105,0,0,516,517,5,109,0,0,517,518,5,105,0,0,518,519,5,116,
0,0,519,521,5,115,0,0,520,505,1,0,0,0,520,509,1,0,0,0,521,68,1,0,
0,0,522,523,5,92,0,0,523,524,5,115,0,0,524,525,5,117,0,0,525,526,
5,109,0,0,526,70,1,0,0,0,527,528,5,92,0,0,528,529,5,112,0,0,529,
530,5,114,0,0,530,531,5,111,0,0,531,532,5,100,0,0,532,72,1,0,0,0,
533,534,5,92,0,0,534,535,5,101,0,0,535,536,5,120,0,0,536,537,5,112,
0,0,537,74,1,0,0,0,538,539,5,92,0,0,539,540,5,108,0,0,540,541,5,
111,0,0,541,542,5,103,0,0,542,76,1,0,0,0,543,544,5,92,0,0,544,545,
5,108,0,0,545,546,5,103,0,0,546,78,1,0,0,0,547,548,5,92,0,0,548,
549,5,108,0,0,549,550,5,110,0,0,550,80,1,0,0,0,551,552,5,92,0,0,
552,553,5,115,0,0,553,554,5,105,0,0,554,555,5,110,0,0,555,82,1,0,
0,0,556,557,5,92,0,0,557,558,5,99,0,0,558,559,5,111,0,0,559,560,
5,115,0,0,560,84,1,0,0,0,561,562,5,92,0,0,562,563,5,116,0,0,563,
564,5,97,0,0,564,565,5,110,0,0,565,86,1,0,0,0,566,567,5,92,0,0,567,
568,5,99,0,0,568,569,5,115,0,0,569,570,5,99,0,0,570,88,1,0,0,0,571,
572,5,92,0,0,572,573,5,115,0,0,573,574,5,101,0,0,574,575,5,99,0,
0,575,90,1,0,0,0,576,577,5,92,0,0,577,578,5,99,0,0,578,579,5,111,
0,0,579,580,5,116,0,0,580,92,1,0,0,0,581,582,5,92,0,0,582,583,5,
97,0,0,583,584,5,114,0,0,584,585,5,99,0,0,585,586,5,115,0,0,586,
587,5,105,0,0,587,588,5,110,0,0,588,94,1,0,0,0,589,590,5,92,0,0,
590,591,5,97,0,0,591,592,5,114,0,0,592,593,5,99,0,0,593,594,5,99,
0,0,594,595,5,111,0,0,595,596,5,115,0,0,596,96,1,0,0,0,597,598,5,
92,0,0,598,599,5,97,0,0,599,600,5,114,0,0,600,601,5,99,0,0,601,602,
5,116,0,0,602,603,5,97,0,0,603,604,5,110,0,0,604,98,1,0,0,0,605,
606,5,92,0,0,606,607,5,97,0,0,607,608,5,114,0,0,608,609,5,99,0,0,
609,610,5,99,0,0,610,611,5,115,0,0,611,612,5,99,0,0,612,100,1,0,
0,0,613,614,5,92,0,0,614,615,5,97,0,0,615,616,5,114,0,0,616,617,
5,99,0,0,617,618,5,115,0,0,618,619,5,101,0,0,619,620,5,99,0,0,620,
102,1,0,0,0,621,622,5,92,0,0,622,623,5,97,0,0,623,624,5,114,0,0,
624,625,5,99,0,0,625,626,5,99,0,0,626,627,5,111,0,0,627,628,5,116,
0,0,628,104,1,0,0,0,629,630,5,92,0,0,630,631,5,115,0,0,631,632,5,
105,0,0,632,633,5,110,0,0,633,634,5,104,0,0,634,106,1,0,0,0,635,
636,5,92,0,0,636,637,5,99,0,0,637,638,5,111,0,0,638,639,5,115,0,
0,639,640,5,104,0,0,640,108,1,0,0,0,641,642,5,92,0,0,642,643,5,116,
0,0,643,644,5,97,0,0,644,645,5,110,0,0,645,646,5,104,0,0,646,110,
1,0,0,0,647,648,5,92,0,0,648,649,5,97,0,0,649,650,5,114,0,0,650,
651,5,115,0,0,651,652,5,105,0,0,652,653,5,110,0,0,653,654,5,104,
0,0,654,112,1,0,0,0,655,656,5,92,0,0,656,657,5,97,0,0,657,658,5,
114,0,0,658,659,5,99,0,0,659,660,5,111,0,0,660,661,5,115,0,0,661,
662,5,104,0,0,662,114,1,0,0,0,663,664,5,92,0,0,664,665,5,97,0,0,
665,666,5,114,0,0,666,667,5,116,0,0,667,668,5,97,0,0,668,669,5,110,
0,0,669,670,5,104,0,0,670,116,1,0,0,0,671,672,5,92,0,0,672,673,5,
108,0,0,673,674,5,102,0,0,674,675,5,108,0,0,675,676,5,111,0,0,676,
677,5,111,0,0,677,678,5,114,0,0,678,118,1,0,0,0,679,680,5,92,0,0,
680,681,5,114,0,0,681,682,5,102,0,0,682,683,5,108,0,0,683,684,5,
111,0,0,684,685,5,111,0,0,685,686,5,114,0,0,686,120,1,0,0,0,687,
688,5,92,0,0,688,689,5,108,0,0,689,690,5,99,0,0,690,691,5,101,0,
0,691,692,5,105,0,0,692,693,5,108,0,0,693,122,1,0,0,0,694,695,5,
92,0,0,695,696,5,114,0,0,696,697,5,99,0,0,697,698,5,101,0,0,698,
699,5,105,0,0,699,700,5,108,0,0,700,124,1,0,0,0,701,702,5,92,0,0,
702,703,5,115,0,0,703,704,5,113,0,0,704,705,5,114,0,0,705,706,5,
116,0,0,706,126,1,0,0,0,707,708,5,92,0,0,708,709,5,111,0,0,709,710,
5,118,0,0,710,711,5,101,0,0,711,712,5,114,0,0,712,713,5,108,0,0,
713,714,5,105,0,0,714,715,5,110,0,0,715,716,5,101,0,0,716,128,1,
0,0,0,717,718,5,92,0,0,718,719,5,116,0,0,719,720,5,105,0,0,720,721,
5,109,0,0,721,722,5,101,0,0,722,723,5,115,0,0,723,130,1,0,0,0,724,
725,5,92,0,0,725,726,5,99,0,0,726,727,5,100,0,0,727,728,5,111,0,
0,728,729,5,116,0,0,729,132,1,0,0,0,730,731,5,92,0,0,731,732,5,100,
0,0,732,733,5,105,0,0,733,734,5,118,0,0,734,134,1,0,0,0,735,736,
5,92,0,0,736,737,5,102,0,0,737,738,5,114,0,0,738,739,5,97,0,0,739,
753,5,99,0,0,740,741,5,92,0,0,741,742,5,100,0,0,742,743,5,102,0,
0,743,744,5,114,0,0,744,745,5,97,0,0,745,753,5,99,0,0,746,747,5,
92,0,0,747,748,5,116,0,0,748,749,5,102,0,0,749,750,5,114,0,0,750,
751,5,97,0,0,751,753,5,99,0,0,752,735,1,0,0,0,752,740,1,0,0,0,752,
746,1,0,0,0,753,136,1,0,0,0,754,755,5,92,0,0,755,756,5,98,0,0,756,
757,5,105,0,0,757,758,5,110,0,0,758,759,5,111,0,0,759,760,5,109,
0,0,760,138,1,0,0,0,761,762,5,92,0,0,762,763,5,100,0,0,763,764,5,
98,0,0,764,765,5,105,0,0,765,766,5,110,0,0,766,767,5,111,0,0,767,
768,5,109,0,0,768,140,1,0,0,0,769,770,5,92,0,0,770,771,5,116,0,0,
771,772,5,98,0,0,772,773,5,105,0,0,773,774,5,110,0,0,774,775,5,111,
0,0,775,776,5,109,0,0,776,142,1,0,0,0,777,778,5,92,0,0,778,779,5,
109,0,0,779,780,5,97,0,0,780,781,5,116,0,0,781,782,5,104,0,0,782,
783,5,105,0,0,783,784,5,116,0,0,784,144,1,0,0,0,785,786,5,95,0,0,
786,146,1,0,0,0,787,788,5,94,0,0,788,148,1,0,0,0,789,790,5,58,0,
0,790,150,1,0,0,0,791,792,7,0,0,0,792,152,1,0,0,0,793,797,5,100,
0,0,794,796,3,151,75,0,795,794,1,0,0,0,796,799,1,0,0,0,797,798,1,
0,0,0,797,795,1,0,0,0,798,807,1,0,0,0,799,797,1,0,0,0,800,808,7,
1,0,0,801,803,5,92,0,0,802,804,7,1,0,0,803,802,1,0,0,0,804,805,1,
0,0,0,805,803,1,0,0,0,805,806,1,0,0,0,806,808,1,0,0,0,807,800,1,
0,0,0,807,801,1,0,0,0,808,154,1,0,0,0,809,810,7,1,0,0,810,156,1,
0,0,0,811,812,7,2,0,0,812,158,1,0,0,0,813,817,5,38,0,0,814,816,3,
151,75,0,815,814,1,0,0,0,816,819,1,0,0,0,817,818,1,0,0,0,817,815,
1,0,0,0,818,821,1,0,0,0,819,817,1,0,0,0,820,813,1,0,0,0,820,821,
1,0,0,0,821,822,1,0,0,0,822,834,5,61,0,0,823,831,5,61,0,0,824,826,
3,151,75,0,825,824,1,0,0,0,826,829,1,0,0,0,827,828,1,0,0,0,827,825,
1,0,0,0,828,830,1,0,0,0,829,827,1,0,0,0,830,832,5,38,0,0,831,827,
1,0,0,0,831,832,1,0,0,0,832,834,1,0,0,0,833,820,1,0,0,0,833,823,
1,0,0,0,834,160,1,0,0,0,835,836,5,92,0,0,836,837,5,110,0,0,837,838,
5,101,0,0,838,839,5,113,0,0,839,162,1,0,0,0,840,841,5,60,0,0,841,
164,1,0,0,0,842,843,5,92,0,0,843,844,5,108,0,0,844,845,5,101,0,0,
845,852,5,113,0,0,846,847,5,92,0,0,847,848,5,108,0,0,848,852,5,101,
0,0,849,852,3,167,83,0,850,852,3,169,84,0,851,842,1,0,0,0,851,846,
1,0,0,0,851,849,1,0,0,0,851,850,1,0,0,0,852,166,1,0,0,0,853,854,
5,92,0,0,854,855,5,108,0,0,855,856,5,101,0,0,856,857,5,113,0,0,857,
858,5,113,0,0,858,168,1,0,0,0,859,860,5,92,0,0,860,861,5,108,0,0,
861,862,5,101,0,0,862,863,5,113,0,0,863,864,5,115,0,0,864,865,5,
108,0,0,865,866,5,97,0,0,866,867,5,110,0,0,867,868,5,116,0,0,868,
170,1,0,0,0,869,870,5,62,0,0,870,172,1,0,0,0,871,872,5,92,0,0,872,
873,5,103,0,0,873,874,5,101,0,0,874,881,5,113,0,0,875,876,5,92,0,
0,876,877,5,103,0,0,877,881,5,101,0,0,878,881,3,175,87,0,879,881,
3,177,88,0,880,871,1,0,0,0,880,875,1,0,0,0,880,878,1,0,0,0,880,879,
1,0,0,0,881,174,1,0,0,0,882,883,5,92,0,0,883,884,5,103,0,0,884,885,
5,101,0,0,885,886,5,113,0,0,886,887,5,113,0,0,887,176,1,0,0,0,888,
889,5,92,0,0,889,890,5,103,0,0,890,891,5,101,0,0,891,892,5,113,0,
0,892,893,5,115,0,0,893,894,5,108,0,0,894,895,5,97,0,0,895,896,5,
110,0,0,896,897,5,116,0,0,897,178,1,0,0,0,898,899,5,33,0,0,899,180,
1,0,0,0,900,902,5,39,0,0,901,900,1,0,0,0,902,903,1,0,0,0,903,901,
1,0,0,0,903,904,1,0,0,0,904,182,1,0,0,0,905,907,5,92,0,0,906,908,
7,1,0,0,907,906,1,0,0,0,908,909,1,0,0,0,909,907,1,0,0,0,909,910,
1,0,0,0,910,184,1,0,0,0,22,0,192,208,223,240,276,380,503,520,752,
797,805,807,817,820,827,831,833,851,880,903,909,1,6,0,0
]
class LaTeXLexer(Lexer):
atn = ATNDeserializer().deserialize(serializedATN())
decisionsToDFA = [ DFA(ds, i) for i, ds in enumerate(atn.decisionToState) ]
T__0 = 1
T__1 = 2
WS = 3
THINSPACE = 4
MEDSPACE = 5
THICKSPACE = 6
QUAD = 7
QQUAD = 8
NEGTHINSPACE = 9
NEGMEDSPACE = 10
NEGTHICKSPACE = 11
CMD_LEFT = 12
CMD_RIGHT = 13
IGNORE = 14
ADD = 15
SUB = 16
MUL = 17
DIV = 18
L_PAREN = 19
R_PAREN = 20
L_BRACE = 21
R_BRACE = 22
L_BRACE_LITERAL = 23
R_BRACE_LITERAL = 24
L_BRACKET = 25
R_BRACKET = 26
BAR = 27
R_BAR = 28
L_BAR = 29
L_ANGLE = 30
R_ANGLE = 31
FUNC_LIM = 32
LIM_APPROACH_SYM = 33
FUNC_INT = 34
FUNC_SUM = 35
FUNC_PROD = 36
FUNC_EXP = 37
FUNC_LOG = 38
FUNC_LG = 39
FUNC_LN = 40
FUNC_SIN = 41
FUNC_COS = 42
FUNC_TAN = 43
FUNC_CSC = 44
FUNC_SEC = 45
FUNC_COT = 46
FUNC_ARCSIN = 47
FUNC_ARCCOS = 48
FUNC_ARCTAN = 49
FUNC_ARCCSC = 50
FUNC_ARCSEC = 51
FUNC_ARCCOT = 52
FUNC_SINH = 53
FUNC_COSH = 54
FUNC_TANH = 55
FUNC_ARSINH = 56
FUNC_ARCOSH = 57
FUNC_ARTANH = 58
L_FLOOR = 59
R_FLOOR = 60
L_CEIL = 61
R_CEIL = 62
FUNC_SQRT = 63
FUNC_OVERLINE = 64
CMD_TIMES = 65
CMD_CDOT = 66
CMD_DIV = 67
CMD_FRAC = 68
CMD_BINOM = 69
CMD_DBINOM = 70
CMD_TBINOM = 71
CMD_MATHIT = 72
UNDERSCORE = 73
CARET = 74
COLON = 75
DIFFERENTIAL = 76
LETTER = 77
DIGIT = 78
EQUAL = 79
NEQ = 80
LT = 81
LTE = 82
LTE_Q = 83
LTE_S = 84
GT = 85
GTE = 86
GTE_Q = 87
GTE_S = 88
BANG = 89
SINGLE_QUOTES = 90
SYMBOL = 91
channelNames = [ u"DEFAULT_TOKEN_CHANNEL", u"HIDDEN" ]
modeNames = [ "DEFAULT_MODE" ]
literalNames = [ "<INVALID>",
"','", "'.'", "'\\quad'", "'\\qquad'", "'\\negmedspace'", "'\\negthickspace'",
"'\\left'", "'\\right'", "'+'", "'-'", "'*'", "'/'", "'('",
"')'", "'{'", "'}'", "'\\{'", "'\\}'", "'['", "']'", "'|'",
"'\\right|'", "'\\left|'", "'\\langle'", "'\\rangle'", "'\\lim'",
"'\\sum'", "'\\prod'", "'\\exp'", "'\\log'", "'\\lg'", "'\\ln'",
"'\\sin'", "'\\cos'", "'\\tan'", "'\\csc'", "'\\sec'", "'\\cot'",
"'\\arcsin'", "'\\arccos'", "'\\arctan'", "'\\arccsc'", "'\\arcsec'",
"'\\arccot'", "'\\sinh'", "'\\cosh'", "'\\tanh'", "'\\arsinh'",
"'\\arcosh'", "'\\artanh'", "'\\lfloor'", "'\\rfloor'", "'\\lceil'",
"'\\rceil'", "'\\sqrt'", "'\\overline'", "'\\times'", "'\\cdot'",
"'\\div'", "'\\binom'", "'\\dbinom'", "'\\tbinom'", "'\\mathit'",
"'_'", "'^'", "':'", "'\\neq'", "'<'", "'\\leqq'", "'\\leqslant'",
"'>'", "'\\geqq'", "'\\geqslant'", "'!'" ]
symbolicNames = [ "<INVALID>",
"WS", "THINSPACE", "MEDSPACE", "THICKSPACE", "QUAD", "QQUAD",
"NEGTHINSPACE", "NEGMEDSPACE", "NEGTHICKSPACE", "CMD_LEFT",
"CMD_RIGHT", "IGNORE", "ADD", "SUB", "MUL", "DIV", "L_PAREN",
"R_PAREN", "L_BRACE", "R_BRACE", "L_BRACE_LITERAL", "R_BRACE_LITERAL",
"L_BRACKET", "R_BRACKET", "BAR", "R_BAR", "L_BAR", "L_ANGLE",
"R_ANGLE", "FUNC_LIM", "LIM_APPROACH_SYM", "FUNC_INT", "FUNC_SUM",
"FUNC_PROD", "FUNC_EXP", "FUNC_LOG", "FUNC_LG", "FUNC_LN", "FUNC_SIN",
"FUNC_COS", "FUNC_TAN", "FUNC_CSC", "FUNC_SEC", "FUNC_COT",
"FUNC_ARCSIN", "FUNC_ARCCOS", "FUNC_ARCTAN", "FUNC_ARCCSC",
"FUNC_ARCSEC", "FUNC_ARCCOT", "FUNC_SINH", "FUNC_COSH", "FUNC_TANH",
"FUNC_ARSINH", "FUNC_ARCOSH", "FUNC_ARTANH", "L_FLOOR", "R_FLOOR",
"L_CEIL", "R_CEIL", "FUNC_SQRT", "FUNC_OVERLINE", "CMD_TIMES",
"CMD_CDOT", "CMD_DIV", "CMD_FRAC", "CMD_BINOM", "CMD_DBINOM",
"CMD_TBINOM", "CMD_MATHIT", "UNDERSCORE", "CARET", "COLON",
"DIFFERENTIAL", "LETTER", "DIGIT", "EQUAL", "NEQ", "LT", "LTE",
"LTE_Q", "LTE_S", "GT", "GTE", "GTE_Q", "GTE_S", "BANG", "SINGLE_QUOTES",
"SYMBOL" ]
ruleNames = [ "T__0", "T__1", "WS", "THINSPACE", "MEDSPACE", "THICKSPACE",
"QUAD", "QQUAD", "NEGTHINSPACE", "NEGMEDSPACE", "NEGTHICKSPACE",
"CMD_LEFT", "CMD_RIGHT", "IGNORE", "ADD", "SUB", "MUL",
"DIV", "L_PAREN", "R_PAREN", "L_BRACE", "R_BRACE", "L_BRACE_LITERAL",
"R_BRACE_LITERAL", "L_BRACKET", "R_BRACKET", "BAR", "R_BAR",
"L_BAR", "L_ANGLE", "R_ANGLE", "FUNC_LIM", "LIM_APPROACH_SYM",
"FUNC_INT", "FUNC_SUM", "FUNC_PROD", "FUNC_EXP", "FUNC_LOG",
"FUNC_LG", "FUNC_LN", "FUNC_SIN", "FUNC_COS", "FUNC_TAN",
"FUNC_CSC", "FUNC_SEC", "FUNC_COT", "FUNC_ARCSIN", "FUNC_ARCCOS",
"FUNC_ARCTAN", "FUNC_ARCCSC", "FUNC_ARCSEC", "FUNC_ARCCOT",
"FUNC_SINH", "FUNC_COSH", "FUNC_TANH", "FUNC_ARSINH",
"FUNC_ARCOSH", "FUNC_ARTANH", "L_FLOOR", "R_FLOOR", "L_CEIL",
"R_CEIL", "FUNC_SQRT", "FUNC_OVERLINE", "CMD_TIMES", "CMD_CDOT",
"CMD_DIV", "CMD_FRAC", "CMD_BINOM", "CMD_DBINOM", "CMD_TBINOM",
"CMD_MATHIT", "UNDERSCORE", "CARET", "COLON", "WS_CHAR",
"DIFFERENTIAL", "LETTER", "DIGIT", "EQUAL", "NEQ", "LT",
"LTE", "LTE_Q", "LTE_S", "GT", "GTE", "GTE_Q", "GTE_S",
"BANG", "SINGLE_QUOTES", "SYMBOL" ]
grammarFileName = "LaTeX.g4"
def __init__(self, input=None, output:TextIO = sys.stdout):
super().__init__(input, output)
self.checkVersion("4.11.1")
self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache())
self._actions = None
self._predicates = None

View File

@@ -0,0 +1,91 @@
import os
import subprocess
import glob
from sympy.utilities.misc import debug
here = os.path.dirname(__file__)
grammar_file = os.path.abspath(os.path.join(here, "LaTeX.g4"))
dir_latex_antlr = os.path.join(here, "_antlr")
header = '''\
# *** GENERATED BY `setup.py antlr`, DO NOT EDIT BY HAND ***
#
# Generated from ../LaTeX.g4, derived from latex2sympy
# latex2sympy is licensed under the MIT license
# https://github.com/augustt198/latex2sympy/blob/master/LICENSE.txt
#
# Generated with antlr4
# antlr4 is licensed under the BSD-3-Clause License
# https://github.com/antlr/antlr4/blob/master/LICENSE.txt
'''
def check_antlr_version():
debug("Checking antlr4 version...")
try:
debug(subprocess.check_output(["antlr4"])
.decode('utf-8').split("\n")[0])
return True
except (subprocess.CalledProcessError, FileNotFoundError):
debug("The 'antlr4' command line tool is not installed, "
"or not on your PATH.\n"
"> Please refer to the README.md file for more information.")
return False
def build_parser(output_dir=dir_latex_antlr):
check_antlr_version()
debug("Updating ANTLR-generated code in {}".format(output_dir))
if not os.path.exists(output_dir):
os.makedirs(output_dir)
with open(os.path.join(output_dir, "__init__.py"), "w+") as fp:
fp.write(header)
args = [
"antlr4",
grammar_file,
"-o", output_dir,
# for now, not generating these as latex2sympy did not use them
"-no-visitor",
"-no-listener",
]
debug("Running code generation...\n\t$ {}".format(" ".join(args)))
subprocess.check_output(args, cwd=output_dir)
debug("Applying headers, removing unnecessary files and renaming...")
# Handle case insensitive file systems. If the files are already
# generated, they will be written to latex* but LaTeX*.* won't match them.
for path in (glob.glob(os.path.join(output_dir, "LaTeX*.*")) or
glob.glob(os.path.join(output_dir, "latex*.*"))):
# Remove files ending in .interp or .tokens as they are not needed.
if not path.endswith(".py"):
os.unlink(path)
continue
new_path = os.path.join(output_dir, os.path.basename(path).lower())
with open(path, 'r') as f:
lines = [line.rstrip() + '\n' for line in f]
os.unlink(path)
with open(new_path, "w") as out_file:
offset = 0
while lines[offset].startswith('#'):
offset += 1
out_file.write(header)
out_file.writelines(lines[offset:])
debug("\t{}".format(new_path))
return True
if __name__ == "__main__":
build_parser()

View File

@@ -0,0 +1,607 @@
# Ported from latex2sympy by @augustt198
# https://github.com/augustt198/latex2sympy
# See license in LICENSE.txt
from importlib.metadata import version
import sympy
from sympy.external import import_module
from sympy.printing.str import StrPrinter
from sympy.physics.quantum.state import Bra, Ket
from .errors import LaTeXParsingError
LaTeXParser = LaTeXLexer = MathErrorListener = None
try:
LaTeXParser = import_module('sympy.parsing.latex._antlr.latexparser',
import_kwargs={'fromlist': ['LaTeXParser']}).LaTeXParser
LaTeXLexer = import_module('sympy.parsing.latex._antlr.latexlexer',
import_kwargs={'fromlist': ['LaTeXLexer']}).LaTeXLexer
except Exception:
pass
ErrorListener = import_module('antlr4.error.ErrorListener',
warn_not_installed=True,
import_kwargs={'fromlist': ['ErrorListener']}
)
if ErrorListener:
class MathErrorListener(ErrorListener.ErrorListener): # type:ignore # noqa:F811
def __init__(self, src):
super(ErrorListener.ErrorListener, self).__init__()
self.src = src
def syntaxError(self, recog, symbol, line, col, msg, e):
fmt = "%s\n%s\n%s"
marker = "~" * col + "^"
if msg.startswith("missing"):
err = fmt % (msg, self.src, marker)
elif msg.startswith("no viable"):
err = fmt % ("I expected something else here", self.src, marker)
elif msg.startswith("mismatched"):
names = LaTeXParser.literalNames
expected = [
names[i] for i in e.getExpectedTokens() if i < len(names)
]
if len(expected) < 10:
expected = " ".join(expected)
err = (fmt % ("I expected one of these: " + expected, self.src,
marker))
else:
err = (fmt % ("I expected something else here", self.src,
marker))
else:
err = fmt % ("I don't understand this", self.src, marker)
raise LaTeXParsingError(err)
def parse_latex(sympy, strict=False):
antlr4 = import_module('antlr4')
if None in [antlr4, MathErrorListener] or \
not version('antlr4-python3-runtime').startswith('4.11'):
raise ImportError("LaTeX parsing requires the antlr4 Python package,"
" provided by pip (antlr4-python3-runtime) or"
" conda (antlr-python-runtime), version 4.11")
sympy = sympy.strip()
matherror = MathErrorListener(sympy)
stream = antlr4.InputStream(sympy)
lex = LaTeXLexer(stream)
lex.removeErrorListeners()
lex.addErrorListener(matherror)
tokens = antlr4.CommonTokenStream(lex)
parser = LaTeXParser(tokens)
# remove default console error listener
parser.removeErrorListeners()
parser.addErrorListener(matherror)
relation = parser.math().relation()
if strict and (relation.start.start != 0 or relation.stop.stop != len(sympy) - 1):
raise LaTeXParsingError("Invalid LaTeX")
expr = convert_relation(relation)
return expr
def convert_relation(rel):
if rel.expr():
return convert_expr(rel.expr())
lh = convert_relation(rel.relation(0))
rh = convert_relation(rel.relation(1))
if rel.LT():
return sympy.StrictLessThan(lh, rh)
elif rel.LTE():
return sympy.LessThan(lh, rh)
elif rel.GT():
return sympy.StrictGreaterThan(lh, rh)
elif rel.GTE():
return sympy.GreaterThan(lh, rh)
elif rel.EQUAL():
return sympy.Eq(lh, rh)
elif rel.NEQ():
return sympy.Ne(lh, rh)
def convert_expr(expr):
return convert_add(expr.additive())
def convert_add(add):
if add.ADD():
lh = convert_add(add.additive(0))
rh = convert_add(add.additive(1))
return sympy.Add(lh, rh, evaluate=False)
elif add.SUB():
lh = convert_add(add.additive(0))
rh = convert_add(add.additive(1))
if hasattr(rh, "is_Atom") and rh.is_Atom:
return sympy.Add(lh, -1 * rh, evaluate=False)
return sympy.Add(lh, sympy.Mul(-1, rh, evaluate=False), evaluate=False)
else:
return convert_mp(add.mp())
def convert_mp(mp):
if hasattr(mp, 'mp'):
mp_left = mp.mp(0)
mp_right = mp.mp(1)
else:
mp_left = mp.mp_nofunc(0)
mp_right = mp.mp_nofunc(1)
if mp.MUL() or mp.CMD_TIMES() or mp.CMD_CDOT():
lh = convert_mp(mp_left)
rh = convert_mp(mp_right)
return sympy.Mul(lh, rh, evaluate=False)
elif mp.DIV() or mp.CMD_DIV() or mp.COLON():
lh = convert_mp(mp_left)
rh = convert_mp(mp_right)
return sympy.Mul(lh, sympy.Pow(rh, -1, evaluate=False), evaluate=False)
else:
if hasattr(mp, 'unary'):
return convert_unary(mp.unary())
else:
return convert_unary(mp.unary_nofunc())
def convert_unary(unary):
if hasattr(unary, 'unary'):
nested_unary = unary.unary()
else:
nested_unary = unary.unary_nofunc()
if hasattr(unary, 'postfix_nofunc'):
first = unary.postfix()
tail = unary.postfix_nofunc()
postfix = [first] + tail
else:
postfix = unary.postfix()
if unary.ADD():
return convert_unary(nested_unary)
elif unary.SUB():
numabs = convert_unary(nested_unary)
# Use Integer(-n) instead of Mul(-1, n)
return -numabs
elif postfix:
return convert_postfix_list(postfix)
def convert_postfix_list(arr, i=0):
if i >= len(arr):
raise LaTeXParsingError("Index out of bounds")
res = convert_postfix(arr[i])
if isinstance(res, sympy.Expr):
if i == len(arr) - 1:
return res # nothing to multiply by
else:
if i > 0:
left = convert_postfix(arr[i - 1])
right = convert_postfix(arr[i + 1])
if isinstance(left, sympy.Expr) and isinstance(
right, sympy.Expr):
left_syms = convert_postfix(arr[i - 1]).atoms(sympy.Symbol)
right_syms = convert_postfix(arr[i + 1]).atoms(
sympy.Symbol)
# if the left and right sides contain no variables and the
# symbol in between is 'x', treat as multiplication.
if not (left_syms or right_syms) and str(res) == 'x':
return convert_postfix_list(arr, i + 1)
# multiply by next
return sympy.Mul(
res, convert_postfix_list(arr, i + 1), evaluate=False)
else: # must be derivative
wrt = res[0]
if i == len(arr) - 1:
raise LaTeXParsingError("Expected expression for derivative")
else:
expr = convert_postfix_list(arr, i + 1)
return sympy.Derivative(expr, wrt)
def do_subs(expr, at):
if at.expr():
at_expr = convert_expr(at.expr())
syms = at_expr.atoms(sympy.Symbol)
if len(syms) == 0:
return expr
elif len(syms) > 0:
sym = next(iter(syms))
return expr.subs(sym, at_expr)
elif at.equality():
lh = convert_expr(at.equality().expr(0))
rh = convert_expr(at.equality().expr(1))
return expr.subs(lh, rh)
def convert_postfix(postfix):
if hasattr(postfix, 'exp'):
exp_nested = postfix.exp()
else:
exp_nested = postfix.exp_nofunc()
exp = convert_exp(exp_nested)
for op in postfix.postfix_op():
if op.BANG():
if isinstance(exp, list):
raise LaTeXParsingError("Cannot apply postfix to derivative")
exp = sympy.factorial(exp, evaluate=False)
elif op.eval_at():
ev = op.eval_at()
at_b = None
at_a = None
if ev.eval_at_sup():
at_b = do_subs(exp, ev.eval_at_sup())
if ev.eval_at_sub():
at_a = do_subs(exp, ev.eval_at_sub())
if at_b is not None and at_a is not None:
exp = sympy.Add(at_b, -1 * at_a, evaluate=False)
elif at_b is not None:
exp = at_b
elif at_a is not None:
exp = at_a
return exp
def convert_exp(exp):
if hasattr(exp, 'exp'):
exp_nested = exp.exp()
else:
exp_nested = exp.exp_nofunc()
if exp_nested:
base = convert_exp(exp_nested)
if isinstance(base, list):
raise LaTeXParsingError("Cannot raise derivative to power")
if exp.atom():
exponent = convert_atom(exp.atom())
elif exp.expr():
exponent = convert_expr(exp.expr())
return sympy.Pow(base, exponent, evaluate=False)
else:
if hasattr(exp, 'comp'):
return convert_comp(exp.comp())
else:
return convert_comp(exp.comp_nofunc())
def convert_comp(comp):
if comp.group():
return convert_expr(comp.group().expr())
elif comp.abs_group():
return sympy.Abs(convert_expr(comp.abs_group().expr()), evaluate=False)
elif comp.atom():
return convert_atom(comp.atom())
elif comp.floor():
return convert_floor(comp.floor())
elif comp.ceil():
return convert_ceil(comp.ceil())
elif comp.func():
return convert_func(comp.func())
def convert_atom(atom):
if atom.LETTER():
sname = atom.LETTER().getText()
if atom.subexpr():
if atom.subexpr().expr(): # subscript is expr
subscript = convert_expr(atom.subexpr().expr())
else: # subscript is atom
subscript = convert_atom(atom.subexpr().atom())
sname += '_{' + StrPrinter().doprint(subscript) + '}'
if atom.SINGLE_QUOTES():
sname += atom.SINGLE_QUOTES().getText() # put after subscript for easy identify
return sympy.Symbol(sname)
elif atom.SYMBOL():
s = atom.SYMBOL().getText()[1:]
if s == "infty":
return sympy.oo
else:
if atom.subexpr():
subscript = None
if atom.subexpr().expr(): # subscript is expr
subscript = convert_expr(atom.subexpr().expr())
else: # subscript is atom
subscript = convert_atom(atom.subexpr().atom())
subscriptName = StrPrinter().doprint(subscript)
s += '_{' + subscriptName + '}'
return sympy.Symbol(s)
elif atom.number():
s = atom.number().getText().replace(",", "")
return sympy.Number(s)
elif atom.DIFFERENTIAL():
var = get_differential_var(atom.DIFFERENTIAL())
return sympy.Symbol('d' + var.name)
elif atom.mathit():
text = rule2text(atom.mathit().mathit_text())
return sympy.Symbol(text)
elif atom.frac():
return convert_frac(atom.frac())
elif atom.binom():
return convert_binom(atom.binom())
elif atom.bra():
val = convert_expr(atom.bra().expr())
return Bra(val)
elif atom.ket():
val = convert_expr(atom.ket().expr())
return Ket(val)
def rule2text(ctx):
stream = ctx.start.getInputStream()
# starting index of starting token
startIdx = ctx.start.start
# stopping index of stopping token
stopIdx = ctx.stop.stop
return stream.getText(startIdx, stopIdx)
def convert_frac(frac):
diff_op = False
partial_op = False
if frac.lower and frac.upper:
lower_itv = frac.lower.getSourceInterval()
lower_itv_len = lower_itv[1] - lower_itv[0] + 1
if (frac.lower.start == frac.lower.stop
and frac.lower.start.type == LaTeXLexer.DIFFERENTIAL):
wrt = get_differential_var_str(frac.lower.start.text)
diff_op = True
elif (lower_itv_len == 2 and frac.lower.start.type == LaTeXLexer.SYMBOL
and frac.lower.start.text == '\\partial'
and (frac.lower.stop.type == LaTeXLexer.LETTER
or frac.lower.stop.type == LaTeXLexer.SYMBOL)):
partial_op = True
wrt = frac.lower.stop.text
if frac.lower.stop.type == LaTeXLexer.SYMBOL:
wrt = wrt[1:]
if diff_op or partial_op:
wrt = sympy.Symbol(wrt)
if (diff_op and frac.upper.start == frac.upper.stop
and frac.upper.start.type == LaTeXLexer.LETTER
and frac.upper.start.text == 'd'):
return [wrt]
elif (partial_op and frac.upper.start == frac.upper.stop
and frac.upper.start.type == LaTeXLexer.SYMBOL
and frac.upper.start.text == '\\partial'):
return [wrt]
upper_text = rule2text(frac.upper)
expr_top = None
if diff_op and upper_text.startswith('d'):
expr_top = parse_latex(upper_text[1:])
elif partial_op and frac.upper.start.text == '\\partial':
expr_top = parse_latex(upper_text[len('\\partial'):])
if expr_top:
return sympy.Derivative(expr_top, wrt)
if frac.upper:
expr_top = convert_expr(frac.upper)
else:
expr_top = sympy.Number(frac.upperd.text)
if frac.lower:
expr_bot = convert_expr(frac.lower)
else:
expr_bot = sympy.Number(frac.lowerd.text)
inverse_denom = sympy.Pow(expr_bot, -1, evaluate=False)
if expr_top == 1:
return inverse_denom
else:
return sympy.Mul(expr_top, inverse_denom, evaluate=False)
def convert_binom(binom):
expr_n = convert_expr(binom.n)
expr_k = convert_expr(binom.k)
return sympy.binomial(expr_n, expr_k, evaluate=False)
def convert_floor(floor):
val = convert_expr(floor.val)
return sympy.floor(val, evaluate=False)
def convert_ceil(ceil):
val = convert_expr(ceil.val)
return sympy.ceiling(val, evaluate=False)
def convert_func(func):
if func.func_normal():
if func.L_PAREN(): # function called with parenthesis
arg = convert_func_arg(func.func_arg())
else:
arg = convert_func_arg(func.func_arg_noparens())
name = func.func_normal().start.text[1:]
# change arc<trig> -> a<trig>
if name in [
"arcsin", "arccos", "arctan", "arccsc", "arcsec", "arccot"
]:
name = "a" + name[3:]
expr = getattr(sympy.functions, name)(arg, evaluate=False)
if name in ["arsinh", "arcosh", "artanh"]:
name = "a" + name[2:]
expr = getattr(sympy.functions, name)(arg, evaluate=False)
if name == "exp":
expr = sympy.exp(arg, evaluate=False)
if name in ("log", "lg", "ln"):
if func.subexpr():
if func.subexpr().expr():
base = convert_expr(func.subexpr().expr())
else:
base = convert_atom(func.subexpr().atom())
elif name == "lg": # ISO 80000-2:2019
base = 10
elif name in ("ln", "log"): # SymPy's latex printer prints ln as log by default
base = sympy.E
expr = sympy.log(arg, base, evaluate=False)
func_pow = None
should_pow = True
if func.supexpr():
if func.supexpr().expr():
func_pow = convert_expr(func.supexpr().expr())
else:
func_pow = convert_atom(func.supexpr().atom())
if name in [
"sin", "cos", "tan", "csc", "sec", "cot", "sinh", "cosh",
"tanh"
]:
if func_pow == -1:
name = "a" + name
should_pow = False
expr = getattr(sympy.functions, name)(arg, evaluate=False)
if func_pow and should_pow:
expr = sympy.Pow(expr, func_pow, evaluate=False)
return expr
elif func.LETTER() or func.SYMBOL():
if func.LETTER():
fname = func.LETTER().getText()
elif func.SYMBOL():
fname = func.SYMBOL().getText()[1:]
fname = str(fname) # can't be unicode
if func.subexpr():
if func.subexpr().expr(): # subscript is expr
subscript = convert_expr(func.subexpr().expr())
else: # subscript is atom
subscript = convert_atom(func.subexpr().atom())
subscriptName = StrPrinter().doprint(subscript)
fname += '_{' + subscriptName + '}'
if func.SINGLE_QUOTES():
fname += func.SINGLE_QUOTES().getText()
input_args = func.args()
output_args = []
while input_args.args(): # handle multiple arguments to function
output_args.append(convert_expr(input_args.expr()))
input_args = input_args.args()
output_args.append(convert_expr(input_args.expr()))
return sympy.Function(fname)(*output_args)
elif func.FUNC_INT():
return handle_integral(func)
elif func.FUNC_SQRT():
expr = convert_expr(func.base)
if func.root:
r = convert_expr(func.root)
return sympy.root(expr, r, evaluate=False)
else:
return sympy.sqrt(expr, evaluate=False)
elif func.FUNC_OVERLINE():
expr = convert_expr(func.base)
return sympy.conjugate(expr, evaluate=False)
elif func.FUNC_SUM():
return handle_sum_or_prod(func, "summation")
elif func.FUNC_PROD():
return handle_sum_or_prod(func, "product")
elif func.FUNC_LIM():
return handle_limit(func)
def convert_func_arg(arg):
if hasattr(arg, 'expr'):
return convert_expr(arg.expr())
else:
return convert_mp(arg.mp_nofunc())
def handle_integral(func):
if func.additive():
integrand = convert_add(func.additive())
elif func.frac():
integrand = convert_frac(func.frac())
else:
integrand = 1
int_var = None
if func.DIFFERENTIAL():
int_var = get_differential_var(func.DIFFERENTIAL())
else:
for sym in integrand.atoms(sympy.Symbol):
s = str(sym)
if len(s) > 1 and s[0] == 'd':
if s[1] == '\\':
int_var = sympy.Symbol(s[2:])
else:
int_var = sympy.Symbol(s[1:])
int_sym = sym
if int_var:
integrand = integrand.subs(int_sym, 1)
else:
# Assume dx by default
int_var = sympy.Symbol('x')
if func.subexpr():
if func.subexpr().atom():
lower = convert_atom(func.subexpr().atom())
else:
lower = convert_expr(func.subexpr().expr())
if func.supexpr().atom():
upper = convert_atom(func.supexpr().atom())
else:
upper = convert_expr(func.supexpr().expr())
return sympy.Integral(integrand, (int_var, lower, upper))
else:
return sympy.Integral(integrand, int_var)
def handle_sum_or_prod(func, name):
val = convert_mp(func.mp())
iter_var = convert_expr(func.subeq().equality().expr(0))
start = convert_expr(func.subeq().equality().expr(1))
if func.supexpr().expr(): # ^{expr}
end = convert_expr(func.supexpr().expr())
else: # ^atom
end = convert_atom(func.supexpr().atom())
if name == "summation":
return sympy.Sum(val, (iter_var, start, end))
elif name == "product":
return sympy.Product(val, (iter_var, start, end))
def handle_limit(func):
sub = func.limit_sub()
if sub.LETTER():
var = sympy.Symbol(sub.LETTER().getText())
elif sub.SYMBOL():
var = sympy.Symbol(sub.SYMBOL().getText()[1:])
else:
var = sympy.Symbol('x')
if sub.SUB():
direction = "-"
elif sub.ADD():
direction = "+"
else:
direction = "+-"
approaching = convert_expr(sub.expr())
content = convert_mp(func.mp())
return sympy.Limit(content, var, approaching, direction)
def get_differential_var(d):
text = get_differential_var_str(d.getText())
return sympy.Symbol(text)
def get_differential_var_str(text):
for i in range(1, len(text)):
c = text[i]
if not (c == " " or c == "\r" or c == "\n" or c == "\t"):
idx = i
break
text = text[idx:]
if text[0] == "\\":
text = text[1:]
return text

View File

@@ -0,0 +1,2 @@
class LaTeXParsingError(Exception):
pass

View File

@@ -0,0 +1,2 @@
from .latex_parser import parse_latex_lark, LarkLaTeXParser # noqa
from .transformer import TransformToSymPyExpr # noqa

View File

@@ -0,0 +1,28 @@
// Greek symbols
// TODO: Shouold we include the uppercase variants for the symbols where the uppercase variant doesn't have a separate meaning?
ALPHA: "\\alpha"
BETA: "\\beta"
GAMMA: "\\gamma"
DELTA: "\\delta" // TODO: Should this be included? Delta usually denotes other things.
EPSILON: "\\epsilon" | "\\varepsilon"
ZETA: "\\zeta"
ETA: "\\eta"
THETA: "\\theta" | "\\vartheta"
// TODO: Should I add iota to the list?
KAPPA: "\\kappa"
LAMBDA: "\\lambda" // TODO: What about the uppercase variant?
MU: "\\mu"
NU: "\\nu"
XI: "\\xi"
// TODO: Should there be a separate note for transforming \pi into sympy.pi?
RHO: "\\rho" | "\\varrho"
// TODO: What should we do about sigma?
TAU: "\\tau"
UPSILON: "\\upsilon"
PHI: "\\phi" | "\\varphi"
CHI: "\\chi"
PSI: "\\psi"
OMEGA: "\\omega"
GREEK_SYMBOL: ALPHA | BETA | GAMMA | DELTA | EPSILON | ZETA | ETA | THETA | KAPPA
| LAMBDA | MU | NU | XI | RHO | TAU | UPSILON | PHI | CHI | PSI | OMEGA

View File

@@ -0,0 +1,403 @@
%ignore /[ \t\n\r]+/
%ignore "\\," | "\\thinspace" | "\\:" | "\\medspace" | "\\;" | "\\thickspace"
%ignore "\\quad" | "\\qquad"
%ignore "\\!" | "\\negthinspace" | "\\negmedspace" | "\\negthickspace"
%ignore "\\vrule" | "\\vcenter" | "\\vbox" | "\\vskip" | "\\vspace" | "\\hfill"
%ignore "\\*" | "\\-" | "\\." | "\\/" | "\\(" | "\\="
%ignore "\\left" | "\\right"
%ignore "\\limits" | "\\nolimits"
%ignore "\\displaystyle"
///////////////////// tokens ///////////////////////
// basic binary operators
ADD: "+"
SUB: "-"
MUL: "*"
DIV: "/"
// tokens with distinct left and right symbols
L_BRACE: "{"
R_BRACE: "}"
L_BRACE_LITERAL: "\\{"
R_BRACE_LITERAL: "\\}"
L_BRACKET: "["
R_BRACKET: "]"
L_CEIL: "\\lceil"
R_CEIL: "\\rceil"
L_FLOOR: "\\lfloor"
R_FLOOR: "\\rfloor"
L_PAREN: "("
R_PAREN: ")"
// limit, integral, sum, and product symbols
FUNC_LIM: "\\lim"
LIM_APPROACH_SYM: "\\to" | "\\rightarrow" | "\\Rightarrow" | "\\longrightarrow" | "\\Longrightarrow"
FUNC_INT: "\\int" | "\\intop"
FUNC_SUM: "\\sum"
FUNC_PROD: "\\prod"
// common functions
FUNC_EXP: "\\exp"
FUNC_LOG: "\\log"
FUNC_LN: "\\ln"
FUNC_LG: "\\lg"
FUNC_MIN: "\\min"
FUNC_MAX: "\\max"
// trigonometric functions
FUNC_SIN: "\\sin"
FUNC_COS: "\\cos"
FUNC_TAN: "\\tan"
FUNC_CSC: "\\csc"
FUNC_SEC: "\\sec"
FUNC_COT: "\\cot"
// inverse trigonometric functions
FUNC_ARCSIN: "\\arcsin"
FUNC_ARCCOS: "\\arccos"
FUNC_ARCTAN: "\\arctan"
FUNC_ARCCSC: "\\arccsc"
FUNC_ARCSEC: "\\arcsec"
FUNC_ARCCOT: "\\arccot"
// hyperbolic trigonometric functions
FUNC_SINH: "\\sinh"
FUNC_COSH: "\\cosh"
FUNC_TANH: "\\tanh"
FUNC_ARSINH: "\\arsinh"
FUNC_ARCOSH: "\\arcosh"
FUNC_ARTANH: "\\artanh"
FUNC_SQRT: "\\sqrt"
// miscellaneous symbols
CMD_TIMES: "\\times"
CMD_CDOT: "\\cdot"
CMD_DIV: "\\div"
CMD_FRAC: "\\frac" | "\\dfrac" | "\\tfrac" | "\\nicefrac"
CMD_BINOM: "\\binom" | "\\dbinom" | "\\tbinom"
CMD_OVERLINE: "\\overline"
CMD_LANGLE: "\\langle"
CMD_RANGLE: "\\rangle"
CMD_MATHIT: "\\mathit"
CMD_INFTY: "\\infty"
BANG: "!"
BAR: "|"
CARET: "^"
COLON: ":"
UNDERSCORE: "_"
// relational symbols
EQUAL: "="
NOT_EQUAL: "\\neq" | "\\ne"
LT: "<"
LTE: "\\leq" | "\\le" | "\\leqslant"
GT: ">"
GTE: "\\geq" | "\\ge" | "\\geqslant"
DIV_SYMBOL: CMD_DIV | DIV
MUL_SYMBOL: MUL | CMD_TIMES | CMD_CDOT
%import .greek_symbols.GREEK_SYMBOL
UPRIGHT_DIFFERENTIAL_SYMBOL: "\\text{d}" | "\\mathrm{d}"
DIFFERENTIAL_SYMBOL: "d" | UPRIGHT_DIFFERENTIAL_SYMBOL
// disallow "d" as a variable name because we want to parse "d" as a differential symbol.
SYMBOL: /[a-zA-Z]'*/
GREEK_SYMBOL_WITH_PRIMES: GREEK_SYMBOL "'"*
LATIN_SYMBOL_WITH_LATIN_SUBSCRIPT: /([a-zA-Z]'*)_(([A-Za-z0-9]|[a-zA-Z]+)|\{([A-Za-z0-9]|[a-zA-Z]+'*)\})/
LATIN_SYMBOL_WITH_GREEK_SUBSCRIPT: /([a-zA-Z]'*)_/ GREEK_SYMBOL | /([a-zA-Z]'*)_/ L_BRACE GREEK_SYMBOL_WITH_PRIMES R_BRACE
// best to define the variant with braces like that instead of shoving it all into one case like in
// /([a-zA-Z])_/ L_BRACE? GREEK_SYMBOL R_BRACE? because then we can easily error out on input like
// r"h_{\theta"
GREEK_SYMBOL_WITH_LATIN_SUBSCRIPT: GREEK_SYMBOL_WITH_PRIMES /_(([A-Za-z0-9]|[a-zA-Z]+)|\{([A-Za-z0-9]|[a-zA-Z]+'*)\})/
GREEK_SYMBOL_WITH_GREEK_SUBSCRIPT: GREEK_SYMBOL_WITH_PRIMES /_/ (GREEK_SYMBOL | L_BRACE GREEK_SYMBOL_WITH_PRIMES R_BRACE)
MULTI_LETTER_SYMBOL: /[a-zA-Z]+(\s+[a-zA-Z]+)*'*/
%import common.DIGIT -> DIGIT
CMD_PRIME: "\\prime"
CMD_ASTERISK: "\\ast"
PRIMES: "'"+
STARS: "*"+
PRIMES_VIA_CMD: CMD_PRIME+
STARS_VIA_CMD: CMD_ASTERISK+
CMD_IMAGINARY_UNIT: "\\imaginaryunit"
CMD_BEGIN: "\\begin"
CMD_END: "\\end"
// matrices
IGNORE_L: /[ \t\n\r]*/ L_BRACE* /[ \t\n\r]*/
IGNORE_R: /[ \t\n\r]*/ R_BRACE* /[ \t\n\r]*/
ARRAY_MATRIX_BEGIN: L_BRACE "array" R_BRACE L_BRACE /[^}]*/ R_BRACE
ARRAY_MATRIX_END: L_BRACE "array" R_BRACE
AMSMATH_MATRIX: L_BRACE "matrix" R_BRACE
AMSMATH_PMATRIX: L_BRACE "pmatrix" R_BRACE
AMSMATH_BMATRIX: L_BRACE "bmatrix" R_BRACE
// Without the (L|R)_PARENs and (L|R)_BRACKETs, a matrix defined using
// \begin{array}...\end{array} or \begin{matrix}...\end{matrix} must
// not qualify as a complete matrix expression; this is done so that
// if we have \begin{array}...\end{array} or \begin{matrix}...\end{matrix}
// between BAR pairs, then they should be interpreted as determinants as
// opposed to sympy.Abs (absolute value) applied to a matrix.
CMD_BEGIN_AMSPMATRIX_AMSBMATRIX: CMD_BEGIN (AMSMATH_PMATRIX | AMSMATH_BMATRIX)
CMD_BEGIN_ARRAY_AMSMATRIX: (L_PAREN | L_BRACKET) IGNORE_L CMD_BEGIN (ARRAY_MATRIX_BEGIN | AMSMATH_MATRIX)
CMD_MATRIX_BEGIN: CMD_BEGIN_AMSPMATRIX_AMSBMATRIX | CMD_BEGIN_ARRAY_AMSMATRIX
CMD_END_AMSPMATRIX_AMSBMATRIX: CMD_END (AMSMATH_PMATRIX | AMSMATH_BMATRIX)
CMD_END_ARRAY_AMSMATRIX: CMD_END (ARRAY_MATRIX_END | AMSMATH_MATRIX) IGNORE_R "\\right"? (R_PAREN | R_BRACKET)
CMD_MATRIX_END: CMD_END_AMSPMATRIX_AMSBMATRIX | CMD_END_ARRAY_AMSMATRIX
MATRIX_COL_DELIM: "&"
MATRIX_ROW_DELIM: "\\\\"
FUNC_MATRIX_TRACE: "\\trace"
FUNC_MATRIX_ADJUGATE: "\\adjugate"
// determinants
AMSMATH_VMATRIX: L_BRACE "vmatrix" R_BRACE
CMD_DETERMINANT_BEGIN_SIMPLE: CMD_BEGIN AMSMATH_VMATRIX
CMD_DETERMINANT_BEGIN_VARIANT: BAR IGNORE_L CMD_BEGIN (ARRAY_MATRIX_BEGIN | AMSMATH_MATRIX)
CMD_DETERMINANT_BEGIN: CMD_DETERMINANT_BEGIN_SIMPLE | CMD_DETERMINANT_BEGIN_VARIANT
CMD_DETERMINANT_END_SIMPLE: CMD_END AMSMATH_VMATRIX
CMD_DETERMINANT_END_VARIANT: CMD_END (ARRAY_MATRIX_END | AMSMATH_MATRIX) IGNORE_R "\\right"? BAR
CMD_DETERMINANT_END: CMD_DETERMINANT_END_SIMPLE | CMD_DETERMINANT_END_VARIANT
FUNC_DETERMINANT: "\\det"
//////////////////// grammar //////////////////////
latex_string: _relation | _expression
_one_letter_symbol: SYMBOL
| LATIN_SYMBOL_WITH_LATIN_SUBSCRIPT
| LATIN_SYMBOL_WITH_GREEK_SUBSCRIPT
| GREEK_SYMBOL_WITH_LATIN_SUBSCRIPT
| GREEK_SYMBOL_WITH_GREEK_SUBSCRIPT
| GREEK_SYMBOL_WITH_PRIMES
// LuaTeX-generated outputs of \mathit{foo'} and \mathit{foo}'
// seem to be the same on the surface. We allow both styles.
multi_letter_symbol: CMD_MATHIT L_BRACE MULTI_LETTER_SYMBOL R_BRACE
| CMD_MATHIT L_BRACE MULTI_LETTER_SYMBOL R_BRACE /'+/
number: /\d+(\.\d*)?/ | CMD_IMAGINARY_UNIT
_atomic_expr: _one_letter_symbol
| multi_letter_symbol
| number
| CMD_INFTY
group_round_parentheses: L_PAREN _expression R_PAREN
group_square_brackets: L_BRACKET _expression R_BRACKET
group_curly_parentheses: L_BRACE _expression R_BRACE
_relation: eq | ne | lt | lte | gt | gte
eq: _expression EQUAL _expression
ne: _expression NOT_EQUAL _expression
lt: _expression LT _expression
lte: _expression LTE _expression
gt: _expression GT _expression
gte: _expression GTE _expression
_expression_core: _atomic_expr | group_curly_parentheses
add: _expression ADD _expression_mul
| ADD _expression_mul
sub: _expression SUB _expression_mul
| SUB _expression_mul
mul: _expression_mul MUL_SYMBOL _expression_power
div: _expression_mul DIV_SYMBOL _expression_power
adjacent_expressions: (_one_letter_symbol | number) _expression_mul
| group_round_parentheses (group_round_parentheses | _one_letter_symbol)
| _function _function
| fraction _expression_mul
_expression_func: _expression_core
| group_round_parentheses
| fraction
| binomial
| _function
| _integral// | derivative
| limit
| matrix
_expression_power: _expression_func | superscript | matrix_prime | symbol_prime
_expression_mul: _expression_power
| mul | div | adjacent_expressions
| summation | product
_expression: _expression_mul | add | sub
_limit_dir: "+" | "-" | L_BRACE ("+" | "-") R_BRACE
limit_dir_expr: _expression CARET _limit_dir
group_curly_parentheses_lim: L_BRACE _expression LIM_APPROACH_SYM (limit_dir_expr | _expression) R_BRACE
limit: FUNC_LIM UNDERSCORE group_curly_parentheses_lim _expression
differential: DIFFERENTIAL_SYMBOL _one_letter_symbol
//_derivative_operator: CMD_FRAC L_BRACE DIFFERENTIAL_SYMBOL R_BRACE L_BRACE differential R_BRACE
//derivative: _derivative_operator _expression
_integral: normal_integral | integral_with_special_fraction
normal_integral: FUNC_INT _expression DIFFERENTIAL_SYMBOL _one_letter_symbol
| FUNC_INT (CARET _expression_core UNDERSCORE _expression_core)? _expression? DIFFERENTIAL_SYMBOL _one_letter_symbol
| FUNC_INT (UNDERSCORE _expression_core CARET _expression_core)? _expression? DIFFERENTIAL_SYMBOL _one_letter_symbol
group_curly_parentheses_int: L_BRACE _expression? differential R_BRACE
special_fraction: CMD_FRAC group_curly_parentheses_int group_curly_parentheses
integral_with_special_fraction: FUNC_INT special_fraction
| FUNC_INT (CARET _expression_core UNDERSCORE _expression_core)? special_fraction
| FUNC_INT (UNDERSCORE _expression_core CARET _expression_core)? special_fraction
group_curly_parentheses_special: UNDERSCORE L_BRACE _atomic_expr EQUAL _atomic_expr R_BRACE CARET _expression_core
| CARET _expression_core UNDERSCORE L_BRACE _atomic_expr EQUAL _atomic_expr R_BRACE
summation: FUNC_SUM group_curly_parentheses_special _expression
| FUNC_SUM group_curly_parentheses_special _expression
product: FUNC_PROD group_curly_parentheses_special _expression
| FUNC_PROD group_curly_parentheses_special _expression
superscript: _expression_func CARET (_expression_power | CMD_PRIME | CMD_ASTERISK)
| _expression_func CARET L_BRACE (PRIMES | STARS | PRIMES_VIA_CMD | STARS_VIA_CMD) R_BRACE
matrix_prime: (matrix | group_round_parentheses) PRIMES
symbol_prime: (LATIN_SYMBOL_WITH_LATIN_SUBSCRIPT
| LATIN_SYMBOL_WITH_GREEK_SUBSCRIPT
| GREEK_SYMBOL_WITH_LATIN_SUBSCRIPT
| GREEK_SYMBOL_WITH_GREEK_SUBSCRIPT) PRIMES
fraction: _basic_fraction
| _simple_fraction
| _general_fraction
_basic_fraction: CMD_FRAC DIGIT (DIGIT | SYMBOL | GREEK_SYMBOL_WITH_PRIMES)
_simple_fraction: CMD_FRAC DIGIT group_curly_parentheses
| CMD_FRAC group_curly_parentheses (DIGIT | SYMBOL | GREEK_SYMBOL_WITH_PRIMES)
_general_fraction: CMD_FRAC group_curly_parentheses group_curly_parentheses
binomial: _basic_binomial
| _simple_binomial
| _general_binomial
_basic_binomial: CMD_BINOM DIGIT (DIGIT | SYMBOL | GREEK_SYMBOL_WITH_PRIMES)
_simple_binomial: CMD_BINOM DIGIT group_curly_parentheses
| CMD_BINOM group_curly_parentheses (DIGIT | SYMBOL | GREEK_SYMBOL_WITH_PRIMES)
_general_binomial: CMD_BINOM group_curly_parentheses group_curly_parentheses
list_of_expressions: _expression ("," _expression)*
function_applied: _one_letter_symbol L_PAREN list_of_expressions R_PAREN
min: FUNC_MIN L_PAREN list_of_expressions R_PAREN
max: FUNC_MAX L_PAREN list_of_expressions R_PAREN
bra: CMD_LANGLE _expression BAR
ket: BAR _expression CMD_RANGLE
inner_product: CMD_LANGLE _expression BAR _expression CMD_RANGLE
_function: function_applied
| abs | floor | ceil
| _trigonometric_function | _inverse_trigonometric_function
| _trigonometric_function_power
| _hyperbolic_trigonometric_function | _inverse_hyperbolic_trigonometric_function
| exponential
| log
| square_root
| factorial
| conjugate
| max | min
| bra | ket | inner_product
| determinant
| trace
| adjugate
exponential: FUNC_EXP _expression
log: FUNC_LOG _expression
| FUNC_LN _expression
| FUNC_LG _expression
| FUNC_LOG UNDERSCORE (DIGIT | _one_letter_symbol) _expression
| FUNC_LOG UNDERSCORE group_curly_parentheses _expression
square_root: FUNC_SQRT group_curly_parentheses
| FUNC_SQRT group_square_brackets group_curly_parentheses
factorial: _expression_func BANG
conjugate: CMD_OVERLINE group_curly_parentheses
| CMD_OVERLINE DIGIT
_trigonometric_function: sin | cos | tan | csc | sec | cot
sin: FUNC_SIN _expression
cos: FUNC_COS _expression
tan: FUNC_TAN _expression
csc: FUNC_CSC _expression
sec: FUNC_SEC _expression
cot: FUNC_COT _expression
_trigonometric_function_power: sin_power | cos_power | tan_power | csc_power | sec_power | cot_power
sin_power: FUNC_SIN CARET _expression_core _expression
cos_power: FUNC_COS CARET _expression_core _expression
tan_power: FUNC_TAN CARET _expression_core _expression
csc_power: FUNC_CSC CARET _expression_core _expression
sec_power: FUNC_SEC CARET _expression_core _expression
cot_power: FUNC_COT CARET _expression_core _expression
_hyperbolic_trigonometric_function: sinh | cosh | tanh
sinh: FUNC_SINH _expression
cosh: FUNC_COSH _expression
tanh: FUNC_TANH _expression
_inverse_trigonometric_function: arcsin | arccos | arctan | arccsc | arcsec | arccot
arcsin: FUNC_ARCSIN _expression
arccos: FUNC_ARCCOS _expression
arctan: FUNC_ARCTAN _expression
arccsc: FUNC_ARCCSC _expression
arcsec: FUNC_ARCSEC _expression
arccot: FUNC_ARCCOT _expression
_inverse_hyperbolic_trigonometric_function: asinh | acosh | atanh
asinh: FUNC_ARSINH _expression
acosh: FUNC_ARCOSH _expression
atanh: FUNC_ARTANH _expression
abs: BAR _expression BAR
floor: L_FLOOR _expression R_FLOOR
ceil: L_CEIL _expression R_CEIL
matrix: CMD_MATRIX_BEGIN matrix_body CMD_MATRIX_END
matrix_body: matrix_row (MATRIX_ROW_DELIM matrix_row)* (MATRIX_ROW_DELIM)?
matrix_row: _expression (MATRIX_COL_DELIM _expression)*
determinant: (CMD_DETERMINANT_BEGIN matrix_body CMD_DETERMINANT_END)
| FUNC_DETERMINANT _expression
trace: FUNC_MATRIX_TRACE _expression
adjugate: FUNC_MATRIX_ADJUGATE _expression

View File

@@ -0,0 +1,145 @@
import os
import logging
import re
from pathlib import Path
from sympy.external import import_module
from sympy.parsing.latex.lark.transformer import TransformToSymPyExpr
_lark = import_module("lark")
class LarkLaTeXParser:
r"""Class for converting input `\mathrm{\LaTeX}` strings into SymPy Expressions.
It holds all the necessary internal data for doing so, and exposes hooks for
customizing its behavior.
Parameters
==========
print_debug_output : bool, optional
If set to ``True``, prints debug output to the logger. Defaults to ``False``.
transform : bool, optional
If set to ``True``, the class runs the Transformer class on the parse tree
generated by running ``Lark.parse`` on the input string. Defaults to ``True``.
Setting it to ``False`` can help with debugging the `\mathrm{\LaTeX}` grammar.
grammar_file : str, optional
The path to the grammar file that the parser should use. If set to ``None``,
it uses the default grammar, which is in ``grammar/latex.lark``, relative to
the ``sympy/parsing/latex/lark/`` directory.
transformer : str, optional
The name of the Transformer class to use. If set to ``None``, it uses the
default transformer class, which is :py:func:`TransformToSymPyExpr`.
"""
def __init__(self, print_debug_output=False, transform=True, grammar_file=None, transformer=None):
grammar_dir_path = os.path.join(os.path.dirname(__file__), "grammar/")
if grammar_file is None:
latex_grammar = Path(os.path.join(grammar_dir_path, "latex.lark")).read_text(encoding="utf-8")
else:
latex_grammar = Path(grammar_file).read_text(encoding="utf-8")
self.parser = _lark.Lark(
latex_grammar,
source_path=grammar_dir_path,
parser="earley",
start="latex_string",
lexer="auto",
ambiguity="explicit",
propagate_positions=False,
maybe_placeholders=False,
keep_all_tokens=True)
self.print_debug_output = print_debug_output
self.transform_expr = transform
if transformer is None:
self.transformer = TransformToSymPyExpr()
else:
self.transformer = transformer()
def doparse(self, s: str):
if self.print_debug_output:
_lark.logger.setLevel(logging.DEBUG)
parse_tree = self.parser.parse(s)
if not self.transform_expr:
# exit early and return the parse tree
_lark.logger.debug("expression = %s", s)
_lark.logger.debug(parse_tree)
_lark.logger.debug(parse_tree.pretty())
return parse_tree
if self.print_debug_output:
# print this stuff before attempting to run the transformer
_lark.logger.debug("expression = %s", s)
# print the `parse_tree` variable
_lark.logger.debug(parse_tree.pretty())
sympy_expression = self.transformer.transform(parse_tree)
if self.print_debug_output:
_lark.logger.debug("SymPy expression = %s", sympy_expression)
return sympy_expression
if _lark is not None:
_lark_latex_parser = LarkLaTeXParser()
def parse_latex_lark(s: str):
"""
Experimental LaTeX parser using Lark.
This function is still under development and its API may change with the
next releases of SymPy.
"""
if _lark is None:
raise ImportError("Lark is probably not installed")
return _lark_latex_parser.doparse(s)
def _pretty_print_lark_trees(tree, indent=0, show_expr=True):
if isinstance(tree, _lark.Token):
return tree.value
data = str(tree.data)
is_expr = data.startswith("expression")
if is_expr:
data = re.sub(r"^expression", "E", data)
is_ambig = (data == "_ambig")
if is_ambig:
new_indent = indent + 2
else:
new_indent = indent
output = ""
show_node = not is_expr or show_expr
if show_node:
output += str(data) + "("
if is_ambig:
output += "\n" + "\n".join([" " * new_indent + _pretty_print_lark_trees(i, new_indent, show_expr) for i in tree.children])
else:
output += ",".join([_pretty_print_lark_trees(i, new_indent, show_expr) for i in tree.children])
if show_node:
output += ")"
return output

View File

@@ -0,0 +1,730 @@
import re
import sympy
from sympy.external import import_module
from sympy.parsing.latex.errors import LaTeXParsingError
lark = import_module("lark")
if lark:
from lark import Transformer, Token, Tree # type: ignore
else:
class Transformer: # type: ignore
def transform(self, *args):
pass
class Token: # type: ignore
pass
class Tree: # type: ignore
pass
# noinspection PyPep8Naming,PyMethodMayBeStatic
class TransformToSymPyExpr(Transformer):
"""Returns a SymPy expression that is generated by traversing the ``lark.Tree``
passed to the ``.transform()`` function.
Notes
=====
**This class is never supposed to be used directly.**
In order to tweak the behavior of this class, it has to be subclassed and then after
the required modifications are made, the name of the new class should be passed to
the :py:class:`LarkLaTeXParser` class by using the ``transformer`` argument in the
constructor.
Parameters
==========
visit_tokens : bool, optional
For information about what this option does, see `here
<https://lark-parser.readthedocs.io/en/latest/visitors.html#lark.visitors.Transformer>`_.
Note that the option must be set to ``True`` for the default parser to work.
"""
SYMBOL = sympy.Symbol
DIGIT = sympy.core.numbers.Integer
def CMD_INFTY(self, tokens):
return sympy.oo
def GREEK_SYMBOL_WITH_PRIMES(self, tokens):
# we omit the first character because it is a backslash. Also, if the variable name has "var" in it,
# like "varphi" or "varepsilon", we remove that too
variable_name = re.sub("var", "", tokens[1:])
return sympy.Symbol(variable_name)
def LATIN_SYMBOL_WITH_LATIN_SUBSCRIPT(self, tokens):
base, sub = tokens.value.split("_")
if sub.startswith("{"):
return sympy.Symbol("%s_{%s}" % (base, sub[1:-1]))
else:
return sympy.Symbol("%s_{%s}" % (base, sub))
def GREEK_SYMBOL_WITH_LATIN_SUBSCRIPT(self, tokens):
base, sub = tokens.value.split("_")
greek_letter = re.sub("var", "", base[1:])
if sub.startswith("{"):
return sympy.Symbol("%s_{%s}" % (greek_letter, sub[1:-1]))
else:
return sympy.Symbol("%s_{%s}" % (greek_letter, sub))
def LATIN_SYMBOL_WITH_GREEK_SUBSCRIPT(self, tokens):
base, sub = tokens.value.split("_")
if sub.startswith("{"):
greek_letter = sub[2:-1]
else:
greek_letter = sub[1:]
greek_letter = re.sub("var", "", greek_letter)
return sympy.Symbol("%s_{%s}" % (base, greek_letter))
def GREEK_SYMBOL_WITH_GREEK_SUBSCRIPT(self, tokens):
base, sub = tokens.value.split("_")
greek_base = re.sub("var", "", base[1:])
if sub.startswith("{"):
greek_sub = sub[2:-1]
else:
greek_sub = sub[1:]
greek_sub = re.sub("var", "", greek_sub)
return sympy.Symbol("%s_{%s}" % (greek_base, greek_sub))
def multi_letter_symbol(self, tokens):
if len(tokens) == 4: # no primes (single quotes) on symbol
return sympy.Symbol(tokens[2])
if len(tokens) == 5: # there are primes on the symbol
return sympy.Symbol(tokens[2] + tokens[4])
def number(self, tokens):
if tokens[0].type == "CMD_IMAGINARY_UNIT":
return sympy.I
if "." in tokens[0]:
return sympy.core.numbers.Float(tokens[0])
else:
return sympy.core.numbers.Integer(tokens[0])
def latex_string(self, tokens):
return tokens[0]
def group_round_parentheses(self, tokens):
return tokens[1]
def group_square_brackets(self, tokens):
return tokens[1]
def group_curly_parentheses(self, tokens):
return tokens[1]
def eq(self, tokens):
return sympy.Eq(tokens[0], tokens[2])
def ne(self, tokens):
return sympy.Ne(tokens[0], tokens[2])
def lt(self, tokens):
return sympy.Lt(tokens[0], tokens[2])
def lte(self, tokens):
return sympy.Le(tokens[0], tokens[2])
def gt(self, tokens):
return sympy.Gt(tokens[0], tokens[2])
def gte(self, tokens):
return sympy.Ge(tokens[0], tokens[2])
def add(self, tokens):
if len(tokens) == 2: # +a
return tokens[1]
if len(tokens) == 3: # a + b
lh = tokens[0]
rh = tokens[2]
if self._obj_is_sympy_Matrix(lh) or self._obj_is_sympy_Matrix(rh):
return sympy.MatAdd(lh, rh)
return sympy.Add(lh, rh)
def sub(self, tokens):
if len(tokens) == 2: # -a
x = tokens[1]
if self._obj_is_sympy_Matrix(x):
return sympy.MatMul(-1, x)
return -x
if len(tokens) == 3: # a - b
lh = tokens[0]
rh = tokens[2]
if self._obj_is_sympy_Matrix(lh) or self._obj_is_sympy_Matrix(rh):
return sympy.MatAdd(lh, sympy.MatMul(-1, rh))
return sympy.Add(lh, -rh)
def mul(self, tokens):
lh = tokens[0]
rh = tokens[2]
if self._obj_is_sympy_Matrix(lh) or self._obj_is_sympy_Matrix(rh):
return sympy.MatMul(lh, rh)
return sympy.Mul(lh, rh)
def div(self, tokens):
return self._handle_division(tokens[0], tokens[2])
def adjacent_expressions(self, tokens):
# Most of the time, if two expressions are next to each other, it means implicit multiplication,
# but not always
from sympy.physics.quantum import Bra, Ket
if isinstance(tokens[0], Ket) and isinstance(tokens[1], Bra):
from sympy.physics.quantum import OuterProduct
return OuterProduct(tokens[0], tokens[1])
elif tokens[0] == sympy.Symbol("d"):
# If the leftmost token is a "d", then it is highly likely that this is a differential
return tokens[0], tokens[1]
elif isinstance(tokens[0], tuple):
# then we have a derivative
return sympy.Derivative(tokens[1], tokens[0][1])
else:
return sympy.Mul(tokens[0], tokens[1])
def superscript(self, tokens):
def isprime(x):
return isinstance(x, Token) and x.type == "PRIMES"
def iscmdprime(x):
return isinstance(x, Token) and (x.type == "PRIMES_VIA_CMD"
or x.type == "CMD_PRIME")
def isstar(x):
return isinstance(x, Token) and x.type == "STARS"
def iscmdstar(x):
return isinstance(x, Token) and (x.type == "STARS_VIA_CMD"
or x.type == "CMD_ASTERISK")
base = tokens[0]
if len(tokens) == 3: # a^b OR a^\prime OR a^\ast
sup = tokens[2]
if len(tokens) == 5:
# a^{'}, a^{''}, ... OR
# a^{*}, a^{**}, ... OR
# a^{\prime}, a^{\prime\prime}, ... OR
# a^{\ast}, a^{\ast\ast}, ...
sup = tokens[3]
if self._obj_is_sympy_Matrix(base):
if sup == sympy.Symbol("T"):
return sympy.Transpose(base)
if sup == sympy.Symbol("H"):
return sympy.adjoint(base)
if isprime(sup):
sup = sup.value
if len(sup) % 2 == 0:
return base
return sympy.Transpose(base)
if iscmdprime(sup):
sup = sup.value
if (len(sup)/len(r"\prime")) % 2 == 0:
return base
return sympy.Transpose(base)
if isstar(sup):
sup = sup.value
# need .doit() in order to be consistent with
# sympy.adjoint() which returns the evaluated adjoint
# of a matrix
if len(sup) % 2 == 0:
return base.doit()
return sympy.adjoint(base)
if iscmdstar(sup):
sup = sup.value
# need .doit() for same reason as above
if (len(sup)/len(r"\ast")) % 2 == 0:
return base.doit()
return sympy.adjoint(base)
if isprime(sup) or iscmdprime(sup) or isstar(sup) or iscmdstar(sup):
raise LaTeXParsingError(f"{base} with superscript {sup} is not understood.")
return sympy.Pow(base, sup)
def matrix_prime(self, tokens):
base = tokens[0]
primes = tokens[1].value
if not self._obj_is_sympy_Matrix(base):
raise LaTeXParsingError(f"({base}){primes} is not understood.")
if len(primes) % 2 == 0:
return base
return sympy.Transpose(base)
def symbol_prime(self, tokens):
base = tokens[0]
primes = tokens[1].value
return sympy.Symbol(f"{base.name}{primes}")
def fraction(self, tokens):
numerator = tokens[1]
if isinstance(tokens[2], tuple):
# we only need the variable w.r.t. which we are differentiating
_, variable = tokens[2]
# we will pass this information upwards
return "derivative", variable
else:
denominator = tokens[2]
return self._handle_division(numerator, denominator)
def binomial(self, tokens):
return sympy.binomial(tokens[1], tokens[2])
def normal_integral(self, tokens):
underscore_index = None
caret_index = None
if "_" in tokens:
# we need to know the index because the next item in the list is the
# arguments for the lower bound of the integral
underscore_index = tokens.index("_")
if "^" in tokens:
# we need to know the index because the next item in the list is the
# arguments for the upper bound of the integral
caret_index = tokens.index("^")
lower_bound = tokens[underscore_index + 1] if underscore_index else None
upper_bound = tokens[caret_index + 1] if caret_index else None
differential_symbol = self._extract_differential_symbol(tokens)
if differential_symbol is None:
raise LaTeXParsingError("Differential symbol was not found in the expression."
"Valid differential symbols are \"d\", \"\\text{d}, and \"\\mathrm{d}\".")
# else we can assume that a differential symbol was found
differential_variable_index = tokens.index(differential_symbol) + 1
differential_variable = tokens[differential_variable_index]
# we can't simply do something like `if (lower_bound and not upper_bound) ...` because this would
# evaluate to `True` if the `lower_bound` is 0 and upper bound is non-zero
if lower_bound is not None and upper_bound is None:
# then one was given and the other wasn't
raise LaTeXParsingError("Lower bound for the integral was found, but upper bound was not found.")
if upper_bound is not None and lower_bound is None:
# then one was given and the other wasn't
raise LaTeXParsingError("Upper bound for the integral was found, but lower bound was not found.")
# check if any expression was given or not. If it wasn't, then set the integrand to 1.
if underscore_index is not None and underscore_index == differential_variable_index - 3:
# The Token at differential_variable_index - 2 should be the integrand. However, if going one more step
# backwards after that gives us the underscore, then that means that there _was_ no integrand.
# Example: \int^7_0 dx
integrand = 1
elif caret_index is not None and caret_index == differential_variable_index - 3:
# The Token at differential_variable_index - 2 should be the integrand. However, if going one more step
# backwards after that gives us the caret, then that means that there _was_ no integrand.
# Example: \int_0^7 dx
integrand = 1
elif differential_variable_index == 2:
# this means we have something like "\int dx", because the "\int" symbol will always be
# at index 0 in `tokens`
integrand = 1
else:
# The Token at differential_variable_index - 1 is the differential symbol itself, so we need to go one
# more step before that.
integrand = tokens[differential_variable_index - 2]
if lower_bound is not None:
# then we have a definite integral
# we can assume that either both the lower and upper bounds are given, or
# neither of them are
return sympy.Integral(integrand, (differential_variable, lower_bound, upper_bound))
else:
# we have an indefinite integral
return sympy.Integral(integrand, differential_variable)
def group_curly_parentheses_int(self, tokens):
# return signature is a tuple consisting of the expression in the numerator, along with the variable of
# integration
if len(tokens) == 3:
return 1, tokens[1]
elif len(tokens) == 4:
return tokens[1], tokens[2]
# there are no other possibilities
def special_fraction(self, tokens):
numerator, variable = tokens[1]
denominator = tokens[2]
# We pass the integrand, along with information about the variable of integration, upw
return sympy.Mul(numerator, sympy.Pow(denominator, -1)), variable
def integral_with_special_fraction(self, tokens):
underscore_index = None
caret_index = None
if "_" in tokens:
# we need to know the index because the next item in the list is the
# arguments for the lower bound of the integral
underscore_index = tokens.index("_")
if "^" in tokens:
# we need to know the index because the next item in the list is the
# arguments for the upper bound of the integral
caret_index = tokens.index("^")
lower_bound = tokens[underscore_index + 1] if underscore_index else None
upper_bound = tokens[caret_index + 1] if caret_index else None
# we can't simply do something like `if (lower_bound and not upper_bound) ...` because this would
# evaluate to `True` if the `lower_bound` is 0 and upper bound is non-zero
if lower_bound is not None and upper_bound is None:
# then one was given and the other wasn't
raise LaTeXParsingError("Lower bound for the integral was found, but upper bound was not found.")
if upper_bound is not None and lower_bound is None:
# then one was given and the other wasn't
raise LaTeXParsingError("Upper bound for the integral was found, but lower bound was not found.")
integrand, differential_variable = tokens[-1]
if lower_bound is not None:
# then we have a definite integral
# we can assume that either both the lower and upper bounds are given, or
# neither of them are
return sympy.Integral(integrand, (differential_variable, lower_bound, upper_bound))
else:
# we have an indefinite integral
return sympy.Integral(integrand, differential_variable)
def group_curly_parentheses_special(self, tokens):
underscore_index = tokens.index("_")
caret_index = tokens.index("^")
# given the type of expressions we are parsing, we can assume that the lower limit
# will always use braces around its arguments. This is because we don't support
# converting unconstrained sums into SymPy expressions.
# first we isolate the bottom limit
left_brace_index = tokens.index("{", underscore_index)
right_brace_index = tokens.index("}", underscore_index)
bottom_limit = tokens[left_brace_index + 1: right_brace_index]
# next, we isolate the upper limit
top_limit = tokens[caret_index + 1:]
# the code below will be useful for supporting things like `\sum_{n = 0}^{n = 5} n^2`
# if "{" in top_limit:
# left_brace_index = tokens.index("{", caret_index)
# if left_brace_index != -1:
# # then there's a left brace in the string, and we need to find the closing right brace
# right_brace_index = tokens.index("}", caret_index)
# top_limit = tokens[left_brace_index + 1: right_brace_index]
# print(f"top limit = {top_limit}")
index_variable = bottom_limit[0]
lower_limit = bottom_limit[-1]
upper_limit = top_limit[0] # for now, the index will always be 0
# print(f"return value = ({index_variable}, {lower_limit}, {upper_limit})")
return index_variable, lower_limit, upper_limit
def summation(self, tokens):
return sympy.Sum(tokens[2], tokens[1])
def product(self, tokens):
return sympy.Product(tokens[2], tokens[1])
def limit_dir_expr(self, tokens):
caret_index = tokens.index("^")
if "{" in tokens:
left_curly_brace_index = tokens.index("{", caret_index)
direction = tokens[left_curly_brace_index + 1]
else:
direction = tokens[caret_index + 1]
if direction == "+":
return tokens[0], "+"
elif direction == "-":
return tokens[0], "-"
else:
return tokens[0], "+-"
def group_curly_parentheses_lim(self, tokens):
limit_variable = tokens[1]
if isinstance(tokens[3], tuple):
destination, direction = tokens[3]
else:
destination = tokens[3]
direction = "+-"
return limit_variable, destination, direction
def limit(self, tokens):
limit_variable, destination, direction = tokens[2]
return sympy.Limit(tokens[-1], limit_variable, destination, direction)
def differential(self, tokens):
return tokens[1]
def derivative(self, tokens):
return sympy.Derivative(tokens[-1], tokens[5])
def list_of_expressions(self, tokens):
if len(tokens) == 1:
# we return it verbatim because the function_applied node expects
# a list
return tokens
else:
def remove_tokens(args):
if isinstance(args, Token):
if args.type != "COMMA":
# An unexpected token was encountered
raise LaTeXParsingError("A comma token was expected, but some other token was encountered.")
return False
return True
return filter(remove_tokens, tokens)
def function_applied(self, tokens):
return sympy.Function(tokens[0])(*tokens[2])
def min(self, tokens):
return sympy.Min(*tokens[2])
def max(self, tokens):
return sympy.Max(*tokens[2])
def bra(self, tokens):
from sympy.physics.quantum import Bra
return Bra(tokens[1])
def ket(self, tokens):
from sympy.physics.quantum import Ket
return Ket(tokens[1])
def inner_product(self, tokens):
from sympy.physics.quantum import Bra, Ket, InnerProduct
return InnerProduct(Bra(tokens[1]), Ket(tokens[3]))
def sin(self, tokens):
return sympy.sin(tokens[1])
def cos(self, tokens):
return sympy.cos(tokens[1])
def tan(self, tokens):
return sympy.tan(tokens[1])
def csc(self, tokens):
return sympy.csc(tokens[1])
def sec(self, tokens):
return sympy.sec(tokens[1])
def cot(self, tokens):
return sympy.cot(tokens[1])
def sin_power(self, tokens):
exponent = tokens[2]
if exponent == -1:
return sympy.asin(tokens[-1])
else:
return sympy.Pow(sympy.sin(tokens[-1]), exponent)
def cos_power(self, tokens):
exponent = tokens[2]
if exponent == -1:
return sympy.acos(tokens[-1])
else:
return sympy.Pow(sympy.cos(tokens[-1]), exponent)
def tan_power(self, tokens):
exponent = tokens[2]
if exponent == -1:
return sympy.atan(tokens[-1])
else:
return sympy.Pow(sympy.tan(tokens[-1]), exponent)
def csc_power(self, tokens):
exponent = tokens[2]
if exponent == -1:
return sympy.acsc(tokens[-1])
else:
return sympy.Pow(sympy.csc(tokens[-1]), exponent)
def sec_power(self, tokens):
exponent = tokens[2]
if exponent == -1:
return sympy.asec(tokens[-1])
else:
return sympy.Pow(sympy.sec(tokens[-1]), exponent)
def cot_power(self, tokens):
exponent = tokens[2]
if exponent == -1:
return sympy.acot(tokens[-1])
else:
return sympy.Pow(sympy.cot(tokens[-1]), exponent)
def arcsin(self, tokens):
return sympy.asin(tokens[1])
def arccos(self, tokens):
return sympy.acos(tokens[1])
def arctan(self, tokens):
return sympy.atan(tokens[1])
def arccsc(self, tokens):
return sympy.acsc(tokens[1])
def arcsec(self, tokens):
return sympy.asec(tokens[1])
def arccot(self, tokens):
return sympy.acot(tokens[1])
def sinh(self, tokens):
return sympy.sinh(tokens[1])
def cosh(self, tokens):
return sympy.cosh(tokens[1])
def tanh(self, tokens):
return sympy.tanh(tokens[1])
def asinh(self, tokens):
return sympy.asinh(tokens[1])
def acosh(self, tokens):
return sympy.acosh(tokens[1])
def atanh(self, tokens):
return sympy.atanh(tokens[1])
def abs(self, tokens):
return sympy.Abs(tokens[1])
def floor(self, tokens):
return sympy.floor(tokens[1])
def ceil(self, tokens):
return sympy.ceiling(tokens[1])
def factorial(self, tokens):
return sympy.factorial(tokens[0])
def conjugate(self, tokens):
return sympy.conjugate(tokens[1])
def square_root(self, tokens):
if len(tokens) == 2:
# then there was no square bracket argument
return sympy.sqrt(tokens[1])
elif len(tokens) == 3:
# then there _was_ a square bracket argument
return sympy.root(tokens[2], tokens[1])
def exponential(self, tokens):
return sympy.exp(tokens[1])
def log(self, tokens):
if tokens[0].type == "FUNC_LG":
# we don't need to check if there's an underscore or not because having one
# in this case would be meaningless
# TODO: ANTLR refers to ISO 80000-2:2019. should we keep base 10 or base 2?
return sympy.log(tokens[1], 10)
elif tokens[0].type == "FUNC_LN":
return sympy.log(tokens[1])
elif tokens[0].type == "FUNC_LOG":
# we check if a base was specified or not
if "_" in tokens:
# then a base was specified
return sympy.log(tokens[3], tokens[2])
else:
# a base was not specified
return sympy.log(tokens[1])
def _extract_differential_symbol(self, s: str):
differential_symbols = {"d", r"\text{d}", r"\mathrm{d}"}
differential_symbol = next((symbol for symbol in differential_symbols if symbol in s), None)
return differential_symbol
def matrix(self, tokens):
def is_matrix_row(x):
return (isinstance(x, Tree) and x.data == "matrix_row")
def is_not_col_delim(y):
return (not isinstance(y, Token) or y.type != "MATRIX_COL_DELIM")
matrix_body = tokens[1].children
return sympy.Matrix([[y for y in x.children if is_not_col_delim(y)]
for x in matrix_body if is_matrix_row(x)])
def determinant(self, tokens):
if len(tokens) == 2: # \det A
if not self._obj_is_sympy_Matrix(tokens[1]):
raise LaTeXParsingError("Cannot take determinant of non-matrix.")
return tokens[1].det()
if len(tokens) == 3: # | A |
return self.matrix(tokens).det()
def trace(self, tokens):
if not self._obj_is_sympy_Matrix(tokens[1]):
raise LaTeXParsingError("Cannot take trace of non-matrix.")
return sympy.Trace(tokens[1])
def adjugate(self, tokens):
if not self._obj_is_sympy_Matrix(tokens[1]):
raise LaTeXParsingError("Cannot take adjugate of non-matrix.")
# need .doit() since MatAdd does not support .adjugate() method
return tokens[1].doit().adjugate()
def _obj_is_sympy_Matrix(self, obj):
if hasattr(obj, "is_Matrix"):
return obj.is_Matrix
return isinstance(obj, sympy.Matrix)
def _handle_division(self, numerator, denominator):
if self._obj_is_sympy_Matrix(denominator):
raise LaTeXParsingError("Cannot divide by matrices like this since "
"it is not clear if left or right multiplication "
"by the inverse is intended. Try explicitly "
"multiplying by the inverse instead.")
if self._obj_is_sympy_Matrix(numerator):
return sympy.MatMul(numerator, sympy.Pow(denominator, -1))
return sympy.Mul(numerator, sympy.Pow(denominator, -1))

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,71 @@
import re
from sympy.concrete.products import product
from sympy.concrete.summations import Sum
from sympy.core.sympify import sympify
from sympy.functions.elementary.trigonometric import (cos, sin)
class MaximaHelpers:
def maxima_expand(expr):
return expr.expand()
def maxima_float(expr):
return expr.evalf()
def maxima_trigexpand(expr):
return expr.expand(trig=True)
def maxima_sum(a1, a2, a3, a4):
return Sum(a1, (a2, a3, a4)).doit()
def maxima_product(a1, a2, a3, a4):
return product(a1, (a2, a3, a4))
def maxima_csc(expr):
return 1/sin(expr)
def maxima_sec(expr):
return 1/cos(expr)
sub_dict = {
'pi': re.compile(r'%pi'),
'E': re.compile(r'%e'),
'I': re.compile(r'%i'),
'**': re.compile(r'\^'),
'oo': re.compile(r'\binf\b'),
'-oo': re.compile(r'\bminf\b'),
"'-'": re.compile(r'\bminus\b'),
'maxima_expand': re.compile(r'\bexpand\b'),
'maxima_float': re.compile(r'\bfloat\b'),
'maxima_trigexpand': re.compile(r'\btrigexpand'),
'maxima_sum': re.compile(r'\bsum\b'),
'maxima_product': re.compile(r'\bproduct\b'),
'cancel': re.compile(r'\bratsimp\b'),
'maxima_csc': re.compile(r'\bcsc\b'),
'maxima_sec': re.compile(r'\bsec\b')
}
var_name = re.compile(r'^\s*(\w+)\s*:')
def parse_maxima(str, globals=None, name_dict={}):
str = str.strip()
str = str.rstrip('; ')
for k, v in sub_dict.items():
str = v.sub(k, str)
assign_var = None
var_match = var_name.search(str)
if var_match:
assign_var = var_match.group(1)
str = str[var_match.end():].strip()
dct = MaximaHelpers.__dict__.copy()
dct.update(name_dict)
obj = sympify(str, locals=dct)
if assign_var and globals:
globals[assign_var] = obj
return obj

View File

@@ -0,0 +1,279 @@
from sympy.printing import pycode, ccode, fcode
from sympy.external import import_module
from sympy.utilities.decorator import doctest_depends_on
lfortran = import_module('lfortran')
cin = import_module('clang.cindex', import_kwargs = {'fromlist': ['cindex']})
if lfortran:
from sympy.parsing.fortran.fortran_parser import src_to_sympy
if cin:
from sympy.parsing.c.c_parser import parse_c
@doctest_depends_on(modules=['lfortran', 'clang.cindex'])
class SymPyExpression: # type: ignore
"""Class to store and handle SymPy expressions
This class will hold SymPy Expressions and handle the API for the
conversion to and from different languages.
It works with the C and the Fortran Parser to generate SymPy expressions
which are stored here and which can be converted to multiple language's
source code.
Notes
=====
The module and its API are currently under development and experimental
and can be changed during development.
The Fortran parser does not support numeric assignments, so all the
variables have been Initialized to zero.
The module also depends on external dependencies:
- LFortran which is required to use the Fortran parser
- Clang which is required for the C parser
Examples
========
Example of parsing C code:
>>> from sympy.parsing.sym_expr import SymPyExpression
>>> src = '''
... int a,b;
... float c = 2, d =4;
... '''
>>> a = SymPyExpression(src, 'c')
>>> a.return_expr()
[Declaration(Variable(a, type=intc)),
Declaration(Variable(b, type=intc)),
Declaration(Variable(c, type=float32, value=2.0)),
Declaration(Variable(d, type=float32, value=4.0))]
An example of variable definition:
>>> from sympy.parsing.sym_expr import SymPyExpression
>>> src2 = '''
... integer :: a, b, c, d
... real :: p, q, r, s
... '''
>>> p = SymPyExpression()
>>> p.convert_to_expr(src2, 'f')
>>> p.convert_to_c()
['int a = 0', 'int b = 0', 'int c = 0', 'int d = 0', 'double p = 0.0', 'double q = 0.0', 'double r = 0.0', 'double s = 0.0']
An example of Assignment:
>>> from sympy.parsing.sym_expr import SymPyExpression
>>> src3 = '''
... integer :: a, b, c, d, e
... d = a + b - c
... e = b * d + c * e / a
... '''
>>> p = SymPyExpression(src3, 'f')
>>> p.convert_to_python()
['a = 0', 'b = 0', 'c = 0', 'd = 0', 'e = 0', 'd = a + b - c', 'e = b*d + c*e/a']
An example of function definition:
>>> from sympy.parsing.sym_expr import SymPyExpression
>>> src = '''
... integer function f(a,b)
... integer, intent(in) :: a, b
... integer :: r
... end function
... '''
>>> a = SymPyExpression(src, 'f')
>>> a.convert_to_python()
['def f(a, b):\\n f = 0\\n r = 0\\n return f']
"""
def __init__(self, source_code = None, mode = None):
"""Constructor for SymPyExpression class"""
super().__init__()
if not(mode or source_code):
self._expr = []
elif mode:
if source_code:
if mode.lower() == 'f':
if lfortran:
self._expr = src_to_sympy(source_code)
else:
raise ImportError("LFortran is not installed, cannot parse Fortran code")
elif mode.lower() == 'c':
if cin:
self._expr = parse_c(source_code)
else:
raise ImportError("Clang is not installed, cannot parse C code")
else:
raise NotImplementedError(
'Parser for specified language is not implemented'
)
else:
raise ValueError('Source code not present')
else:
raise ValueError('Please specify a mode for conversion')
def convert_to_expr(self, src_code, mode):
"""Converts the given source code to SymPy Expressions
Attributes
==========
src_code : String
the source code or filename of the source code that is to be
converted
mode: String
the mode to determine which parser is to be used according to
the language of the source code
f or F for Fortran
c or C for C/C++
Examples
========
>>> from sympy.parsing.sym_expr import SymPyExpression
>>> src3 = '''
... integer function f(a,b) result(r)
... integer, intent(in) :: a, b
... integer :: x
... r = a + b -x
... end function
... '''
>>> p = SymPyExpression()
>>> p.convert_to_expr(src3, 'f')
>>> p.return_expr()
[FunctionDefinition(integer, name=f, parameters=(Variable(a), Variable(b)), body=CodeBlock(
Declaration(Variable(r, type=integer, value=0)),
Declaration(Variable(x, type=integer, value=0)),
Assignment(Variable(r), a + b - x),
Return(Variable(r))
))]
"""
if mode.lower() == 'f':
if lfortran:
self._expr = src_to_sympy(src_code)
else:
raise ImportError("LFortran is not installed, cannot parse Fortran code")
elif mode.lower() == 'c':
if cin:
self._expr = parse_c(src_code)
else:
raise ImportError("Clang is not installed, cannot parse C code")
else:
raise NotImplementedError(
"Parser for specified language has not been implemented"
)
def convert_to_python(self):
"""Returns a list with Python code for the SymPy expressions
Examples
========
>>> from sympy.parsing.sym_expr import SymPyExpression
>>> src2 = '''
... integer :: a, b, c, d
... real :: p, q, r, s
... c = a/b
... d = c/a
... s = p/q
... r = q/p
... '''
>>> p = SymPyExpression(src2, 'f')
>>> p.convert_to_python()
['a = 0', 'b = 0', 'c = 0', 'd = 0', 'p = 0.0', 'q = 0.0', 'r = 0.0', 's = 0.0', 'c = a/b', 'd = c/a', 's = p/q', 'r = q/p']
"""
self._pycode = []
for iter in self._expr:
self._pycode.append(pycode(iter))
return self._pycode
def convert_to_c(self):
"""Returns a list with the c source code for the SymPy expressions
Examples
========
>>> from sympy.parsing.sym_expr import SymPyExpression
>>> src2 = '''
... integer :: a, b, c, d
... real :: p, q, r, s
... c = a/b
... d = c/a
... s = p/q
... r = q/p
... '''
>>> p = SymPyExpression()
>>> p.convert_to_expr(src2, 'f')
>>> p.convert_to_c()
['int a = 0', 'int b = 0', 'int c = 0', 'int d = 0', 'double p = 0.0', 'double q = 0.0', 'double r = 0.0', 'double s = 0.0', 'c = a/b;', 'd = c/a;', 's = p/q;', 'r = q/p;']
"""
self._ccode = []
for iter in self._expr:
self._ccode.append(ccode(iter))
return self._ccode
def convert_to_fortran(self):
"""Returns a list with the fortran source code for the SymPy expressions
Examples
========
>>> from sympy.parsing.sym_expr import SymPyExpression
>>> src2 = '''
... integer :: a, b, c, d
... real :: p, q, r, s
... c = a/b
... d = c/a
... s = p/q
... r = q/p
... '''
>>> p = SymPyExpression(src2, 'f')
>>> p.convert_to_fortran()
[' integer*4 a', ' integer*4 b', ' integer*4 c', ' integer*4 d', ' real*8 p', ' real*8 q', ' real*8 r', ' real*8 s', ' c = a/b', ' d = c/a', ' s = p/q', ' r = q/p']
"""
self._fcode = []
for iter in self._expr:
self._fcode.append(fcode(iter))
return self._fcode
def return_expr(self):
"""Returns the expression list
Examples
========
>>> from sympy.parsing.sym_expr import SymPyExpression
>>> src3 = '''
... integer function f(a,b)
... integer, intent(in) :: a, b
... integer :: r
... r = a+b
... f = r
... end function
... '''
>>> p = SymPyExpression()
>>> p.convert_to_expr(src3, 'f')
>>> p.return_expr()
[FunctionDefinition(integer, name=f, parameters=(Variable(a), Variable(b)), body=CodeBlock(
Declaration(Variable(f, type=integer, value=0)),
Declaration(Variable(r, type=integer, value=0)),
Assignment(Variable(f), Variable(r)),
Return(Variable(f))
))]
"""
return self._expr

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
from sympy.core.singleton import S
from sympy.core.symbol import symbols
from sympy.parsing.ast_parser import parse_expr
from sympy.testing.pytest import raises
from sympy.core.sympify import SympifyError
import warnings
def test_parse_expr():
a, b = symbols('a, b')
# tests issue_16393
assert parse_expr('a + b', {}) == a + b
raises(SympifyError, lambda: parse_expr('a + ', {}))
# tests Transform.visit_Constant
assert parse_expr('1 + 2', {}) == S(3)
assert parse_expr('1 + 2.0', {}) == S(3.0)
# tests Transform.visit_Name
assert parse_expr('Rational(1, 2)', {}) == S(1)/2
assert parse_expr('a', {'a': a}) == a
# tests issue_23092
with warnings.catch_warnings():
warnings.simplefilter('error')
assert parse_expr('6 * 7', {}) == S(42)

View File

@@ -0,0 +1,178 @@
import os
from sympy.functions.elementary.trigonometric import (cos, sin)
from sympy.external import import_module
from sympy.testing.pytest import skip
from sympy.parsing.autolev import parse_autolev
antlr4 = import_module("antlr4")
if not antlr4:
disabled = True
FILE_DIR = os.path.dirname(
os.path.dirname(os.path.abspath(os.path.realpath(__file__))))
def _test_examples(in_filename, out_filename, test_name=""):
in_file_path = os.path.join(FILE_DIR, 'autolev', 'test-examples',
in_filename)
correct_file_path = os.path.join(FILE_DIR, 'autolev', 'test-examples',
out_filename)
with open(in_file_path) as f:
generated_code = parse_autolev(f, include_numeric=True)
with open(correct_file_path) as f:
for idx, line1 in enumerate(f):
if line1.startswith("#"):
break
try:
line2 = generated_code.split('\n')[idx]
assert line1.rstrip() == line2.rstrip()
except Exception:
msg = 'mismatch in ' + test_name + ' in line no: {0}'
raise AssertionError(msg.format(idx+1))
def test_rule_tests():
l = ["ruletest1", "ruletest2", "ruletest3", "ruletest4", "ruletest5",
"ruletest6", "ruletest7", "ruletest8", "ruletest9", "ruletest10",
"ruletest11", "ruletest12"]
for i in l:
in_filepath = i + ".al"
out_filepath = i + ".py"
_test_examples(in_filepath, out_filepath, i)
def test_pydy_examples():
l = ["mass_spring_damper", "chaos_pendulum", "double_pendulum",
"non_min_pendulum"]
for i in l:
in_filepath = os.path.join("pydy-example-repo", i + ".al")
out_filepath = os.path.join("pydy-example-repo", i + ".py")
_test_examples(in_filepath, out_filepath, i)
def test_autolev_tutorial():
dir_path = os.path.join(FILE_DIR, 'autolev', 'test-examples',
'autolev-tutorial')
if os.path.isdir(dir_path):
l = ["tutor1", "tutor2", "tutor3", "tutor4", "tutor5", "tutor6",
"tutor7"]
for i in l:
in_filepath = os.path.join("autolev-tutorial", i + ".al")
out_filepath = os.path.join("autolev-tutorial", i + ".py")
_test_examples(in_filepath, out_filepath, i)
def test_dynamics_online():
dir_path = os.path.join(FILE_DIR, 'autolev', 'test-examples',
'dynamics-online')
if os.path.isdir(dir_path):
ch1 = ["1-4", "1-5", "1-6", "1-7", "1-8", "1-9_1", "1-9_2", "1-9_3"]
ch2 = ["2-1", "2-2", "2-3", "2-4", "2-5", "2-6", "2-7", "2-8", "2-9",
"circular"]
ch3 = ["3-1_1", "3-1_2", "3-2_1", "3-2_2", "3-2_3", "3-2_4", "3-2_5",
"3-3"]
ch4 = ["4-1_1", "4-2_1", "4-4_1", "4-4_2", "4-5_1", "4-5_2"]
chapters = [(ch1, "ch1"), (ch2, "ch2"), (ch3, "ch3"), (ch4, "ch4")]
for ch, name in chapters:
for i in ch:
in_filepath = os.path.join("dynamics-online", name, i + ".al")
out_filepath = os.path.join("dynamics-online", name, i + ".py")
_test_examples(in_filepath, out_filepath, i)
def test_output_01():
"""Autolev example calculates the position, velocity, and acceleration of a
point and expresses in a single reference frame::
(1) FRAMES C,D,F
(2) VARIABLES FD'',DC''
(3) CONSTANTS R,L
(4) POINTS O,E
(5) SIMPROT(F,D,1,FD)
-> (6) F_D = [1, 0, 0; 0, COS(FD), -SIN(FD); 0, SIN(FD), COS(FD)]
(7) SIMPROT(D,C,2,DC)
-> (8) D_C = [COS(DC), 0, SIN(DC); 0, 1, 0; -SIN(DC), 0, COS(DC)]
(9) W_C_F> = EXPRESS(W_C_F>, F)
-> (10) W_C_F> = FD'*F1> + COS(FD)*DC'*F2> + SIN(FD)*DC'*F3>
(11) P_O_E>=R*D2>-L*C1>
(12) P_O_E>=EXPRESS(P_O_E>, D)
-> (13) P_O_E> = -L*COS(DC)*D1> + R*D2> + L*SIN(DC)*D3>
(14) V_E_F>=EXPRESS(DT(P_O_E>,F),D)
-> (15) V_E_F> = L*SIN(DC)*DC'*D1> - L*SIN(DC)*FD'*D2> + (R*FD'+L*COS(DC)*DC')*D3>
(16) A_E_F>=EXPRESS(DT(V_E_F>,F),D)
-> (17) A_E_F> = L*(COS(DC)*DC'^2+SIN(DC)*DC'')*D1> + (-R*FD'^2-2*L*COS(DC)*DC'*FD'-L*SIN(DC)*FD'')*D2> + (R*FD''+L*COS(DC)*DC''-L*SIN(DC)*DC'^2-L*SIN(DC)*FD'^2)*D3>
"""
if not antlr4:
skip('Test skipped: antlr4 is not installed.')
autolev_input = """\
FRAMES C,D,F
VARIABLES FD'',DC''
CONSTANTS R,L
POINTS O,E
SIMPROT(F,D,1,FD)
SIMPROT(D,C,2,DC)
W_C_F>=EXPRESS(W_C_F>,F)
P_O_E>=R*D2>-L*C1>
P_O_E>=EXPRESS(P_O_E>,D)
V_E_F>=EXPRESS(DT(P_O_E>,F),D)
A_E_F>=EXPRESS(DT(V_E_F>,F),D)\
"""
sympy_input = parse_autolev(autolev_input)
g = {}
l = {}
exec(sympy_input, g, l)
w_c_f = l['frame_c'].ang_vel_in(l['frame_f'])
# P_O_E> means "the position of point E wrt to point O"
p_o_e = l['point_e'].pos_from(l['point_o'])
v_e_f = l['point_e'].vel(l['frame_f'])
a_e_f = l['point_e'].acc(l['frame_f'])
# NOTE : The Autolev outputs above were manually transformed into
# equivalent SymPy physics vector expressions. Would be nice to automate
# this transformation.
expected_w_c_f = (l['fd'].diff()*l['frame_f'].x +
cos(l['fd'])*l['dc'].diff()*l['frame_f'].y +
sin(l['fd'])*l['dc'].diff()*l['frame_f'].z)
assert (w_c_f - expected_w_c_f).simplify() == 0
expected_p_o_e = (-l['l']*cos(l['dc'])*l['frame_d'].x +
l['r']*l['frame_d'].y +
l['l']*sin(l['dc'])*l['frame_d'].z)
assert (p_o_e - expected_p_o_e).simplify() == 0
expected_v_e_f = (l['l']*sin(l['dc'])*l['dc'].diff()*l['frame_d'].x -
l['l']*sin(l['dc'])*l['fd'].diff()*l['frame_d'].y +
(l['r']*l['fd'].diff() +
l['l']*cos(l['dc'])*l['dc'].diff())*l['frame_d'].z)
assert (v_e_f - expected_v_e_f).simplify() == 0
expected_a_e_f = (l['l']*(cos(l['dc'])*l['dc'].diff()**2 +
sin(l['dc'])*l['dc'].diff().diff())*l['frame_d'].x +
(-l['r']*l['fd'].diff()**2 -
2*l['l']*cos(l['dc'])*l['dc'].diff()*l['fd'].diff() -
l['l']*sin(l['dc'])*l['fd'].diff().diff())*l['frame_d'].y +
(l['r']*l['fd'].diff().diff() +
l['l']*cos(l['dc'])*l['dc'].diff().diff() -
l['l']*sin(l['dc'])*l['dc'].diff()**2 -
l['l']*sin(l['dc'])*l['fd'].diff()**2)*l['frame_d'].z)
assert (a_e_f - expected_a_e_f).simplify() == 0

View File

@@ -0,0 +1,69 @@
import os
import tempfile
from pathlib import Path
import sympy
from sympy.testing.pytest import raises
from sympy.parsing.latex.lark import LarkLaTeXParser, TransformToSymPyExpr, parse_latex_lark
from sympy.external import import_module
lark = import_module("lark")
# disable tests if lark is not present
disabled = lark is None
grammar_file = os.path.join(os.path.dirname(__file__), "../latex/lark/grammar/latex.lark")
modification1 = """
%override DIV_SYMBOL: DIV
%override MUL_SYMBOL: MUL | CMD_TIMES
"""
modification2 = r"""
%override number: /\d+(,\d*)?/
"""
def init_custom_parser(modification, transformer=None):
latex_grammar = Path(grammar_file).read_text(encoding="utf-8")
latex_grammar += modification
with tempfile.NamedTemporaryFile() as f:
f.write(bytes(latex_grammar, encoding="utf8"))
f.flush()
parser = LarkLaTeXParser(grammar_file=f.name, transformer=transformer)
return parser
def test_custom1():
# Removes the parser's ability to understand \cdot and \div.
parser = init_custom_parser(modification1)
with raises(lark.exceptions.UnexpectedCharacters):
parser.doparse(r"a \cdot b")
parser.doparse(r"x \div y")
class CustomTransformer(TransformToSymPyExpr):
def number(self, tokens):
if "," in tokens[0]:
# The Float constructor expects a dot as the decimal separator
return sympy.core.numbers.Float(tokens[0].replace(",", "."))
else:
return sympy.core.numbers.Integer(tokens[0])
def test_custom2():
# Makes the parser parse commas as the decimal separator instead of dots
parser = init_custom_parser(modification2, CustomTransformer)
with raises(lark.exceptions.UnexpectedCharacters):
# Asserting that the default parser cannot parse numbers which have commas as
# the decimal separator
parse_latex_lark("100,1")
parse_latex_lark("0,009")
parser.doparse("100,1")
parser.doparse("0,009")
parser.doparse("2,71828")
parser.doparse("3,14159")

View File

@@ -0,0 +1,406 @@
from sympy.testing.pytest import raises
from sympy.parsing.sym_expr import SymPyExpression
from sympy.external import import_module
lfortran = import_module('lfortran')
if lfortran:
from sympy.codegen.ast import (Variable, IntBaseType, FloatBaseType, String,
Return, FunctionDefinition, Assignment,
Declaration, CodeBlock)
from sympy.core import Integer, Float, Add
from sympy.core.symbol import Symbol
expr1 = SymPyExpression()
expr2 = SymPyExpression()
src = """\
integer :: a, b, c, d
real :: p, q, r, s
"""
def test_sym_expr():
src1 = (
src +
"""\
d = a + b -c
"""
)
expr3 = SymPyExpression(src,'f')
expr4 = SymPyExpression(src1,'f')
ls1 = expr3.return_expr()
ls2 = expr4.return_expr()
for i in range(0, 7):
assert isinstance(ls1[i], Declaration)
assert isinstance(ls2[i], Declaration)
assert isinstance(ls2[8], Assignment)
assert ls1[0] == Declaration(
Variable(
Symbol('a'),
type = IntBaseType(String('integer')),
value = Integer(0)
)
)
assert ls1[1] == Declaration(
Variable(
Symbol('b'),
type = IntBaseType(String('integer')),
value = Integer(0)
)
)
assert ls1[2] == Declaration(
Variable(
Symbol('c'),
type = IntBaseType(String('integer')),
value = Integer(0)
)
)
assert ls1[3] == Declaration(
Variable(
Symbol('d'),
type = IntBaseType(String('integer')),
value = Integer(0)
)
)
assert ls1[4] == Declaration(
Variable(
Symbol('p'),
type = FloatBaseType(String('real')),
value = Float(0.0)
)
)
assert ls1[5] == Declaration(
Variable(
Symbol('q'),
type = FloatBaseType(String('real')),
value = Float(0.0)
)
)
assert ls1[6] == Declaration(
Variable(
Symbol('r'),
type = FloatBaseType(String('real')),
value = Float(0.0)
)
)
assert ls1[7] == Declaration(
Variable(
Symbol('s'),
type = FloatBaseType(String('real')),
value = Float(0.0)
)
)
assert ls2[8] == Assignment(
Variable(Symbol('d')),
Symbol('a') + Symbol('b') - Symbol('c')
)
def test_assignment():
src1 = (
src +
"""\
a = b
c = d
p = q
r = s
"""
)
expr1.convert_to_expr(src1, 'f')
ls1 = expr1.return_expr()
for iter in range(0, 12):
if iter < 8:
assert isinstance(ls1[iter], Declaration)
else:
assert isinstance(ls1[iter], Assignment)
assert ls1[8] == Assignment(
Variable(Symbol('a')),
Variable(Symbol('b'))
)
assert ls1[9] == Assignment(
Variable(Symbol('c')),
Variable(Symbol('d'))
)
assert ls1[10] == Assignment(
Variable(Symbol('p')),
Variable(Symbol('q'))
)
assert ls1[11] == Assignment(
Variable(Symbol('r')),
Variable(Symbol('s'))
)
def test_binop_add():
src1 = (
src +
"""\
c = a + b
d = a + c
s = p + q + r
"""
)
expr1.convert_to_expr(src1, 'f')
ls1 = expr1.return_expr()
for iter in range(8, 11):
assert isinstance(ls1[iter], Assignment)
assert ls1[8] == Assignment(
Variable(Symbol('c')),
Symbol('a') + Symbol('b')
)
assert ls1[9] == Assignment(
Variable(Symbol('d')),
Symbol('a') + Symbol('c')
)
assert ls1[10] == Assignment(
Variable(Symbol('s')),
Symbol('p') + Symbol('q') + Symbol('r')
)
def test_binop_sub():
src1 = (
src +
"""\
c = a - b
d = a - c
s = p - q - r
"""
)
expr1.convert_to_expr(src1, 'f')
ls1 = expr1.return_expr()
for iter in range(8, 11):
assert isinstance(ls1[iter], Assignment)
assert ls1[8] == Assignment(
Variable(Symbol('c')),
Symbol('a') - Symbol('b')
)
assert ls1[9] == Assignment(
Variable(Symbol('d')),
Symbol('a') - Symbol('c')
)
assert ls1[10] == Assignment(
Variable(Symbol('s')),
Symbol('p') - Symbol('q') - Symbol('r')
)
def test_binop_mul():
src1 = (
src +
"""\
c = a * b
d = a * c
s = p * q * r
"""
)
expr1.convert_to_expr(src1, 'f')
ls1 = expr1.return_expr()
for iter in range(8, 11):
assert isinstance(ls1[iter], Assignment)
assert ls1[8] == Assignment(
Variable(Symbol('c')),
Symbol('a') * Symbol('b')
)
assert ls1[9] == Assignment(
Variable(Symbol('d')),
Symbol('a') * Symbol('c')
)
assert ls1[10] == Assignment(
Variable(Symbol('s')),
Symbol('p') * Symbol('q') * Symbol('r')
)
def test_binop_div():
src1 = (
src +
"""\
c = a / b
d = a / c
s = p / q
r = q / p
"""
)
expr1.convert_to_expr(src1, 'f')
ls1 = expr1.return_expr()
for iter in range(8, 12):
assert isinstance(ls1[iter], Assignment)
assert ls1[8] == Assignment(
Variable(Symbol('c')),
Symbol('a') / Symbol('b')
)
assert ls1[9] == Assignment(
Variable(Symbol('d')),
Symbol('a') / Symbol('c')
)
assert ls1[10] == Assignment(
Variable(Symbol('s')),
Symbol('p') / Symbol('q')
)
assert ls1[11] == Assignment(
Variable(Symbol('r')),
Symbol('q') / Symbol('p')
)
def test_mul_binop():
src1 = (
src +
"""\
d = a + b - c
c = a * b + d
s = p * q / r
r = p * s + q / p
"""
)
expr1.convert_to_expr(src1, 'f')
ls1 = expr1.return_expr()
for iter in range(8, 12):
assert isinstance(ls1[iter], Assignment)
assert ls1[8] == Assignment(
Variable(Symbol('d')),
Symbol('a') + Symbol('b') - Symbol('c')
)
assert ls1[9] == Assignment(
Variable(Symbol('c')),
Symbol('a') * Symbol('b') + Symbol('d')
)
assert ls1[10] == Assignment(
Variable(Symbol('s')),
Symbol('p') * Symbol('q') / Symbol('r')
)
assert ls1[11] == Assignment(
Variable(Symbol('r')),
Symbol('p') * Symbol('s') + Symbol('q') / Symbol('p')
)
def test_function():
src1 = """\
integer function f(a,b)
integer :: x, y
f = x + y
end function
"""
expr1.convert_to_expr(src1, 'f')
for iter in expr1.return_expr():
assert isinstance(iter, FunctionDefinition)
assert iter == FunctionDefinition(
IntBaseType(String('integer')),
name=String('f'),
parameters=(
Variable(Symbol('a')),
Variable(Symbol('b'))
),
body=CodeBlock(
Declaration(
Variable(
Symbol('a'),
type=IntBaseType(String('integer')),
value=Integer(0)
)
),
Declaration(
Variable(
Symbol('b'),
type=IntBaseType(String('integer')),
value=Integer(0)
)
),
Declaration(
Variable(
Symbol('f'),
type=IntBaseType(String('integer')),
value=Integer(0)
)
),
Declaration(
Variable(
Symbol('x'),
type=IntBaseType(String('integer')),
value=Integer(0)
)
),
Declaration(
Variable(
Symbol('y'),
type=IntBaseType(String('integer')),
value=Integer(0)
)
),
Assignment(
Variable(Symbol('f')),
Add(Symbol('x'), Symbol('y'))
),
Return(Variable(Symbol('f')))
)
)
def test_var():
expr1.convert_to_expr(src, 'f')
ls = expr1.return_expr()
for iter in expr1.return_expr():
assert isinstance(iter, Declaration)
assert ls[0] == Declaration(
Variable(
Symbol('a'),
type = IntBaseType(String('integer')),
value = Integer(0)
)
)
assert ls[1] == Declaration(
Variable(
Symbol('b'),
type = IntBaseType(String('integer')),
value = Integer(0)
)
)
assert ls[2] == Declaration(
Variable(
Symbol('c'),
type = IntBaseType(String('integer')),
value = Integer(0)
)
)
assert ls[3] == Declaration(
Variable(
Symbol('d'),
type = IntBaseType(String('integer')),
value = Integer(0)
)
)
assert ls[4] == Declaration(
Variable(
Symbol('p'),
type = FloatBaseType(String('real')),
value = Float(0.0)
)
)
assert ls[5] == Declaration(
Variable(
Symbol('q'),
type = FloatBaseType(String('real')),
value = Float(0.0)
)
)
assert ls[6] == Declaration(
Variable(
Symbol('r'),
type = FloatBaseType(String('real')),
value = Float(0.0)
)
)
assert ls[7] == Declaration(
Variable(
Symbol('s'),
type = FloatBaseType(String('real')),
value = Float(0.0)
)
)
else:
def test_raise():
from sympy.parsing.fortran.fortran_parser import ASR2PyVisitor
raises(ImportError, lambda: ASR2PyVisitor())
raises(ImportError, lambda: SymPyExpression(' ', mode = 'f'))

View File

@@ -0,0 +1,195 @@
import sympy
from sympy.parsing.sympy_parser import (
parse_expr,
standard_transformations,
convert_xor,
implicit_multiplication_application,
implicit_multiplication,
implicit_application,
function_exponentiation,
split_symbols,
split_symbols_custom,
_token_splittable
)
from sympy.testing.pytest import raises
def test_implicit_multiplication():
cases = {
'5x': '5*x',
'abc': 'a*b*c',
'3sin(x)': '3*sin(x)',
'(x+1)(x+2)': '(x+1)*(x+2)',
'(5 x**2)sin(x)': '(5*x**2)*sin(x)',
'2 sin(x) cos(x)': '2*sin(x)*cos(x)',
'pi x': 'pi*x',
'x pi': 'x*pi',
'E x': 'E*x',
'EulerGamma y': 'EulerGamma*y',
'E pi': 'E*pi',
'pi (x + 2)': 'pi*(x+2)',
'(x + 2) pi': '(x+2)*pi',
'pi sin(x)': 'pi*sin(x)',
}
transformations = standard_transformations + (convert_xor,)
transformations2 = transformations + (split_symbols,
implicit_multiplication)
for case in cases:
implicit = parse_expr(case, transformations=transformations2)
normal = parse_expr(cases[case], transformations=transformations)
assert(implicit == normal)
application = ['sin x', 'cos 2*x', 'sin cos x']
for case in application:
raises(SyntaxError,
lambda: parse_expr(case, transformations=transformations2))
raises(TypeError,
lambda: parse_expr('sin**2(x)', transformations=transformations2))
def test_implicit_application():
cases = {
'factorial': 'factorial',
'sin x': 'sin(x)',
'tan y**3': 'tan(y**3)',
'cos 2*x': 'cos(2*x)',
'(cot)': 'cot',
'sin cos tan x': 'sin(cos(tan(x)))'
}
transformations = standard_transformations + (convert_xor,)
transformations2 = transformations + (implicit_application,)
for case in cases:
implicit = parse_expr(case, transformations=transformations2)
normal = parse_expr(cases[case], transformations=transformations)
assert(implicit == normal), (implicit, normal)
multiplication = ['x y', 'x sin x', '2x']
for case in multiplication:
raises(SyntaxError,
lambda: parse_expr(case, transformations=transformations2))
raises(TypeError,
lambda: parse_expr('sin**2(x)', transformations=transformations2))
def test_function_exponentiation():
cases = {
'sin**2(x)': 'sin(x)**2',
'exp^y(z)': 'exp(z)^y',
'sin**2(E^(x))': 'sin(E^(x))**2'
}
transformations = standard_transformations + (convert_xor,)
transformations2 = transformations + (function_exponentiation,)
for case in cases:
implicit = parse_expr(case, transformations=transformations2)
normal = parse_expr(cases[case], transformations=transformations)
assert(implicit == normal)
other_implicit = ['x y', 'x sin x', '2x', 'sin x',
'cos 2*x', 'sin cos x']
for case in other_implicit:
raises(SyntaxError,
lambda: parse_expr(case, transformations=transformations2))
assert parse_expr('x**2', local_dict={ 'x': sympy.Symbol('x') },
transformations=transformations2) == parse_expr('x**2')
def test_symbol_splitting():
# By default Greek letter names should not be split (lambda is a keyword
# so skip it)
transformations = standard_transformations + (split_symbols,)
greek_letters = ('alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta',
'eta', 'theta', 'iota', 'kappa', 'mu', 'nu', 'xi',
'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon',
'phi', 'chi', 'psi', 'omega')
for letter in greek_letters:
assert(parse_expr(letter, transformations=transformations) ==
parse_expr(letter))
# Make sure symbol splitting resolves names
transformations += (implicit_multiplication,)
local_dict = { 'e': sympy.E }
cases = {
'xe': 'E*x',
'Iy': 'I*y',
'ee': 'E*E',
}
for case, expected in cases.items():
assert(parse_expr(case, local_dict=local_dict,
transformations=transformations) ==
parse_expr(expected))
# Make sure custom splitting works
def can_split(symbol):
if symbol not in ('unsplittable', 'names'):
return _token_splittable(symbol)
return False
transformations = standard_transformations
transformations += (split_symbols_custom(can_split),
implicit_multiplication)
assert(parse_expr('unsplittable', transformations=transformations) ==
parse_expr('unsplittable'))
assert(parse_expr('names', transformations=transformations) ==
parse_expr('names'))
assert(parse_expr('xy', transformations=transformations) ==
parse_expr('x*y'))
for letter in greek_letters:
assert(parse_expr(letter, transformations=transformations) ==
parse_expr(letter))
def test_all_implicit_steps():
cases = {
'2x': '2*x', # implicit multiplication
'x y': 'x*y',
'xy': 'x*y',
'sin x': 'sin(x)', # add parentheses
'2sin x': '2*sin(x)',
'x y z': 'x*y*z',
'sin(2 * 3x)': 'sin(2 * 3 * x)',
'sin(x) (1 + cos(x))': 'sin(x) * (1 + cos(x))',
'(x + 2) sin(x)': '(x + 2) * sin(x)',
'(x + 2) sin x': '(x + 2) * sin(x)',
'sin(sin x)': 'sin(sin(x))',
'sin x!': 'sin(factorial(x))',
'sin x!!': 'sin(factorial2(x))',
'factorial': 'factorial', # don't apply a bare function
'x sin x': 'x * sin(x)', # both application and multiplication
'xy sin x': 'x * y * sin(x)',
'(x+2)(x+3)': '(x + 2) * (x+3)',
'x**2 + 2xy + y**2': 'x**2 + 2 * x * y + y**2', # split the xy
'pi': 'pi', # don't mess with constants
'None': 'None',
'ln sin x': 'ln(sin(x))', # multiple implicit function applications
'sin x**2': 'sin(x**2)', # implicit application to an exponential
'alpha': 'Symbol("alpha")', # don't split Greek letters/subscripts
'x_2': 'Symbol("x_2")',
'sin^2 x**2': 'sin(x**2)**2', # function raised to a power
'sin**3(x)': 'sin(x)**3',
'(factorial)': 'factorial',
'tan 3x': 'tan(3*x)',
'sin^2(3*E^(x))': 'sin(3*E**(x))**2',
'sin**2(E^(3x))': 'sin(E**(3*x))**2',
'sin^2 (3x*E^(x))': 'sin(3*x*E^x)**2',
'pi sin x': 'pi*sin(x)',
}
transformations = standard_transformations + (convert_xor,)
transformations2 = transformations + (implicit_multiplication_application,)
for case in cases:
implicit = parse_expr(case, transformations=transformations2)
normal = parse_expr(cases[case], transformations=transformations)
assert(implicit == normal)
def test_no_methods_implicit_multiplication():
# Issue 21020
u = sympy.Symbol('u')
transformations = standard_transformations + \
(implicit_multiplication,)
expr = parse_expr('x.is_polynomial(x)', transformations=transformations)
assert expr == True
expr = parse_expr('(exp(x) / (1 + exp(2x))).subs(exp(x), u)',
transformations=transformations)
assert expr == u/(u**2 + 1)

View File

@@ -0,0 +1,358 @@
from sympy.testing.pytest import raises, XFAIL
from sympy.external import import_module
from sympy.concrete.products import Product
from sympy.concrete.summations import Sum
from sympy.core.add import Add
from sympy.core.function import (Derivative, Function)
from sympy.core.mul import Mul
from sympy.core.numbers import (E, oo)
from sympy.core.power import Pow
from sympy.core.relational import (GreaterThan, LessThan, StrictGreaterThan, StrictLessThan, Unequality)
from sympy.core.symbol import Symbol
from sympy.functions.combinatorial.factorials import (binomial, factorial)
from sympy.functions.elementary.complexes import (Abs, conjugate)
from sympy.functions.elementary.exponential import (exp, log)
from sympy.functions.elementary.integers import (ceiling, floor)
from sympy.functions.elementary.miscellaneous import (root, sqrt)
from sympy.functions.elementary.trigonometric import (asin, cos, csc, sec, sin, tan)
from sympy.integrals.integrals import Integral
from sympy.series.limits import Limit
from sympy.core.relational import Eq, Ne, Lt, Le, Gt, Ge
from sympy.physics.quantum.state import Bra, Ket
from sympy.abc import x, y, z, a, b, c, t, k, n
antlr4 = import_module("antlr4")
# disable tests if antlr4-python3-runtime is not present
disabled = antlr4 is None
theta = Symbol('theta')
f = Function('f')
# shorthand definitions
def _Add(a, b):
return Add(a, b, evaluate=False)
def _Mul(a, b):
return Mul(a, b, evaluate=False)
def _Pow(a, b):
return Pow(a, b, evaluate=False)
def _Sqrt(a):
return sqrt(a, evaluate=False)
def _Conjugate(a):
return conjugate(a, evaluate=False)
def _Abs(a):
return Abs(a, evaluate=False)
def _factorial(a):
return factorial(a, evaluate=False)
def _exp(a):
return exp(a, evaluate=False)
def _log(a, b):
return log(a, b, evaluate=False)
def _binomial(n, k):
return binomial(n, k, evaluate=False)
def test_import():
from sympy.parsing.latex._build_latex_antlr import (
build_parser,
check_antlr_version,
dir_latex_antlr
)
# XXX: It would be better to come up with a test for these...
del build_parser, check_antlr_version, dir_latex_antlr
# These LaTeX strings should parse to the corresponding SymPy expression
GOOD_PAIRS = [
(r"0", 0),
(r"1", 1),
(r"-3.14", -3.14),
(r"(-7.13)(1.5)", _Mul(-7.13, 1.5)),
(r"x", x),
(r"2x", 2*x),
(r"x^2", x**2),
(r"x^\frac{1}{2}", _Pow(x, _Pow(2, -1))),
(r"x^{3 + 1}", x**_Add(3, 1)),
(r"-c", -c),
(r"a \cdot b", a * b),
(r"a / b", a / b),
(r"a \div b", a / b),
(r"a + b", a + b),
(r"a + b - a", _Add(a+b, -a)),
(r"a^2 + b^2 = c^2", Eq(a**2 + b**2, c**2)),
(r"(x + y) z", _Mul(_Add(x, y), z)),
(r"a'b+ab'", _Add(_Mul(Symbol("a'"), b), _Mul(a, Symbol("b'")))),
(r"y''_1", Symbol("y_{1}''")),
(r"y_1''", Symbol("y_{1}''")),
(r"\left(x + y\right) z", _Mul(_Add(x, y), z)),
(r"\left( x + y\right ) z", _Mul(_Add(x, y), z)),
(r"\left( x + y\right ) z", _Mul(_Add(x, y), z)),
(r"\left[x + y\right] z", _Mul(_Add(x, y), z)),
(r"\left\{x + y\right\} z", _Mul(_Add(x, y), z)),
(r"1+1", _Add(1, 1)),
(r"0+1", _Add(0, 1)),
(r"1*2", _Mul(1, 2)),
(r"0*1", _Mul(0, 1)),
(r"1 \times 2 ", _Mul(1, 2)),
(r"x = y", Eq(x, y)),
(r"x \neq y", Ne(x, y)),
(r"x < y", Lt(x, y)),
(r"x > y", Gt(x, y)),
(r"x \leq y", Le(x, y)),
(r"x \geq y", Ge(x, y)),
(r"x \le y", Le(x, y)),
(r"x \ge y", Ge(x, y)),
(r"\lfloor x \rfloor", floor(x)),
(r"\lceil x \rceil", ceiling(x)),
(r"\langle x |", Bra('x')),
(r"| x \rangle", Ket('x')),
(r"\sin \theta", sin(theta)),
(r"\sin(\theta)", sin(theta)),
(r"\sin^{-1} a", asin(a)),
(r"\sin a \cos b", _Mul(sin(a), cos(b))),
(r"\sin \cos \theta", sin(cos(theta))),
(r"\sin(\cos \theta)", sin(cos(theta))),
(r"\frac{a}{b}", a / b),
(r"\dfrac{a}{b}", a / b),
(r"\tfrac{a}{b}", a / b),
(r"\frac12", _Pow(2, -1)),
(r"\frac12y", _Mul(_Pow(2, -1), y)),
(r"\frac1234", _Mul(_Pow(2, -1), 34)),
(r"\frac2{3}", _Mul(2, _Pow(3, -1))),
(r"\frac{\sin{x}}2", _Mul(sin(x), _Pow(2, -1))),
(r"\frac{a + b}{c}", _Mul(a + b, _Pow(c, -1))),
(r"\frac{7}{3}", _Mul(7, _Pow(3, -1))),
(r"(\csc x)(\sec y)", csc(x)*sec(y)),
(r"\lim_{x \to 3} a", Limit(a, x, 3, dir='+-')),
(r"\lim_{x \rightarrow 3} a", Limit(a, x, 3, dir='+-')),
(r"\lim_{x \Rightarrow 3} a", Limit(a, x, 3, dir='+-')),
(r"\lim_{x \longrightarrow 3} a", Limit(a, x, 3, dir='+-')),
(r"\lim_{x \Longrightarrow 3} a", Limit(a, x, 3, dir='+-')),
(r"\lim_{x \to 3^{+}} a", Limit(a, x, 3, dir='+')),
(r"\lim_{x \to 3^{-}} a", Limit(a, x, 3, dir='-')),
(r"\lim_{x \to 3^+} a", Limit(a, x, 3, dir='+')),
(r"\lim_{x \to 3^-} a", Limit(a, x, 3, dir='-')),
(r"\infty", oo),
(r"\lim_{x \to \infty} \frac{1}{x}", Limit(_Pow(x, -1), x, oo)),
(r"\frac{d}{dx} x", Derivative(x, x)),
(r"\frac{d}{dt} x", Derivative(x, t)),
(r"f(x)", f(x)),
(r"f(x, y)", f(x, y)),
(r"f(x, y, z)", f(x, y, z)),
(r"f'_1(x)", Function("f_{1}'")(x)),
(r"f_{1}''(x+y)", Function("f_{1}''")(x+y)),
(r"\frac{d f(x)}{dx}", Derivative(f(x), x)),
(r"\frac{d\theta(x)}{dx}", Derivative(Function('theta')(x), x)),
(r"x \neq y", Unequality(x, y)),
(r"|x|", _Abs(x)),
(r"||x||", _Abs(Abs(x))),
(r"|x||y|", _Abs(x)*_Abs(y)),
(r"||x||y||", _Abs(_Abs(x)*_Abs(y))),
(r"\pi^{|xy|}", Symbol('pi')**_Abs(x*y)),
(r"\int x dx", Integral(x, x)),
(r"\int x d\theta", Integral(x, theta)),
(r"\int (x^2 - y)dx", Integral(x**2 - y, x)),
(r"\int x + a dx", Integral(_Add(x, a), x)),
(r"\int da", Integral(1, a)),
(r"\int_0^7 dx", Integral(1, (x, 0, 7))),
(r"\int\limits_{0}^{1} x dx", Integral(x, (x, 0, 1))),
(r"\int_a^b x dx", Integral(x, (x, a, b))),
(r"\int^b_a x dx", Integral(x, (x, a, b))),
(r"\int_{a}^b x dx", Integral(x, (x, a, b))),
(r"\int^{b}_a x dx", Integral(x, (x, a, b))),
(r"\int_{a}^{b} x dx", Integral(x, (x, a, b))),
(r"\int^{b}_{a} x dx", Integral(x, (x, a, b))),
(r"\int_{f(a)}^{f(b)} f(z) dz", Integral(f(z), (z, f(a), f(b)))),
(r"\int (x+a)", Integral(_Add(x, a), x)),
(r"\int a + b + c dx", Integral(_Add(_Add(a, b), c), x)),
(r"\int \frac{dz}{z}", Integral(Pow(z, -1), z)),
(r"\int \frac{3 dz}{z}", Integral(3*Pow(z, -1), z)),
(r"\int \frac{1}{x} dx", Integral(Pow(x, -1), x)),
(r"\int \frac{1}{a} + \frac{1}{b} dx",
Integral(_Add(_Pow(a, -1), Pow(b, -1)), x)),
(r"\int \frac{3 \cdot d\theta}{\theta}",
Integral(3*_Pow(theta, -1), theta)),
(r"\int \frac{1}{x} + 1 dx", Integral(_Add(_Pow(x, -1), 1), x)),
(r"x_0", Symbol('x_{0}')),
(r"x_{1}", Symbol('x_{1}')),
(r"x_a", Symbol('x_{a}')),
(r"x_{b}", Symbol('x_{b}')),
(r"h_\theta", Symbol('h_{theta}')),
(r"h_{\theta}", Symbol('h_{theta}')),
(r"h_{\theta}(x_0, x_1)",
Function('h_{theta}')(Symbol('x_{0}'), Symbol('x_{1}'))),
(r"x!", _factorial(x)),
(r"100!", _factorial(100)),
(r"\theta!", _factorial(theta)),
(r"(x + 1)!", _factorial(_Add(x, 1))),
(r"(x!)!", _factorial(_factorial(x))),
(r"x!!!", _factorial(_factorial(_factorial(x)))),
(r"5!7!", _Mul(_factorial(5), _factorial(7))),
(r"\sqrt{x}", sqrt(x)),
(r"\sqrt{x + b}", sqrt(_Add(x, b))),
(r"\sqrt[3]{\sin x}", root(sin(x), 3)),
(r"\sqrt[y]{\sin x}", root(sin(x), y)),
(r"\sqrt[\theta]{\sin x}", root(sin(x), theta)),
(r"\sqrt{\frac{12}{6}}", _Sqrt(_Mul(12, _Pow(6, -1)))),
(r"\overline{z}", _Conjugate(z)),
(r"\overline{\overline{z}}", _Conjugate(_Conjugate(z))),
(r"\overline{x + y}", _Conjugate(_Add(x, y))),
(r"\overline{x} + \overline{y}", _Conjugate(x) + _Conjugate(y)),
(r"x < y", StrictLessThan(x, y)),
(r"x \leq y", LessThan(x, y)),
(r"x > y", StrictGreaterThan(x, y)),
(r"x \geq y", GreaterThan(x, y)),
(r"\mathit{x}", Symbol('x')),
(r"\mathit{test}", Symbol('test')),
(r"\mathit{TEST}", Symbol('TEST')),
(r"\mathit{HELLO world}", Symbol('HELLO world')),
(r"\sum_{k = 1}^{3} c", Sum(c, (k, 1, 3))),
(r"\sum_{k = 1}^3 c", Sum(c, (k, 1, 3))),
(r"\sum^{3}_{k = 1} c", Sum(c, (k, 1, 3))),
(r"\sum^3_{k = 1} c", Sum(c, (k, 1, 3))),
(r"\sum_{k = 1}^{10} k^2", Sum(k**2, (k, 1, 10))),
(r"\sum_{n = 0}^{\infty} \frac{1}{n!}",
Sum(_Pow(_factorial(n), -1), (n, 0, oo))),
(r"\prod_{a = b}^{c} x", Product(x, (a, b, c))),
(r"\prod_{a = b}^c x", Product(x, (a, b, c))),
(r"\prod^{c}_{a = b} x", Product(x, (a, b, c))),
(r"\prod^c_{a = b} x", Product(x, (a, b, c))),
(r"\exp x", _exp(x)),
(r"\exp(x)", _exp(x)),
(r"\lg x", _log(x, 10)),
(r"\ln x", _log(x, E)),
(r"\ln xy", _log(x*y, E)),
(r"\log x", _log(x, E)),
(r"\log xy", _log(x*y, E)),
(r"\log_{2} x", _log(x, 2)),
(r"\log_{a} x", _log(x, a)),
(r"\log_{11} x", _log(x, 11)),
(r"\log_{a^2} x", _log(x, _Pow(a, 2))),
(r"[x]", x),
(r"[a + b]", _Add(a, b)),
(r"\frac{d}{dx} [ \tan x ]", Derivative(tan(x), x)),
(r"\binom{n}{k}", _binomial(n, k)),
(r"\tbinom{n}{k}", _binomial(n, k)),
(r"\dbinom{n}{k}", _binomial(n, k)),
(r"\binom{n}{0}", _binomial(n, 0)),
(r"x^\binom{n}{k}", _Pow(x, _binomial(n, k))),
(r"a \, b", _Mul(a, b)),
(r"a \thinspace b", _Mul(a, b)),
(r"a \: b", _Mul(a, b)),
(r"a \medspace b", _Mul(a, b)),
(r"a \; b", _Mul(a, b)),
(r"a \thickspace b", _Mul(a, b)),
(r"a \quad b", _Mul(a, b)),
(r"a \qquad b", _Mul(a, b)),
(r"a \! b", _Mul(a, b)),
(r"a \negthinspace b", _Mul(a, b)),
(r"a \negmedspace b", _Mul(a, b)),
(r"a \negthickspace b", _Mul(a, b)),
(r"\int x \, dx", Integral(x, x)),
(r"\log_2 x", _log(x, 2)),
(r"\log_a x", _log(x, a)),
(r"5^0 - 4^0", _Add(_Pow(5, 0), _Mul(-1, _Pow(4, 0)))),
(r"3x - 1", _Add(_Mul(3, x), -1))
]
def test_parseable():
from sympy.parsing.latex import parse_latex
for latex_str, sympy_expr in GOOD_PAIRS:
assert parse_latex(latex_str) == sympy_expr, latex_str
# These bad LaTeX strings should raise a LaTeXParsingError when parsed
BAD_STRINGS = [
r"(",
r")",
r"\frac{d}{dx}",
r"(\frac{d}{dx})",
r"\sqrt{}",
r"\sqrt",
r"\overline{}",
r"\overline",
r"{",
r"}",
r"\mathit{x + y}",
r"\mathit{21}",
r"\frac{2}{}",
r"\frac{}{2}",
r"\int",
r"!",
r"!0",
r"_",
r"^",
r"|",
r"||x|",
r"()",
r"((((((((((((((((()))))))))))))))))",
r"-",
r"\frac{d}{dx} + \frac{d}{dt}",
r"f(x,,y)",
r"f(x,y,",
r"\sin^x",
r"\cos^2",
r"@",
r"#",
r"$",
r"%",
r"&",
r"*",
r"" "\\",
r"~",
r"\frac{(2 + x}{1 - x)}",
]
def test_not_parseable():
from sympy.parsing.latex import parse_latex, LaTeXParsingError
for latex_str in BAD_STRINGS:
with raises(LaTeXParsingError):
parse_latex(latex_str)
# At time of migration from latex2sympy, should fail but doesn't
FAILING_BAD_STRINGS = [
r"\cos 1 \cos",
r"f(,",
r"f()",
r"a \div \div b",
r"a \cdot \cdot b",
r"a // b",
r"a +",
r"1.1.1",
r"1 +",
r"a / b /",
]
@XFAIL
def test_failing_not_parseable():
from sympy.parsing.latex import parse_latex, LaTeXParsingError
for latex_str in FAILING_BAD_STRINGS:
with raises(LaTeXParsingError):
parse_latex(latex_str)
# In strict mode, FAILING_BAD_STRINGS would fail
def test_strict_mode():
from sympy.parsing.latex import parse_latex, LaTeXParsingError
for latex_str in FAILING_BAD_STRINGS:
with raises(LaTeXParsingError):
parse_latex(latex_str, strict=True)

View File

@@ -0,0 +1,16 @@
from sympy.external import import_module
from sympy.testing.pytest import ignore_warnings, raises
antlr4 = import_module("antlr4", warn_not_installed=False)
# disable tests if antlr4-python3-runtime is not present
if antlr4:
disabled = True
def test_no_import():
from sympy.parsing.latex import parse_latex
with ignore_warnings(UserWarning):
with raises(ImportError):
parse_latex('1 + 1')

View File

@@ -0,0 +1,872 @@
from sympy.testing.pytest import XFAIL
from sympy.parsing.latex.lark import parse_latex_lark
from sympy.external import import_module
from sympy.concrete.products import Product
from sympy.concrete.summations import Sum
from sympy.core.function import Derivative, Function
from sympy.core.numbers import E, oo, Rational
from sympy.core.power import Pow
from sympy.core.parameters import evaluate
from sympy.core.relational import GreaterThan, LessThan, StrictGreaterThan, StrictLessThan, Unequality
from sympy.core.symbol import Symbol
from sympy.functions.combinatorial.factorials import binomial, factorial
from sympy.functions.elementary.complexes import Abs, conjugate
from sympy.functions.elementary.exponential import exp, log
from sympy.functions.elementary.integers import ceiling, floor
from sympy.functions.elementary.miscellaneous import root, sqrt, Min, Max
from sympy.functions.elementary.trigonometric import asin, cos, csc, sec, sin, tan
from sympy.integrals.integrals import Integral
from sympy.series.limits import Limit
from sympy import Matrix, MatAdd, MatMul, Transpose, Trace
from sympy import I
from sympy.core.relational import Eq, Ne, Lt, Le, Gt, Ge
from sympy.physics.quantum import Bra, Ket, InnerProduct
from sympy.abc import x, y, z, a, b, c, d, t, k, n
from .test_latex import theta, f, _Add, _Mul, _Pow, _Sqrt, _Conjugate, _Abs, _factorial, _exp, _binomial
lark = import_module("lark")
# disable tests if lark is not present
disabled = lark is None
# shorthand definitions that are only needed for the Lark LaTeX parser
def _Min(*args):
return Min(*args, evaluate=False)
def _Max(*args):
return Max(*args, evaluate=False)
def _log(a, b=E):
if b == E:
return log(a, evaluate=False)
else:
return log(a, b, evaluate=False)
def _MatAdd(a, b):
return MatAdd(a, b, evaluate=False)
def _MatMul(a, b):
return MatMul(a, b, evaluate=False)
# These LaTeX strings should parse to the corresponding SymPy expression
SYMBOL_EXPRESSION_PAIRS = [
(r"x_0", Symbol('x_{0}')),
(r"x_{1}", Symbol('x_{1}')),
(r"x_a", Symbol('x_{a}')),
(r"x_{b}", Symbol('x_{b}')),
(r"h_\theta", Symbol('h_{theta}')),
(r"h_{\theta}", Symbol('h_{theta}')),
(r"y''_1", Symbol("y''_{1}")),
(r"y_1''", Symbol("y_{1}''")),
(r"\mathit{x}", Symbol('x')),
(r"\mathit{test}", Symbol('test')),
(r"\mathit{TEST}", Symbol('TEST')),
(r"\mathit{HELLO world}", Symbol('HELLO world')),
(r"a'", Symbol("a'")),
(r"a''", Symbol("a''")),
(r"\alpha'", Symbol("alpha'")),
(r"\alpha''", Symbol("alpha''")),
(r"a_b", Symbol("a_{b}")),
(r"a_b'", Symbol("a_{b}'")),
(r"a'_b", Symbol("a'_{b}")),
(r"a'_b'", Symbol("a'_{b}'")),
(r"a_{b'}", Symbol("a_{b'}")),
(r"a_{b'}'", Symbol("a_{b'}'")),
(r"a'_{b'}", Symbol("a'_{b'}")),
(r"a'_{b'}'", Symbol("a'_{b'}'")),
(r"\mathit{foo}'", Symbol("foo'")),
(r"\mathit{foo'}", Symbol("foo'")),
(r"\mathit{foo'}'", Symbol("foo''")),
(r"a_b''", Symbol("a_{b}''")),
(r"a''_b", Symbol("a''_{b}")),
(r"a''_b'''", Symbol("a''_{b}'''")),
(r"a_{b''}", Symbol("a_{b''}")),
(r"a_{b''}''", Symbol("a_{b''}''")),
(r"a''_{b''}", Symbol("a''_{b''}")),
(r"a''_{b''}'''", Symbol("a''_{b''}'''")),
(r"\mathit{foo}''", Symbol("foo''")),
(r"\mathit{foo''}", Symbol("foo''")),
(r"\mathit{foo''}'''", Symbol("foo'''''")),
(r"a_\alpha", Symbol("a_{alpha}")),
(r"a_\alpha'", Symbol("a_{alpha}'")),
(r"a'_\alpha", Symbol("a'_{alpha}")),
(r"a'_\alpha'", Symbol("a'_{alpha}'")),
(r"a_{\alpha'}", Symbol("a_{alpha'}")),
(r"a_{\alpha'}'", Symbol("a_{alpha'}'")),
(r"a'_{\alpha'}", Symbol("a'_{alpha'}")),
(r"a'_{\alpha'}'", Symbol("a'_{alpha'}'")),
(r"a_\alpha''", Symbol("a_{alpha}''")),
(r"a''_\alpha", Symbol("a''_{alpha}")),
(r"a''_\alpha'''", Symbol("a''_{alpha}'''")),
(r"a_{\alpha''}", Symbol("a_{alpha''}")),
(r"a_{\alpha''}''", Symbol("a_{alpha''}''")),
(r"a''_{\alpha''}", Symbol("a''_{alpha''}")),
(r"a''_{\alpha''}'''", Symbol("a''_{alpha''}'''")),
(r"\alpha_b", Symbol("alpha_{b}")),
(r"\alpha_b'", Symbol("alpha_{b}'")),
(r"\alpha'_b", Symbol("alpha'_{b}")),
(r"\alpha'_b'", Symbol("alpha'_{b}'")),
(r"\alpha_{b'}", Symbol("alpha_{b'}")),
(r"\alpha_{b'}'", Symbol("alpha_{b'}'")),
(r"\alpha'_{b'}", Symbol("alpha'_{b'}")),
(r"\alpha'_{b'}'", Symbol("alpha'_{b'}'")),
(r"\alpha_b''", Symbol("alpha_{b}''")),
(r"\alpha''_b", Symbol("alpha''_{b}")),
(r"\alpha''_b'''", Symbol("alpha''_{b}'''")),
(r"\alpha_{b''}", Symbol("alpha_{b''}")),
(r"\alpha_{b''}''", Symbol("alpha_{b''}''")),
(r"\alpha''_{b''}", Symbol("alpha''_{b''}")),
(r"\alpha''_{b''}'''", Symbol("alpha''_{b''}'''")),
(r"\alpha_\beta", Symbol("alpha_{beta}")),
(r"\alpha_{\beta}", Symbol("alpha_{beta}")),
(r"\alpha_{\beta'}", Symbol("alpha_{beta'}")),
(r"\alpha_{\beta''}", Symbol("alpha_{beta''}")),
(r"\alpha'_\beta", Symbol("alpha'_{beta}")),
(r"\alpha'_{\beta}", Symbol("alpha'_{beta}")),
(r"\alpha'_{\beta'}", Symbol("alpha'_{beta'}")),
(r"\alpha'_{\beta''}", Symbol("alpha'_{beta''}")),
(r"\alpha''_\beta", Symbol("alpha''_{beta}")),
(r"\alpha''_{\beta}", Symbol("alpha''_{beta}")),
(r"\alpha''_{\beta'}", Symbol("alpha''_{beta'}")),
(r"\alpha''_{\beta''}", Symbol("alpha''_{beta''}")),
(r"\alpha_\beta'", Symbol("alpha_{beta}'")),
(r"\alpha_{\beta}'", Symbol("alpha_{beta}'")),
(r"\alpha_{\beta'}'", Symbol("alpha_{beta'}'")),
(r"\alpha_{\beta''}'", Symbol("alpha_{beta''}'")),
(r"\alpha'_\beta'", Symbol("alpha'_{beta}'")),
(r"\alpha'_{\beta}'", Symbol("alpha'_{beta}'")),
(r"\alpha'_{\beta'}'", Symbol("alpha'_{beta'}'")),
(r"\alpha'_{\beta''}'", Symbol("alpha'_{beta''}'")),
(r"\alpha''_\beta'", Symbol("alpha''_{beta}'")),
(r"\alpha''_{\beta}'", Symbol("alpha''_{beta}'")),
(r"\alpha''_{\beta'}'", Symbol("alpha''_{beta'}'")),
(r"\alpha''_{\beta''}'", Symbol("alpha''_{beta''}'")),
(r"\alpha_\beta''", Symbol("alpha_{beta}''")),
(r"\alpha_{\beta}''", Symbol("alpha_{beta}''")),
(r"\alpha_{\beta'}''", Symbol("alpha_{beta'}''")),
(r"\alpha_{\beta''}''", Symbol("alpha_{beta''}''")),
(r"\alpha'_\beta''", Symbol("alpha'_{beta}''")),
(r"\alpha'_{\beta}''", Symbol("alpha'_{beta}''")),
(r"\alpha'_{\beta'}''", Symbol("alpha'_{beta'}''")),
(r"\alpha'_{\beta''}''", Symbol("alpha'_{beta''}''")),
(r"\alpha''_\beta''", Symbol("alpha''_{beta}''")),
(r"\alpha''_{\beta}''", Symbol("alpha''_{beta}''")),
(r"\alpha''_{\beta'}''", Symbol("alpha''_{beta'}''")),
(r"\alpha''_{\beta''}''", Symbol("alpha''_{beta''}''"))
]
UNEVALUATED_SIMPLE_EXPRESSION_PAIRS = [
(r"0", 0),
(r"1", 1),
(r"-3.14", -3.14),
(r"(-7.13)(1.5)", _Mul(-7.13, 1.5)),
(r"1+1", _Add(1, 1)),
(r"0+1", _Add(0, 1)),
(r"1*2", _Mul(1, 2)),
(r"0*1", _Mul(0, 1)),
(r"x", x),
(r"2x", 2 * x),
(r"3x - 1", _Add(_Mul(3, x), -1)),
(r"-c", -c),
(r"\infty", oo),
(r"a \cdot b", a * b),
(r"1 \times 2 ", _Mul(1, 2)),
(r"a / b", a / b),
(r"a \div b", a / b),
(r"a + b", a + b),
(r"a + b - a", _Add(a + b, -a)),
(r"(x + y) z", _Mul(_Add(x, y), z)),
(r"a'b+ab'", _Add(_Mul(Symbol("a'"), b), _Mul(a, Symbol("b'"))))
]
EVALUATED_SIMPLE_EXPRESSION_PAIRS = [
(r"(-7.13)(1.5)", -10.695),
(r"1+1", 2),
(r"0+1", 1),
(r"1*2", 2),
(r"0*1", 0),
(r"2x", 2 * x),
(r"3x - 1", 3 * x - 1),
(r"-c", -c),
(r"a \cdot b", a * b),
(r"1 \times 2 ", 2),
(r"a / b", a / b),
(r"a \div b", a / b),
(r"a + b", a + b),
(r"a + b - a", b),
(r"(x + y) z", (x + y) * z),
]
UNEVALUATED_FRACTION_EXPRESSION_PAIRS = [
(r"\frac{a}{b}", a / b),
(r"\dfrac{a}{b}", a / b),
(r"\tfrac{a}{b}", a / b),
(r"\frac12", _Mul(1, _Pow(2, -1))),
(r"\frac12y", _Mul(_Mul(1, _Pow(2, -1)), y)),
(r"\frac1234", _Mul(_Mul(1, _Pow(2, -1)), 34)),
(r"\frac2{3}", _Mul(2, _Pow(3, -1))),
(r"\frac{a + b}{c}", _Mul(a + b, _Pow(c, -1))),
(r"\frac{7}{3}", _Mul(7, _Pow(3, -1)))
]
EVALUATED_FRACTION_EXPRESSION_PAIRS = [
(r"\frac{a}{b}", a / b),
(r"\dfrac{a}{b}", a / b),
(r"\tfrac{a}{b}", a / b),
(r"\frac12", Rational(1, 2)),
(r"\frac12y", y / 2),
(r"\frac1234", 17),
(r"\frac2{3}", Rational(2, 3)),
(r"\frac{a + b}{c}", (a + b) / c),
(r"\frac{7}{3}", Rational(7, 3))
]
RELATION_EXPRESSION_PAIRS = [
(r"x = y", Eq(x, y)),
(r"x \neq y", Ne(x, y)),
(r"x < y", Lt(x, y)),
(r"x > y", Gt(x, y)),
(r"x \leq y", Le(x, y)),
(r"x \geq y", Ge(x, y)),
(r"x \le y", Le(x, y)),
(r"x \ge y", Ge(x, y)),
(r"x < y", StrictLessThan(x, y)),
(r"x \leq y", LessThan(x, y)),
(r"x > y", StrictGreaterThan(x, y)),
(r"x \geq y", GreaterThan(x, y)),
(r"x \neq y", Unequality(x, y)), # same as 2nd one in the list
(r"a^2 + b^2 = c^2", Eq(a**2 + b**2, c**2))
]
UNEVALUATED_POWER_EXPRESSION_PAIRS = [
(r"x^2", x ** 2),
(r"x^\frac{1}{2}", _Pow(x, _Mul(1, _Pow(2, -1)))),
(r"x^{3 + 1}", x ** _Add(3, 1)),
(r"\pi^{|xy|}", Symbol('pi') ** _Abs(x * y)),
(r"5^0 - 4^0", _Add(_Pow(5, 0), _Mul(-1, _Pow(4, 0))))
]
EVALUATED_POWER_EXPRESSION_PAIRS = [
(r"x^2", x ** 2),
(r"x^\frac{1}{2}", sqrt(x)),
(r"x^{3 + 1}", x ** 4),
(r"\pi^{|xy|}", Symbol('pi') ** _Abs(x * y)),
(r"5^0 - 4^0", 0)
]
UNEVALUATED_INTEGRAL_EXPRESSION_PAIRS = [
(r"\int x dx", Integral(_Mul(1, x), x)),
(r"\int x \, dx", Integral(_Mul(1, x), x)),
(r"\int x d\theta", Integral(_Mul(1, x), theta)),
(r"\int (x^2 - y)dx", Integral(_Mul(1, x ** 2 - y), x)),
(r"\int x + a dx", Integral(_Mul(1, _Add(x, a)), x)),
(r"\int da", Integral(_Mul(1, 1), a)),
(r"\int_0^7 dx", Integral(_Mul(1, 1), (x, 0, 7))),
(r"\int\limits_{0}^{1} x dx", Integral(_Mul(1, x), (x, 0, 1))),
(r"\int_a^b x dx", Integral(_Mul(1, x), (x, a, b))),
(r"\int^b_a x dx", Integral(_Mul(1, x), (x, a, b))),
(r"\int_{a}^b x dx", Integral(_Mul(1, x), (x, a, b))),
(r"\int^{b}_a x dx", Integral(_Mul(1, x), (x, a, b))),
(r"\int_{a}^{b} x dx", Integral(_Mul(1, x), (x, a, b))),
(r"\int^{b}_{a} x dx", Integral(_Mul(1, x), (x, a, b))),
(r"\int_{f(a)}^{f(b)} f(z) dz", Integral(f(z), (z, f(a), f(b)))),
(r"\int a + b + c dx", Integral(_Mul(1, _Add(_Add(a, b), c)), x)),
(r"\int \frac{dz}{z}", Integral(_Mul(1, _Mul(1, Pow(z, -1))), z)),
(r"\int \frac{3 dz}{z}", Integral(_Mul(1, _Mul(3, _Pow(z, -1))), z)),
(r"\int \frac{1}{x} dx", Integral(_Mul(1, _Mul(1, Pow(x, -1))), x)),
(r"\int \frac{1}{a} + \frac{1}{b} dx",
Integral(_Mul(1, _Add(_Mul(1, _Pow(a, -1)), _Mul(1, Pow(b, -1)))), x)),
(r"\int \frac{1}{x} + 1 dx", Integral(_Mul(1, _Add(_Mul(1, _Pow(x, -1)), 1)), x))
]
EVALUATED_INTEGRAL_EXPRESSION_PAIRS = [
(r"\int x dx", Integral(x, x)),
(r"\int x \, dx", Integral(x, x)),
(r"\int x d\theta", Integral(x, theta)),
(r"\int (x^2 - y)dx", Integral(x ** 2 - y, x)),
(r"\int x + a dx", Integral(x + a, x)),
(r"\int da", Integral(1, a)),
(r"\int_0^7 dx", Integral(1, (x, 0, 7))),
(r"\int\limits_{0}^{1} x dx", Integral(x, (x, 0, 1))),
(r"\int_a^b x dx", Integral(x, (x, a, b))),
(r"\int^b_a x dx", Integral(x, (x, a, b))),
(r"\int_{a}^b x dx", Integral(x, (x, a, b))),
(r"\int^{b}_a x dx", Integral(x, (x, a, b))),
(r"\int_{a}^{b} x dx", Integral(x, (x, a, b))),
(r"\int^{b}_{a} x dx", Integral(x, (x, a, b))),
(r"\int_{f(a)}^{f(b)} f(z) dz", Integral(f(z), (z, f(a), f(b)))),
(r"\int a + b + c dx", Integral(a + b + c, x)),
(r"\int \frac{dz}{z}", Integral(Pow(z, -1), z)),
(r"\int \frac{3 dz}{z}", Integral(3 * Pow(z, -1), z)),
(r"\int \frac{1}{x} dx", Integral(1 / x, x)),
(r"\int \frac{1}{a} + \frac{1}{b} dx", Integral(1 / a + 1 / b, x)),
(r"\int \frac{1}{a} - \frac{1}{b} dx", Integral(1 / a - 1 / b, x)),
(r"\int \frac{1}{x} + 1 dx", Integral(1 / x + 1, x))
]
DERIVATIVE_EXPRESSION_PAIRS = [
(r"\frac{d}{dx} x", Derivative(x, x)),
(r"\frac{d}{dt} x", Derivative(x, t)),
(r"\frac{d}{dx} ( \tan x )", Derivative(tan(x), x)),
(r"\frac{d f(x)}{dx}", Derivative(f(x), x)),
(r"\frac{d\theta(x)}{dx}", Derivative(Function('theta')(x), x))
]
TRIGONOMETRIC_EXPRESSION_PAIRS = [
(r"\sin \theta", sin(theta)),
(r"\sin(\theta)", sin(theta)),
(r"\sin^{-1} a", asin(a)),
(r"\sin a \cos b", _Mul(sin(a), cos(b))),
(r"\sin \cos \theta", sin(cos(theta))),
(r"\sin(\cos \theta)", sin(cos(theta))),
(r"(\csc x)(\sec y)", csc(x) * sec(y)),
(r"\frac{\sin{x}}2", _Mul(sin(x), _Pow(2, -1)))
]
UNEVALUATED_LIMIT_EXPRESSION_PAIRS = [
(r"\lim_{x \to 3} a", Limit(a, x, 3, dir="+-")),
(r"\lim_{x \rightarrow 3} a", Limit(a, x, 3, dir="+-")),
(r"\lim_{x \Rightarrow 3} a", Limit(a, x, 3, dir="+-")),
(r"\lim_{x \longrightarrow 3} a", Limit(a, x, 3, dir="+-")),
(r"\lim_{x \Longrightarrow 3} a", Limit(a, x, 3, dir="+-")),
(r"\lim_{x \to 3^{+}} a", Limit(a, x, 3, dir="+")),
(r"\lim_{x \to 3^{-}} a", Limit(a, x, 3, dir="-")),
(r"\lim_{x \to 3^+} a", Limit(a, x, 3, dir="+")),
(r"\lim_{x \to 3^-} a", Limit(a, x, 3, dir="-")),
(r"\lim_{x \to \infty} \frac{1}{x}", Limit(_Mul(1, _Pow(x, -1)), x, oo))
]
EVALUATED_LIMIT_EXPRESSION_PAIRS = [
(r"\lim_{x \to \infty} \frac{1}{x}", Limit(1 / x, x, oo))
]
UNEVALUATED_SQRT_EXPRESSION_PAIRS = [
(r"\sqrt{x}", sqrt(x)),
(r"\sqrt{x + b}", sqrt(_Add(x, b))),
(r"\sqrt[3]{\sin x}", _Pow(sin(x), _Pow(3, -1))),
# the above test needed to be handled differently than the ones below because root
# acts differently if its second argument is a number
(r"\sqrt[y]{\sin x}", root(sin(x), y)),
(r"\sqrt[\theta]{\sin x}", root(sin(x), theta)),
(r"\sqrt{\frac{12}{6}}", _Sqrt(_Mul(12, _Pow(6, -1))))
]
EVALUATED_SQRT_EXPRESSION_PAIRS = [
(r"\sqrt{x}", sqrt(x)),
(r"\sqrt{x + b}", sqrt(x + b)),
(r"\sqrt[3]{\sin x}", root(sin(x), 3)),
(r"\sqrt[y]{\sin x}", root(sin(x), y)),
(r"\sqrt[\theta]{\sin x}", root(sin(x), theta)),
(r"\sqrt{\frac{12}{6}}", sqrt(2))
]
UNEVALUATED_FACTORIAL_EXPRESSION_PAIRS = [
(r"x!", _factorial(x)),
(r"100!", _factorial(100)),
(r"\theta!", _factorial(theta)),
(r"(x + 1)!", _factorial(_Add(x, 1))),
(r"(x!)!", _factorial(_factorial(x))),
(r"x!!!", _factorial(_factorial(_factorial(x)))),
(r"5!7!", _Mul(_factorial(5), _factorial(7)))
]
EVALUATED_FACTORIAL_EXPRESSION_PAIRS = [
(r"x!", factorial(x)),
(r"100!", factorial(100)),
(r"\theta!", factorial(theta)),
(r"(x + 1)!", factorial(x + 1)),
(r"(x!)!", factorial(factorial(x))),
(r"x!!!", factorial(factorial(factorial(x)))),
(r"5!7!", factorial(5) * factorial(7)),
(r"24! \times 24!", factorial(24) * factorial(24))
]
UNEVALUATED_SUM_EXPRESSION_PAIRS = [
(r"\sum_{k = 1}^{3} c", Sum(_Mul(1, c), (k, 1, 3))),
(r"\sum_{k = 1}^3 c", Sum(_Mul(1, c), (k, 1, 3))),
(r"\sum^{3}_{k = 1} c", Sum(_Mul(1, c), (k, 1, 3))),
(r"\sum^3_{k = 1} c", Sum(_Mul(1, c), (k, 1, 3))),
(r"\sum_{k = 1}^{10} k^2", Sum(_Mul(1, k ** 2), (k, 1, 10))),
(r"\sum_{n = 0}^{\infty} \frac{1}{n!}",
Sum(_Mul(1, _Mul(1, _Pow(_factorial(n), -1))), (n, 0, oo)))
]
EVALUATED_SUM_EXPRESSION_PAIRS = [
(r"\sum_{k = 1}^{3} c", Sum(c, (k, 1, 3))),
(r"\sum_{k = 1}^3 c", Sum(c, (k, 1, 3))),
(r"\sum^{3}_{k = 1} c", Sum(c, (k, 1, 3))),
(r"\sum^3_{k = 1} c", Sum(c, (k, 1, 3))),
(r"\sum_{k = 1}^{10} k^2", Sum(k ** 2, (k, 1, 10))),
(r"\sum_{n = 0}^{\infty} \frac{1}{n!}", Sum(1 / factorial(n), (n, 0, oo)))
]
UNEVALUATED_PRODUCT_EXPRESSION_PAIRS = [
(r"\prod_{a = b}^{c} x", Product(x, (a, b, c))),
(r"\prod_{a = b}^c x", Product(x, (a, b, c))),
(r"\prod^{c}_{a = b} x", Product(x, (a, b, c))),
(r"\prod^c_{a = b} x", Product(x, (a, b, c)))
]
APPLIED_FUNCTION_EXPRESSION_PAIRS = [
(r"f(x)", f(x)),
(r"f(x, y)", f(x, y)),
(r"f(x, y, z)", f(x, y, z)),
(r"f'_1(x)", Function("f_{1}'")(x)),
(r"f_{1}''(x+y)", Function("f_{1}''")(x + y)),
(r"h_{\theta}(x_0, x_1)",
Function('h_{theta}')(Symbol('x_{0}'), Symbol('x_{1}')))
]
UNEVALUATED_COMMON_FUNCTION_EXPRESSION_PAIRS = [
(r"|x|", _Abs(x)),
(r"||x||", _Abs(Abs(x))),
(r"|x||y|", _Abs(x) * _Abs(y)),
(r"||x||y||", _Abs(_Abs(x) * _Abs(y))),
(r"\lfloor x \rfloor", floor(x)),
(r"\lceil x \rceil", ceiling(x)),
(r"\exp x", _exp(x)),
(r"\exp(x)", _exp(x)),
(r"\lg x", _log(x, 10)),
(r"\ln x", _log(x)),
(r"\ln xy", _log(x * y)),
(r"\log x", _log(x)),
(r"\log xy", _log(x * y)),
(r"\log_{2} x", _log(x, 2)),
(r"\log_{a} x", _log(x, a)),
(r"\log_{11} x", _log(x, 11)),
(r"\log_{a^2} x", _log(x, _Pow(a, 2))),
(r"\log_2 x", _log(x, 2)),
(r"\log_a x", _log(x, a)),
(r"\overline{z}", _Conjugate(z)),
(r"\overline{\overline{z}}", _Conjugate(_Conjugate(z))),
(r"\overline{x + y}", _Conjugate(_Add(x, y))),
(r"\overline{x} + \overline{y}", _Conjugate(x) + _Conjugate(y)),
(r"\min(a, b)", _Min(a, b)),
(r"\min(a, b, c - d, xy)", _Min(a, b, c - d, x * y)),
(r"\max(a, b)", _Max(a, b)),
(r"\max(a, b, c - d, xy)", _Max(a, b, c - d, x * y)),
# physics things don't have an `evaluate=False` variant
(r"\langle x |", Bra('x')),
(r"| x \rangle", Ket('x')),
(r"\langle x | y \rangle", InnerProduct(Bra('x'), Ket('y'))),
]
EVALUATED_COMMON_FUNCTION_EXPRESSION_PAIRS = [
(r"|x|", Abs(x)),
(r"||x||", Abs(Abs(x))),
(r"|x||y|", Abs(x) * Abs(y)),
(r"||x||y||", Abs(Abs(x) * Abs(y))),
(r"\lfloor x \rfloor", floor(x)),
(r"\lceil x \rceil", ceiling(x)),
(r"\exp x", exp(x)),
(r"\exp(x)", exp(x)),
(r"\lg x", log(x, 10)),
(r"\ln x", log(x)),
(r"\ln xy", log(x * y)),
(r"\log x", log(x)),
(r"\log xy", log(x * y)),
(r"\log_{2} x", log(x, 2)),
(r"\log_{a} x", log(x, a)),
(r"\log_{11} x", log(x, 11)),
(r"\log_{a^2} x", log(x, _Pow(a, 2))),
(r"\log_2 x", log(x, 2)),
(r"\log_a x", log(x, a)),
(r"\overline{z}", conjugate(z)),
(r"\overline{\overline{z}}", conjugate(conjugate(z))),
(r"\overline{x + y}", conjugate(x + y)),
(r"\overline{x} + \overline{y}", conjugate(x) + conjugate(y)),
(r"\min(a, b)", Min(a, b)),
(r"\min(a, b, c - d, xy)", Min(a, b, c - d, x * y)),
(r"\max(a, b)", Max(a, b)),
(r"\max(a, b, c - d, xy)", Max(a, b, c - d, x * y)),
(r"\langle x |", Bra('x')),
(r"| x \rangle", Ket('x')),
(r"\langle x | y \rangle", InnerProduct(Bra('x'), Ket('y'))),
]
SPACING_RELATED_EXPRESSION_PAIRS = [
(r"a \, b", _Mul(a, b)),
(r"a \thinspace b", _Mul(a, b)),
(r"a \: b", _Mul(a, b)),
(r"a \medspace b", _Mul(a, b)),
(r"a \; b", _Mul(a, b)),
(r"a \thickspace b", _Mul(a, b)),
(r"a \quad b", _Mul(a, b)),
(r"a \qquad b", _Mul(a, b)),
(r"a \! b", _Mul(a, b)),
(r"a \negthinspace b", _Mul(a, b)),
(r"a \negmedspace b", _Mul(a, b)),
(r"a \negthickspace b", _Mul(a, b))
]
UNEVALUATED_BINOMIAL_EXPRESSION_PAIRS = [
(r"\binom{n}{k}", _binomial(n, k)),
(r"\tbinom{n}{k}", _binomial(n, k)),
(r"\dbinom{n}{k}", _binomial(n, k)),
(r"\binom{n}{0}", _binomial(n, 0)),
(r"x^\binom{n}{k}", _Pow(x, _binomial(n, k)))
]
EVALUATED_BINOMIAL_EXPRESSION_PAIRS = [
(r"\binom{n}{k}", binomial(n, k)),
(r"\tbinom{n}{k}", binomial(n, k)),
(r"\dbinom{n}{k}", binomial(n, k)),
(r"\binom{n}{0}", binomial(n, 0)),
(r"x^\binom{n}{k}", x ** binomial(n, k))
]
MISCELLANEOUS_EXPRESSION_PAIRS = [
(r"\left(x + y\right) z", _Mul(_Add(x, y), z)),
(r"\left( x + y\right ) z", _Mul(_Add(x, y), z)),
(r"\left( x + y\right ) z", _Mul(_Add(x, y), z)),
]
UNEVALUATED_LITERAL_COMPLEX_NUMBER_EXPRESSION_PAIRS = [
(r"\imaginaryunit^2", _Pow(I, 2)),
(r"|\imaginaryunit|", _Abs(I)),
(r"\overline{\imaginaryunit}", _Conjugate(I)),
(r"\imaginaryunit+\imaginaryunit", _Add(I, I)),
(r"\imaginaryunit-\imaginaryunit", _Add(I, -I)),
(r"\imaginaryunit*\imaginaryunit", _Mul(I, I)),
(r"\imaginaryunit/\imaginaryunit", _Mul(I, _Pow(I, -1))),
(r"(1+\imaginaryunit)/|1+\imaginaryunit|", _Mul(_Add(1, I), _Pow(_Abs(_Add(1, I)), -1)))
]
UNEVALUATED_MATRIX_EXPRESSION_PAIRS = [
(r"\begin{pmatrix}a & b \\x & y\end{pmatrix}",
Matrix([[a, b], [x, y]])),
(r"\begin{pmatrix}a & b \\x & y\\\end{pmatrix}",
Matrix([[a, b], [x, y]])),
(r"\begin{bmatrix}a & b \\x & y\end{bmatrix}",
Matrix([[a, b], [x, y]])),
(r"\left(\begin{matrix}a & b \\x & y\end{matrix}\right)",
Matrix([[a, b], [x, y]])),
(r"\left[\begin{matrix}a & b \\x & y\end{matrix}\right]",
Matrix([[a, b], [x, y]])),
(r"\left[\begin{array}{cc}a & b \\x & y\end{array}\right]",
Matrix([[a, b], [x, y]])),
(r"\left(\begin{array}{cc}a & b \\x & y\end{array}\right)",
Matrix([[a, b], [x, y]])),
(r"\left( { \begin{array}{cc}a & b \\x & y\end{array} } \right)",
Matrix([[a, b], [x, y]])),
(r"+\begin{pmatrix}a & b \\x & y\end{pmatrix}",
Matrix([[a, b], [x, y]])),
((r"\begin{pmatrix}x & y \\a & b\end{pmatrix}+"
r"\begin{pmatrix}a & b \\x & y\end{pmatrix}"),
_MatAdd(Matrix([[x, y], [a, b]]), Matrix([[a, b], [x, y]]))),
(r"-\begin{pmatrix}a & b \\x & y\end{pmatrix}",
_MatMul(-1, Matrix([[a, b], [x, y]]))),
((r"\begin{pmatrix}x & y \\a & b\end{pmatrix}-"
r"\begin{pmatrix}a & b \\x & y\end{pmatrix}"),
_MatAdd(Matrix([[x, y], [a, b]]), _MatMul(-1, Matrix([[a, b], [x, y]])))),
((r"\begin{pmatrix}a & b & c \\x & y & z \\a & b & c \end{pmatrix}*"
r"\begin{pmatrix}x & y & z \\a & b & c \\a & b & c \end{pmatrix}*"
r"\begin{pmatrix}a & b & c \\x & y & z \\x & y & z \end{pmatrix}"),
_MatMul(_MatMul(Matrix([[a, b, c], [x, y, z], [a, b, c]]),
Matrix([[x, y, z], [a, b, c], [a, b, c]])),
Matrix([[a, b, c], [x, y, z], [x, y, z]]))),
(r"\begin{pmatrix}a & b \\x & y\end{pmatrix}/2",
_MatMul(Matrix([[a, b], [x, y]]), _Pow(2, -1))),
(r"\begin{pmatrix}a & b \\x & y\end{pmatrix}^2",
_Pow(Matrix([[a, b], [x, y]]), 2)),
(r"\begin{pmatrix}a & b \\x & y\end{pmatrix}^{-1}",
_Pow(Matrix([[a, b], [x, y]]), -1)),
(r"\begin{pmatrix}a & b \\x & y\end{pmatrix}^T",
Transpose(Matrix([[a, b], [x, y]]))),
(r"\begin{pmatrix}a & b \\x & y\end{pmatrix}^{T}",
Transpose(Matrix([[a, b], [x, y]]))),
(r"\begin{pmatrix}a & b \\x & y\end{pmatrix}^\mathit{T}",
Transpose(Matrix([[a, b], [x, y]]))),
(r"\begin{pmatrix}1 & 2 \\3 & 4\end{pmatrix}^T",
Transpose(Matrix([[1, 2], [3, 4]]))),
((r"(\begin{pmatrix}1 & 2 \\3 & 4\end{pmatrix}+"
r"\begin{pmatrix}1 & 2 \\3 & 4\end{pmatrix}^T)*"
r"\begin{bmatrix}1\\0\end{bmatrix}"),
_MatMul(_MatAdd(Matrix([[1, 2], [3, 4]]),
Transpose(Matrix([[1, 2], [3, 4]]))),
Matrix([[1], [0]]))),
((r"(\begin{pmatrix}a & b \\x & y\end{pmatrix}+"
r"\begin{pmatrix}x & y \\a & b\end{pmatrix})^2"),
_Pow(_MatAdd(Matrix([[a, b], [x, y]]),
Matrix([[x, y], [a, b]])), 2)),
((r"(\begin{pmatrix}a & b \\x & y\end{pmatrix}+"
r"\begin{pmatrix}x & y \\a & b\end{pmatrix})^T"),
Transpose(_MatAdd(Matrix([[a, b], [x, y]]),
Matrix([[x, y], [a, b]])))),
(r"\overline{\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}}",
_Conjugate(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]]))))
]
EVALUATED_MATRIX_EXPRESSION_PAIRS = [
(r"\det\left(\left[ { \begin{array}{cc}a&b\\x&y\end{array} } \right]\right)",
Matrix([[a, b], [x, y]]).det()),
(r"\det \begin{pmatrix}1&2\\3&4\end{pmatrix}", -2),
(r"\det{\begin{pmatrix}1&2\\3&4\end{pmatrix}}", -2),
(r"\det(\begin{pmatrix}1&2\\3&4\end{pmatrix})", -2),
(r"\det\left(\begin{pmatrix}1&2\\3&4\end{pmatrix}\right)", -2),
(r"\begin{pmatrix}a & b \\x & y\end{pmatrix}/\begin{vmatrix}a & b \\x & y\end{vmatrix}",
_MatMul(Matrix([[a, b], [x, y]]), _Pow(Matrix([[a, b], [x, y]]).det(), -1))),
(r"\begin{pmatrix}a & b \\x & y\end{pmatrix}/|\begin{matrix}a & b \\x & y\end{matrix}|",
_MatMul(Matrix([[a, b], [x, y]]), _Pow(Matrix([[a, b], [x, y]]).det(), -1))),
(r"\frac{\begin{pmatrix}a & b \\x & y\end{pmatrix}}{| { \begin{matrix}a & b \\x & y\end{matrix} } |}",
_MatMul(Matrix([[a, b], [x, y]]), _Pow(Matrix([[a, b], [x, y]]).det(), -1))),
(r"\overline{\begin{pmatrix}\imaginaryunit & 1+\imaginaryunit \\-\imaginaryunit & 4\end{pmatrix}}",
Matrix([[-I, 1-I], [I, 4]])),
(r"\begin{pmatrix}\imaginaryunit & 1+\imaginaryunit \\-\imaginaryunit & 4\end{pmatrix}^H",
Matrix([[-I, I], [1-I, 4]])),
(r"\trace(\begin{pmatrix}\imaginaryunit & 1+\imaginaryunit \\-\imaginaryunit & 4\end{pmatrix})",
Trace(Matrix([[I, 1+I], [-I, 4]]))),
(r"\adjugate(\begin{pmatrix}1 & 2 \\3 & 4\end{pmatrix})",
Matrix([[4, -2], [-3, 1]])),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^\ast",
Matrix([[-2*I, 6], [4, 8]])),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{\ast}",
Matrix([[-2*I, 6], [4, 8]])),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{\ast\ast}",
Matrix([[2*I, 4], [6, 8]])),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{\ast\ast\ast}",
Matrix([[-2*I, 6], [4, 8]])),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{*}",
Matrix([[-2*I, 6], [4, 8]])),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{**}",
Matrix([[2*I, 4], [6, 8]])),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{***}",
Matrix([[-2*I, 6], [4, 8]])),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^\prime",
Transpose(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]])))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{\prime}",
Transpose(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]])))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{\prime\prime}",
_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]]))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{\prime\prime\prime}",
Transpose(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]])))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{'}",
Transpose(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]])))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{''}",
_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]]))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^{'''}",
Transpose(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]])))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})'",
Transpose(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]])))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})''",
_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]]))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})'''",
Transpose(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]])))),
(r"\det(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})",
(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]]))).det()),
(r"\trace(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})",
Trace(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]])))),
(r"\adjugate(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})",
(Matrix([[8, -4], [-6, 2*I]]))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^T",
Transpose(_MatAdd(Matrix([[I, 2], [3, 4]]),
Matrix([[I, 2], [3, 4]])))),
(r"(\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix}+\begin{pmatrix}\imaginaryunit&2\\3&4\end{pmatrix})^H",
(Matrix([[-2*I, 6], [4, 8]])))
]
def test_symbol_expressions():
expected_failures = {6, 7}
for i, (latex_str, sympy_expr) in enumerate(SYMBOL_EXPRESSION_PAIRS):
if i in expected_failures:
continue
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_simple_expressions():
expected_failures = {20}
for i, (latex_str, sympy_expr) in enumerate(UNEVALUATED_SIMPLE_EXPRESSION_PAIRS):
if i in expected_failures:
continue
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for i, (latex_str, sympy_expr) in enumerate(EVALUATED_SIMPLE_EXPRESSION_PAIRS):
if i in expected_failures:
continue
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_fraction_expressions():
for latex_str, sympy_expr in UNEVALUATED_FRACTION_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for latex_str, sympy_expr in EVALUATED_FRACTION_EXPRESSION_PAIRS:
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_relation_expressions():
for latex_str, sympy_expr in RELATION_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_power_expressions():
expected_failures = {3}
for i, (latex_str, sympy_expr) in enumerate(UNEVALUATED_POWER_EXPRESSION_PAIRS):
if i in expected_failures:
continue
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for i, (latex_str, sympy_expr) in enumerate(EVALUATED_POWER_EXPRESSION_PAIRS):
if i in expected_failures:
continue
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_integral_expressions():
expected_failures = {14}
for i, (latex_str, sympy_expr) in enumerate(UNEVALUATED_INTEGRAL_EXPRESSION_PAIRS):
if i in expected_failures:
continue
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, i
for i, (latex_str, sympy_expr) in enumerate(EVALUATED_INTEGRAL_EXPRESSION_PAIRS):
if i in expected_failures:
continue
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_derivative_expressions():
expected_failures = {3, 4}
for i, (latex_str, sympy_expr) in enumerate(DERIVATIVE_EXPRESSION_PAIRS):
if i in expected_failures:
continue
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for i, (latex_str, sympy_expr) in enumerate(DERIVATIVE_EXPRESSION_PAIRS):
if i in expected_failures:
continue
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_trigonometric_expressions():
expected_failures = {3}
for i, (latex_str, sympy_expr) in enumerate(TRIGONOMETRIC_EXPRESSION_PAIRS):
if i in expected_failures:
continue
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_limit_expressions():
for latex_str, sympy_expr in UNEVALUATED_LIMIT_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_square_root_expressions():
for latex_str, sympy_expr in UNEVALUATED_SQRT_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for latex_str, sympy_expr in EVALUATED_SQRT_EXPRESSION_PAIRS:
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_factorial_expressions():
for latex_str, sympy_expr in UNEVALUATED_FACTORIAL_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for latex_str, sympy_expr in EVALUATED_FACTORIAL_EXPRESSION_PAIRS:
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_sum_expressions():
for latex_str, sympy_expr in UNEVALUATED_SUM_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for latex_str, sympy_expr in EVALUATED_SUM_EXPRESSION_PAIRS:
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_product_expressions():
for latex_str, sympy_expr in UNEVALUATED_PRODUCT_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
@XFAIL
def test_applied_function_expressions():
expected_failures = {0, 3, 4} # 0 is ambiguous, and the others require not-yet-added features
# not sure why 1, and 2 are failing
for i, (latex_str, sympy_expr) in enumerate(APPLIED_FUNCTION_EXPRESSION_PAIRS):
if i in expected_failures:
continue
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_common_function_expressions():
for latex_str, sympy_expr in UNEVALUATED_COMMON_FUNCTION_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for latex_str, sympy_expr in EVALUATED_COMMON_FUNCTION_EXPRESSION_PAIRS:
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
# unhandled bug causing these to fail
@XFAIL
def test_spacing():
for latex_str, sympy_expr in SPACING_RELATED_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_binomial_expressions():
for latex_str, sympy_expr in UNEVALUATED_BINOMIAL_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for latex_str, sympy_expr in EVALUATED_BINOMIAL_EXPRESSION_PAIRS:
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_miscellaneous_expressions():
for latex_str, sympy_expr in MISCELLANEOUS_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_literal_complex_number_expressions():
for latex_str, sympy_expr in UNEVALUATED_LITERAL_COMPLEX_NUMBER_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
def test_matrix_expressions():
for latex_str, sympy_expr in UNEVALUATED_MATRIX_EXPRESSION_PAIRS:
with evaluate(False):
assert parse_latex_lark(latex_str) == sympy_expr, latex_str
for latex_str, sympy_expr in EVALUATED_MATRIX_EXPRESSION_PAIRS:
assert parse_latex_lark(latex_str) == sympy_expr, latex_str

View File

@@ -0,0 +1,280 @@
from sympy import sin, Function, symbols, Dummy, Lambda, cos
from sympy.parsing.mathematica import parse_mathematica, MathematicaParser
from sympy.core.sympify import sympify
from sympy.abc import n, w, x, y, z
from sympy.testing.pytest import raises
def test_mathematica():
d = {
'- 6x': '-6*x',
'Sin[x]^2': 'sin(x)**2',
'2(x-1)': '2*(x-1)',
'3y+8': '3*y+8',
'ArcSin[2x+9(4-x)^2]/x': 'asin(2*x+9*(4-x)**2)/x',
'x+y': 'x+y',
'355/113': '355/113',
'2.718281828': '2.718281828',
'Cos(1/2 * π)': 'Cos(π/2)',
'Sin[12]': 'sin(12)',
'Exp[Log[4]]': 'exp(log(4))',
'(x+1)(x+3)': '(x+1)*(x+3)',
'Cos[ArcCos[3.6]]': 'cos(acos(3.6))',
'Cos[x]==Sin[y]': 'Eq(cos(x), sin(y))',
'2*Sin[x+y]': '2*sin(x+y)',
'Sin[x]+Cos[y]': 'sin(x)+cos(y)',
'Sin[Cos[x]]': 'sin(cos(x))',
'2*Sqrt[x+y]': '2*sqrt(x+y)', # Test case from the issue 4259
'+Sqrt[2]': 'sqrt(2)',
'-Sqrt[2]': '-sqrt(2)',
'-1/Sqrt[2]': '-1/sqrt(2)',
'-(1/Sqrt[3])': '-(1/sqrt(3))',
'1/(2*Sqrt[5])': '1/(2*sqrt(5))',
'Mod[5,3]': 'Mod(5,3)',
'-Mod[5,3]': '-Mod(5,3)',
'(x+1)y': '(x+1)*y',
'x(y+1)': 'x*(y+1)',
'Sin[x]Cos[y]': 'sin(x)*cos(y)',
'Sin[x]^2Cos[y]^2': 'sin(x)**2*cos(y)**2',
'Cos[x]^2(1 - Cos[y]^2)': 'cos(x)**2*(1-cos(y)**2)',
'x y': 'x*y',
'x y': 'x*y',
'2 x': '2*x',
'x 8': 'x*8',
'2 8': '2*8',
'4.x': '4.*x',
'4. 3': '4.*3',
'4. 3.': '4.*3.',
'1 2 3': '1*2*3',
' - 2 * Sqrt[ 2 3 * ( 1 + 5 ) ] ': '-2*sqrt(2*3*(1+5))',
'Log[2,4]': 'log(4,2)',
'Log[Log[2,4],4]': 'log(4,log(4,2))',
'Exp[Sqrt[2]^2Log[2, 8]]': 'exp(sqrt(2)**2*log(8,2))',
'ArcSin[Cos[0]]': 'asin(cos(0))',
'Log2[16]': 'log(16,2)',
'Max[1,-2,3,-4]': 'Max(1,-2,3,-4)',
'Min[1,-2,3]': 'Min(1,-2,3)',
'Exp[I Pi/2]': 'exp(I*pi/2)',
'ArcTan[x,y]': 'atan2(y,x)',
'Pochhammer[x,y]': 'rf(x,y)',
'ExpIntegralEi[x]': 'Ei(x)',
'SinIntegral[x]': 'Si(x)',
'CosIntegral[x]': 'Ci(x)',
'AiryAi[x]': 'airyai(x)',
'AiryAiPrime[5]': 'airyaiprime(5)',
'AiryBi[x]': 'airybi(x)',
'AiryBiPrime[7]': 'airybiprime(7)',
'LogIntegral[4]': ' li(4)',
'PrimePi[7]': 'primepi(7)',
'Prime[5]': 'prime(5)',
'PrimeQ[5]': 'isprime(5)',
'Rational[2,19]': 'Rational(2,19)', # test case for issue 25716
}
for e in d:
assert parse_mathematica(e) == sympify(d[e])
# The parsed form of this expression should not evaluate the Lambda object:
assert parse_mathematica("Sin[#]^2 + Cos[#]^2 &[x]") == sin(x)**2 + cos(x)**2
d1, d2, d3 = symbols("d1:4", cls=Dummy)
assert parse_mathematica("Sin[#] + Cos[#3] &").dummy_eq(Lambda((d1, d2, d3), sin(d1) + cos(d3)))
assert parse_mathematica("Sin[#^2] &").dummy_eq(Lambda(d1, sin(d1**2)))
assert parse_mathematica("Function[x, x^3]") == Lambda(x, x**3)
assert parse_mathematica("Function[{x, y}, x^2 + y^2]") == Lambda((x, y), x**2 + y**2)
def test_parser_mathematica_tokenizer():
parser = MathematicaParser()
chain = lambda expr: parser._from_tokens_to_fullformlist(parser._from_mathematica_to_tokens(expr))
# Basic patterns
assert chain("x") == "x"
assert chain("42") == "42"
assert chain(".2") == ".2"
assert chain("+x") == "x"
assert chain("-1") == "-1"
assert chain("- 3") == "-3"
assert chain("α") == "α"
assert chain("+Sin[x]") == ["Sin", "x"]
assert chain("-Sin[x]") == ["Times", "-1", ["Sin", "x"]]
assert chain("x(a+1)") == ["Times", "x", ["Plus", "a", "1"]]
assert chain("(x)") == "x"
assert chain("(+x)") == "x"
assert chain("-a") == ["Times", "-1", "a"]
assert chain("(-x)") == ["Times", "-1", "x"]
assert chain("(x + y)") == ["Plus", "x", "y"]
assert chain("3 + 4") == ["Plus", "3", "4"]
assert chain("a - 3") == ["Plus", "a", "-3"]
assert chain("a - b") == ["Plus", "a", ["Times", "-1", "b"]]
assert chain("7 * 8") == ["Times", "7", "8"]
assert chain("a + b*c") == ["Plus", "a", ["Times", "b", "c"]]
assert chain("a + b* c* d + 2 * e") == ["Plus", "a", ["Times", "b", "c", "d"], ["Times", "2", "e"]]
assert chain("a / b") == ["Times", "a", ["Power", "b", "-1"]]
# Missing asterisk (*) patterns:
assert chain("x y") == ["Times", "x", "y"]
assert chain("3 4") == ["Times", "3", "4"]
assert chain("a[b] c") == ["Times", ["a", "b"], "c"]
assert chain("(x) (y)") == ["Times", "x", "y"]
assert chain("3 (a)") == ["Times", "3", "a"]
assert chain("(a) b") == ["Times", "a", "b"]
assert chain("4.2") == "4.2"
assert chain("4 2") == ["Times", "4", "2"]
assert chain("4 2") == ["Times", "4", "2"]
assert chain("3 . 4") == ["Dot", "3", "4"]
assert chain("4. 2") == ["Times", "4.", "2"]
assert chain("x.y") == ["Dot", "x", "y"]
assert chain("4.y") == ["Times", "4.", "y"]
assert chain("4 .y") == ["Dot", "4", "y"]
assert chain("x.4") == ["Times", "x", ".4"]
assert chain("x0.3") == ["Times", "x0", ".3"]
assert chain("x. 4") == ["Dot", "x", "4"]
# Comments
assert chain("a (* +b *) + c") == ["Plus", "a", "c"]
assert chain("a (* + b *) + (**)c (* +d *) + e") == ["Plus", "a", "c", "e"]
assert chain("""a + (*
+ b
*) c + (* d
*) e
""") == ["Plus", "a", "c", "e"]
# Operators couples + and -, * and / are mutually associative:
# (i.e. expression gets flattened when mixing these operators)
assert chain("a*b/c") == ["Times", "a", "b", ["Power", "c", "-1"]]
assert chain("a/b*c") == ["Times", "a", ["Power", "b", "-1"], "c"]
assert chain("a+b-c") == ["Plus", "a", "b", ["Times", "-1", "c"]]
assert chain("a-b+c") == ["Plus", "a", ["Times", "-1", "b"], "c"]
assert chain("-a + b -c ") == ["Plus", ["Times", "-1", "a"], "b", ["Times", "-1", "c"]]
assert chain("a/b/c*d") == ["Times", "a", ["Power", "b", "-1"], ["Power", "c", "-1"], "d"]
assert chain("a/b/c") == ["Times", "a", ["Power", "b", "-1"], ["Power", "c", "-1"]]
assert chain("a-b-c") == ["Plus", "a", ["Times", "-1", "b"], ["Times", "-1", "c"]]
assert chain("1/a") == ["Times", "1", ["Power", "a", "-1"]]
assert chain("1/a/b") == ["Times", "1", ["Power", "a", "-1"], ["Power", "b", "-1"]]
assert chain("-1/a*b") == ["Times", "-1", ["Power", "a", "-1"], "b"]
# Enclosures of various kinds, i.e. ( ) [ ] [[ ]] { }
assert chain("(a + b) + c") == ["Plus", ["Plus", "a", "b"], "c"]
assert chain(" a + (b + c) + d ") == ["Plus", "a", ["Plus", "b", "c"], "d"]
assert chain("a * (b + c)") == ["Times", "a", ["Plus", "b", "c"]]
assert chain("a b (c d)") == ["Times", "a", "b", ["Times", "c", "d"]]
assert chain("{a, b, 2, c}") == ["List", "a", "b", "2", "c"]
assert chain("{a, {b, c}}") == ["List", "a", ["List", "b", "c"]]
assert chain("{{a}}") == ["List", ["List", "a"]]
assert chain("a[b, c]") == ["a", "b", "c"]
assert chain("a[[b, c]]") == ["Part", "a", "b", "c"]
assert chain("a[b[c]]") == ["a", ["b", "c"]]
assert chain("a[[b, c[[d, {e,f}]]]]") == ["Part", "a", "b", ["Part", "c", "d", ["List", "e", "f"]]]
assert chain("a[b[[c,d]]]") == ["a", ["Part", "b", "c", "d"]]
assert chain("a[[b[c]]]") == ["Part", "a", ["b", "c"]]
assert chain("a[[b[[c]]]]") == ["Part", "a", ["Part", "b", "c"]]
assert chain("a[[b[c[[d]]]]]") == ["Part", "a", ["b", ["Part", "c", "d"]]]
assert chain("a[b[[c[d]]]]") == ["a", ["Part", "b", ["c", "d"]]]
assert chain("x[[a+1, b+2, c+3]]") == ["Part", "x", ["Plus", "a", "1"], ["Plus", "b", "2"], ["Plus", "c", "3"]]
assert chain("x[a+1, b+2, c+3]") == ["x", ["Plus", "a", "1"], ["Plus", "b", "2"], ["Plus", "c", "3"]]
assert chain("{a+1, b+2, c+3}") == ["List", ["Plus", "a", "1"], ["Plus", "b", "2"], ["Plus", "c", "3"]]
# Flat operator:
assert chain("a*b*c*d*e") == ["Times", "a", "b", "c", "d", "e"]
assert chain("a +b + c+ d+e") == ["Plus", "a", "b", "c", "d", "e"]
# Right priority operator:
assert chain("a^b") == ["Power", "a", "b"]
assert chain("a^b^c") == ["Power", "a", ["Power", "b", "c"]]
assert chain("a^b^c^d") == ["Power", "a", ["Power", "b", ["Power", "c", "d"]]]
# Left priority operator:
assert chain("a/.b") == ["ReplaceAll", "a", "b"]
assert chain("a/.b/.c/.d") == ["ReplaceAll", ["ReplaceAll", ["ReplaceAll", "a", "b"], "c"], "d"]
assert chain("a//b") == ["a", "b"]
assert chain("a//b//c") == [["a", "b"], "c"]
assert chain("a//b//c//d") == [[["a", "b"], "c"], "d"]
# Compound expressions
assert chain("a;b") == ["CompoundExpression", "a", "b"]
assert chain("a;") == ["CompoundExpression", "a", "Null"]
assert chain("a;b;") == ["CompoundExpression", "a", "b", "Null"]
assert chain("a[b;c]") == ["a", ["CompoundExpression", "b", "c"]]
assert chain("a[b,c;d,e]") == ["a", "b", ["CompoundExpression", "c", "d"], "e"]
assert chain("a[b,c;,d]") == ["a", "b", ["CompoundExpression", "c", "Null"], "d"]
# New lines
assert chain("a\nb\n") == ["CompoundExpression", "a", "b"]
assert chain("a\n\nb\n (c \nd) \n") == ["CompoundExpression", "a", "b", ["Times", "c", "d"]]
assert chain("\na; b\nc") == ["CompoundExpression", "a", "b", "c"]
assert chain("a + \nb\n") == ["Plus", "a", "b"]
assert chain("a\nb; c; d\n e; (f \n g); h + \n i") == ["CompoundExpression", "a", "b", "c", "d", "e", ["Times", "f", "g"], ["Plus", "h", "i"]]
assert chain("\n{\na\nb; c; d\n e (f \n g); h + \n i\n\n}\n") == ["List", ["CompoundExpression", ["Times", "a", "b"], "c", ["Times", "d", "e", ["Times", "f", "g"]], ["Plus", "h", "i"]]]
# Patterns
assert chain("y_") == ["Pattern", "y", ["Blank"]]
assert chain("y_.") == ["Optional", ["Pattern", "y", ["Blank"]]]
assert chain("y__") == ["Pattern", "y", ["BlankSequence"]]
assert chain("y___") == ["Pattern", "y", ["BlankNullSequence"]]
assert chain("a[b_.,c_]") == ["a", ["Optional", ["Pattern", "b", ["Blank"]]], ["Pattern", "c", ["Blank"]]]
assert chain("b_. c") == ["Times", ["Optional", ["Pattern", "b", ["Blank"]]], "c"]
# Slots for lambda functions
assert chain("#") == ["Slot", "1"]
assert chain("#3") == ["Slot", "3"]
assert chain("#n") == ["Slot", "n"]
assert chain("##") == ["SlotSequence", "1"]
assert chain("##a") == ["SlotSequence", "a"]
# Lambda functions
assert chain("x&") == ["Function", "x"]
assert chain("#&") == ["Function", ["Slot", "1"]]
assert chain("#+3&") == ["Function", ["Plus", ["Slot", "1"], "3"]]
assert chain("#1 + #2&") == ["Function", ["Plus", ["Slot", "1"], ["Slot", "2"]]]
assert chain("# + #&") == ["Function", ["Plus", ["Slot", "1"], ["Slot", "1"]]]
assert chain("#&[x]") == [["Function", ["Slot", "1"]], "x"]
assert chain("#1 + #2 & [x, y]") == [["Function", ["Plus", ["Slot", "1"], ["Slot", "2"]]], "x", "y"]
assert chain("#1^2#2^3&") == ["Function", ["Times", ["Power", ["Slot", "1"], "2"], ["Power", ["Slot", "2"], "3"]]]
# Strings inside Mathematica expressions:
assert chain('"abc"') == ["_Str", "abc"]
assert chain('"a\\"b"') == ["_Str", 'a"b']
# This expression does not make sense mathematically, it's just testing the parser:
assert chain('x + "abc" ^ 3') == ["Plus", "x", ["Power", ["_Str", "abc"], "3"]]
assert chain('"a (* b *) c"') == ["_Str", "a (* b *) c"]
assert chain('"a" (* b *) ') == ["_Str", "a"]
assert chain('"a [ b] "') == ["_Str", "a [ b] "]
raises(SyntaxError, lambda: chain('"'))
raises(SyntaxError, lambda: chain('"\\"'))
raises(SyntaxError, lambda: chain('"abc'))
raises(SyntaxError, lambda: chain('"abc\\"def'))
# Invalid expressions:
raises(SyntaxError, lambda: chain("(,"))
raises(SyntaxError, lambda: chain("()"))
raises(SyntaxError, lambda: chain("a (* b"))
def test_parser_mathematica_exp_alt():
parser = MathematicaParser()
convert_chain2 = lambda expr: parser._from_fullformlist_to_fullformsympy(parser._from_fullform_to_fullformlist(expr))
convert_chain3 = lambda expr: parser._from_fullformsympy_to_sympy(convert_chain2(expr))
Sin, Times, Plus, Power = symbols("Sin Times Plus Power", cls=Function)
full_form1 = "Sin[Times[x, y]]"
full_form2 = "Plus[Times[x, y], z]"
full_form3 = "Sin[Times[x, Plus[y, z], Power[w, n]]]]"
full_form4 = "Rational[Rational[x, y], z]"
assert parser._from_fullform_to_fullformlist(full_form1) == ["Sin", ["Times", "x", "y"]]
assert parser._from_fullform_to_fullformlist(full_form2) == ["Plus", ["Times", "x", "y"], "z"]
assert parser._from_fullform_to_fullformlist(full_form3) == ["Sin", ["Times", "x", ["Plus", "y", "z"], ["Power", "w", "n"]]]
assert parser._from_fullform_to_fullformlist(full_form4) == ["Rational", ["Rational", "x", "y"], "z"]
assert convert_chain2(full_form1) == Sin(Times(x, y))
assert convert_chain2(full_form2) == Plus(Times(x, y), z)
assert convert_chain2(full_form3) == Sin(Times(x, Plus(y, z), Power(w, n)))
assert convert_chain3(full_form1) == sin(x*y)
assert convert_chain3(full_form2) == x*y + z
assert convert_chain3(full_form3) == sin(x*(y + z)*w**n)

View File

@@ -0,0 +1,50 @@
from sympy.parsing.maxima import parse_maxima
from sympy.core.numbers import (E, Rational, oo)
from sympy.core.symbol import Symbol
from sympy.functions.combinatorial.factorials import factorial
from sympy.functions.elementary.complexes import Abs
from sympy.functions.elementary.exponential import log
from sympy.functions.elementary.trigonometric import (cos, sin)
from sympy.abc import x
n = Symbol('n', integer=True)
def test_parser():
assert Abs(parse_maxima('float(1/3)') - 0.333333333) < 10**(-5)
assert parse_maxima('13^26') == 91733330193268616658399616009
assert parse_maxima('sin(%pi/2) + cos(%pi/3)') == Rational(3, 2)
assert parse_maxima('log(%e)') == 1
def test_injection():
parse_maxima('c: x+1', globals=globals())
# c created by parse_maxima
assert c == x + 1 # noqa:F821
parse_maxima('g: sqrt(81)', globals=globals())
# g created by parse_maxima
assert g == 9 # noqa:F821
def test_maxima_functions():
assert parse_maxima('expand( (x+1)^2)') == x**2 + 2*x + 1
assert parse_maxima('factor( x**2 + 2*x + 1)') == (x + 1)**2
assert parse_maxima('2*cos(x)^2 + sin(x)^2') == 2*cos(x)**2 + sin(x)**2
assert parse_maxima('trigexpand(sin(2*x)+cos(2*x))') == \
-1 + 2*cos(x)**2 + 2*cos(x)*sin(x)
assert parse_maxima('solve(x^2-4,x)') == [-2, 2]
assert parse_maxima('limit((1+1/x)^x,x,inf)') == E
assert parse_maxima('limit(sqrt(-x)/x,x,0,minus)') is -oo
assert parse_maxima('diff(x^x, x)') == x**x*(1 + log(x))
assert parse_maxima('sum(k, k, 1, n)', name_dict={
"n": Symbol('n', integer=True),
"k": Symbol('k', integer=True)
}) == (n**2 + n)/2
assert parse_maxima('product(k, k, 1, n)', name_dict={
"n": Symbol('n', integer=True),
"k": Symbol('k', integer=True)
}) == factorial(n)
assert parse_maxima('ratsimp((x^2-1)/(x+1))') == x - 1
assert Abs( parse_maxima(
'float(sec(%pi/3) + csc(%pi/3))') - 3.154700538379252) < 10**(-5)

View File

@@ -0,0 +1,209 @@
from sympy.parsing.sym_expr import SymPyExpression
from sympy.testing.pytest import raises
from sympy.external import import_module
lfortran = import_module('lfortran')
cin = import_module('clang.cindex', import_kwargs = {'fromlist': ['cindex']})
if lfortran and cin:
from sympy.codegen.ast import (Variable, IntBaseType, FloatBaseType, String,
Declaration, FloatType)
from sympy.core import Integer, Float
from sympy.core.symbol import Symbol
expr1 = SymPyExpression()
src = """\
integer :: a, b, c, d
real :: p, q, r, s
"""
def test_c_parse():
src1 = """\
int a, b = 4;
float c, d = 2.4;
"""
expr1.convert_to_expr(src1, 'c')
ls = expr1.return_expr()
assert ls[0] == Declaration(
Variable(
Symbol('a'),
type=IntBaseType(String('intc'))
)
)
assert ls[1] == Declaration(
Variable(
Symbol('b'),
type=IntBaseType(String('intc')),
value=Integer(4)
)
)
assert ls[2] == Declaration(
Variable(
Symbol('c'),
type=FloatType(
String('float32'),
nbits=Integer(32),
nmant=Integer(23),
nexp=Integer(8)
)
)
)
assert ls[3] == Declaration(
Variable(
Symbol('d'),
type=FloatType(
String('float32'),
nbits=Integer(32),
nmant=Integer(23),
nexp=Integer(8)
),
value=Float('2.3999999999999999', precision=53)
)
)
def test_fortran_parse():
expr = SymPyExpression(src, 'f')
ls = expr.return_expr()
assert ls[0] == Declaration(
Variable(
Symbol('a'),
type=IntBaseType(String('integer')),
value=Integer(0)
)
)
assert ls[1] == Declaration(
Variable(
Symbol('b'),
type=IntBaseType(String('integer')),
value=Integer(0)
)
)
assert ls[2] == Declaration(
Variable(
Symbol('c'),
type=IntBaseType(String('integer')),
value=Integer(0)
)
)
assert ls[3] == Declaration(
Variable(
Symbol('d'),
type=IntBaseType(String('integer')),
value=Integer(0)
)
)
assert ls[4] == Declaration(
Variable(
Symbol('p'),
type=FloatBaseType(String('real')),
value=Float('0.0', precision=53)
)
)
assert ls[5] == Declaration(
Variable(
Symbol('q'),
type=FloatBaseType(String('real')),
value=Float('0.0', precision=53)
)
)
assert ls[6] == Declaration(
Variable(
Symbol('r'),
type=FloatBaseType(String('real')),
value=Float('0.0', precision=53)
)
)
assert ls[7] == Declaration(
Variable(
Symbol('s'),
type=FloatBaseType(String('real')),
value=Float('0.0', precision=53)
)
)
def test_convert_py():
src1 = (
src +
"""\
a = b + c
s = p * q / r
"""
)
expr1.convert_to_expr(src1, 'f')
exp_py = expr1.convert_to_python()
assert exp_py == [
'a = 0',
'b = 0',
'c = 0',
'd = 0',
'p = 0.0',
'q = 0.0',
'r = 0.0',
's = 0.0',
'a = b + c',
's = p*q/r'
]
def test_convert_fort():
src1 = (
src +
"""\
a = b + c
s = p * q / r
"""
)
expr1.convert_to_expr(src1, 'f')
exp_fort = expr1.convert_to_fortran()
assert exp_fort == [
' integer*4 a',
' integer*4 b',
' integer*4 c',
' integer*4 d',
' real*8 p',
' real*8 q',
' real*8 r',
' real*8 s',
' a = b + c',
' s = p*q/r'
]
def test_convert_c():
src1 = (
src +
"""\
a = b + c
s = p * q / r
"""
)
expr1.convert_to_expr(src1, 'f')
exp_c = expr1.convert_to_c()
assert exp_c == [
'int a = 0',
'int b = 0',
'int c = 0',
'int d = 0',
'double p = 0.0',
'double q = 0.0',
'double r = 0.0',
'double s = 0.0',
'a = b + c;',
's = p*q/r;'
]
def test_exceptions():
src = 'int a;'
raises(ValueError, lambda: SymPyExpression(src))
raises(ValueError, lambda: SymPyExpression(mode = 'c'))
raises(NotImplementedError, lambda: SymPyExpression(src, mode = 'd'))
elif not lfortran and not cin:
def test_raise():
raises(ImportError, lambda: SymPyExpression('int a;', 'c'))
raises(ImportError, lambda: SymPyExpression('integer :: a', 'f'))

View File

@@ -0,0 +1,371 @@
# -*- coding: utf-8 -*-
import builtins
import types
from sympy.assumptions import Q
from sympy.core import Symbol, Function, Float, Rational, Integer, I, Mul, Pow, Eq, Lt, Le, Gt, Ge, Ne
from sympy.functions import exp, factorial, factorial2, sin, Min, Max
from sympy.logic import And
from sympy.series import Limit
from sympy.testing.pytest import raises
from sympy.parsing.sympy_parser import (
parse_expr, standard_transformations, rationalize, TokenError,
split_symbols, implicit_multiplication, convert_equals_signs,
convert_xor, function_exponentiation, lambda_notation, auto_symbol,
repeated_decimals, implicit_multiplication_application,
auto_number, factorial_notation, implicit_application,
_transformation, T
)
def test_sympy_parser():
x = Symbol('x')
inputs = {
'2*x': 2 * x,
'3.00': Float(3),
'22/7': Rational(22, 7),
'2+3j': 2 + 3*I,
'exp(x)': exp(x),
'x!': factorial(x),
'x!!': factorial2(x),
'(x + 1)! - 1': factorial(x + 1) - 1,
'3.[3]': Rational(10, 3),
'.0[3]': Rational(1, 30),
'3.2[3]': Rational(97, 30),
'1.3[12]': Rational(433, 330),
'1 + 3.[3]': Rational(13, 3),
'1 + .0[3]': Rational(31, 30),
'1 + 3.2[3]': Rational(127, 30),
'.[0011]': Rational(1, 909),
'0.1[00102] + 1': Rational(366697, 333330),
'1.[0191]': Rational(10190, 9999),
'10!': 3628800,
'-(2)': -Integer(2),
'[-1, -2, 3]': [Integer(-1), Integer(-2), Integer(3)],
'Symbol("x").free_symbols': x.free_symbols,
"S('S(3).n(n=3)')": Float(3, 3),
'factorint(12, visual=True)': Mul(
Pow(2, 2, evaluate=False),
Pow(3, 1, evaluate=False),
evaluate=False),
'Limit(sin(x), x, 0, dir="-")': Limit(sin(x), x, 0, dir='-'),
'Q.even(x)': Q.even(x),
}
for text, result in inputs.items():
assert parse_expr(text) == result
raises(TypeError, lambda:
parse_expr('x', standard_transformations))
raises(TypeError, lambda:
parse_expr('x', transformations=lambda x,y: 1))
raises(TypeError, lambda:
parse_expr('x', transformations=(lambda x,y: 1,)))
raises(TypeError, lambda: parse_expr('x', transformations=((),)))
raises(TypeError, lambda: parse_expr('x', {}, [], []))
raises(TypeError, lambda: parse_expr('x', [], [], {}))
raises(TypeError, lambda: parse_expr('x', [], [], {}))
def test_rationalize():
inputs = {
'0.123': Rational(123, 1000)
}
transformations = standard_transformations + (rationalize,)
for text, result in inputs.items():
assert parse_expr(text, transformations=transformations) == result
def test_factorial_fail():
inputs = ['x!!!', 'x!!!!', '(!)']
for text in inputs:
try:
parse_expr(text)
assert False
except TokenError:
assert True
def test_repeated_fail():
inputs = ['1[1]', '.1e1[1]', '0x1[1]', '1.1j[1]', '1.1[1 + 1]',
'0.1[[1]]', '0x1.1[1]']
# All are valid Python, so only raise TypeError for invalid indexing
for text in inputs:
raises(TypeError, lambda: parse_expr(text))
inputs = ['0.1[', '0.1[1', '0.1[]']
for text in inputs:
raises((TokenError, SyntaxError), lambda: parse_expr(text))
def test_repeated_dot_only():
assert parse_expr('.[1]') == Rational(1, 9)
assert parse_expr('1 + .[1]') == Rational(10, 9)
def test_local_dict():
local_dict = {
'my_function': lambda x: x + 2
}
inputs = {
'my_function(2)': Integer(4)
}
for text, result in inputs.items():
assert parse_expr(text, local_dict=local_dict) == result
def test_local_dict_split_implmult():
t = standard_transformations + (split_symbols, implicit_multiplication,)
w = Symbol('w', real=True)
y = Symbol('y')
assert parse_expr('yx', local_dict={'x':w}, transformations=t) == y*w
def test_local_dict_symbol_to_fcn():
x = Symbol('x')
d = {'foo': Function('bar')}
assert parse_expr('foo(x)', local_dict=d) == d['foo'](x)
d = {'foo': Symbol('baz')}
raises(TypeError, lambda: parse_expr('foo(x)', local_dict=d))
def test_global_dict():
global_dict = {
'Symbol': Symbol
}
inputs = {
'Q & S': And(Symbol('Q'), Symbol('S'))
}
for text, result in inputs.items():
assert parse_expr(text, global_dict=global_dict) == result
def test_no_globals():
# Replicate creating the default global_dict:
default_globals = {}
exec('from sympy import *', default_globals)
builtins_dict = vars(builtins)
for name, obj in builtins_dict.items():
if isinstance(obj, types.BuiltinFunctionType):
default_globals[name] = obj
default_globals['max'] = Max
default_globals['min'] = Min
# Need to include Symbol or parse_expr will not work:
default_globals.pop('Symbol')
global_dict = {'Symbol':Symbol}
for name in default_globals:
obj = parse_expr(name, global_dict=global_dict)
assert obj == Symbol(name)
def test_issue_2515():
raises(TokenError, lambda: parse_expr('(()'))
raises(TokenError, lambda: parse_expr('"""'))
def test_issue_7663():
x = Symbol('x')
e = '2*(x+1)'
assert parse_expr(e, evaluate=False) == parse_expr(e, evaluate=False)
assert parse_expr(e, evaluate=False).equals(2*(x+1))
def test_recursive_evaluate_false_10560():
inputs = {
'4*-3' : '4*-3',
'-4*3' : '(-4)*3',
"-2*x*y": '(-2)*x*y',
"x*-4*x": "x*(-4)*x"
}
for text, result in inputs.items():
assert parse_expr(text, evaluate=False) == parse_expr(result, evaluate=False)
def test_function_evaluate_false():
inputs = [
'Abs(0)', 'im(0)', 're(0)', 'sign(0)', 'arg(0)', 'conjugate(0)',
'acos(0)', 'acot(0)', 'acsc(0)', 'asec(0)', 'asin(0)', 'atan(0)',
'acosh(0)', 'acoth(0)', 'acsch(0)', 'asech(0)', 'asinh(0)', 'atanh(0)',
'cos(0)', 'cot(0)', 'csc(0)', 'sec(0)', 'sin(0)', 'tan(0)',
'cosh(0)', 'coth(0)', 'csch(0)', 'sech(0)', 'sinh(0)', 'tanh(0)',
'exp(0)', 'log(0)', 'sqrt(0)',
]
for case in inputs:
expr = parse_expr(case, evaluate=False)
assert case == str(expr) != str(expr.doit())
assert str(parse_expr('ln(0)', evaluate=False)) == 'log(0)'
assert str(parse_expr('cbrt(0)', evaluate=False)) == '0**(1/3)'
def test_issue_10773():
inputs = {
'-10/5': '(-10)/5',
'-10/-5' : '(-10)/(-5)',
}
for text, result in inputs.items():
assert parse_expr(text, evaluate=False) == parse_expr(result, evaluate=False)
def test_split_symbols():
transformations = standard_transformations + \
(split_symbols, implicit_multiplication,)
x = Symbol('x')
y = Symbol('y')
xy = Symbol('xy')
assert parse_expr("xy") == xy
assert parse_expr("xy", transformations=transformations) == x*y
def test_split_symbols_function():
transformations = standard_transformations + \
(split_symbols, implicit_multiplication,)
x = Symbol('x')
y = Symbol('y')
a = Symbol('a')
f = Function('f')
assert parse_expr("ay(x+1)", transformations=transformations) == a*y*(x+1)
assert parse_expr("af(x+1)", transformations=transformations,
local_dict={'f':f}) == a*f(x+1)
def test_functional_exponent():
t = standard_transformations + (convert_xor, function_exponentiation)
x = Symbol('x')
y = Symbol('y')
a = Symbol('a')
yfcn = Function('y')
assert parse_expr("sin^2(x)", transformations=t) == (sin(x))**2
assert parse_expr("sin^y(x)", transformations=t) == (sin(x))**y
assert parse_expr("exp^y(x)", transformations=t) == (exp(x))**y
assert parse_expr("E^y(x)", transformations=t) == exp(yfcn(x))
assert parse_expr("a^y(x)", transformations=t) == a**(yfcn(x))
def test_match_parentheses_implicit_multiplication():
transformations = standard_transformations + \
(implicit_multiplication,)
raises(TokenError, lambda: parse_expr('(1,2),(3,4]',transformations=transformations))
def test_convert_equals_signs():
transformations = standard_transformations + \
(convert_equals_signs, )
x = Symbol('x')
y = Symbol('y')
assert parse_expr("1*2=x", transformations=transformations) == Eq(2, x)
assert parse_expr("y = x", transformations=transformations) == Eq(y, x)
assert parse_expr("(2*y = x) = False",
transformations=transformations) == Eq(Eq(2*y, x), False)
def test_parse_function_issue_3539():
x = Symbol('x')
f = Function('f')
assert parse_expr('f(x)') == f(x)
def test_issue_24288():
assert parse_expr("1 < 2", evaluate=False) == Lt(1, 2, evaluate=False)
assert parse_expr("1 <= 2", evaluate=False) == Le(1, 2, evaluate=False)
assert parse_expr("1 > 2", evaluate=False) == Gt(1, 2, evaluate=False)
assert parse_expr("1 >= 2", evaluate=False) == Ge(1, 2, evaluate=False)
assert parse_expr("1 != 2", evaluate=False) == Ne(1, 2, evaluate=False)
assert parse_expr("1 == 2", evaluate=False) == Eq(1, 2, evaluate=False)
assert parse_expr("1 < 2 < 3", evaluate=False) == And(Lt(1, 2, evaluate=False), Lt(2, 3, evaluate=False), evaluate=False)
assert parse_expr("1 <= 2 <= 3", evaluate=False) == And(Le(1, 2, evaluate=False), Le(2, 3, evaluate=False), evaluate=False)
assert parse_expr("1 < 2 <= 3 < 4", evaluate=False) == \
And(Lt(1, 2, evaluate=False), Le(2, 3, evaluate=False), Lt(3, 4, evaluate=False), evaluate=False)
# Valid Python relational operators that SymPy does not decide how to handle them yet
raises(ValueError, lambda: parse_expr("1 in 2", evaluate=False))
raises(ValueError, lambda: parse_expr("1 is 2", evaluate=False))
raises(ValueError, lambda: parse_expr("1 not in 2", evaluate=False))
raises(ValueError, lambda: parse_expr("1 is not 2", evaluate=False))
def test_split_symbols_numeric():
transformations = (
standard_transformations +
(implicit_multiplication_application,))
n = Symbol('n')
expr1 = parse_expr('2**n * 3**n')
expr2 = parse_expr('2**n3**n', transformations=transformations)
assert expr1 == expr2 == 2**n*3**n
expr1 = parse_expr('n12n34', transformations=transformations)
assert expr1 == n*12*n*34
def test_unicode_names():
assert parse_expr('α') == Symbol('α')
def test_python3_features():
assert parse_expr("123_456") == 123456
assert parse_expr("1.2[3_4]") == parse_expr("1.2[34]") == Rational(611, 495)
assert parse_expr("1.2[012_012]") == parse_expr("1.2[012012]") == Rational(400, 333)
assert parse_expr('.[3_4]') == parse_expr('.[34]') == Rational(34, 99)
assert parse_expr('.1[3_4]') == parse_expr('.1[34]') == Rational(133, 990)
assert parse_expr('123_123.123_123[3_4]') == parse_expr('123123.123123[34]') == Rational(12189189189211, 99000000)
def test_issue_19501():
x = Symbol('x')
eq = parse_expr('E**x(1+x)', local_dict={'x': x}, transformations=(
standard_transformations +
(implicit_multiplication_application,)))
assert eq.free_symbols == {x}
def test_parsing_definitions():
from sympy.abc import x
assert len(_transformation) == 12 # if this changes, extend below
assert _transformation[0] == lambda_notation
assert _transformation[1] == auto_symbol
assert _transformation[2] == repeated_decimals
assert _transformation[3] == auto_number
assert _transformation[4] == factorial_notation
assert _transformation[5] == implicit_multiplication_application
assert _transformation[6] == convert_xor
assert _transformation[7] == implicit_application
assert _transformation[8] == implicit_multiplication
assert _transformation[9] == convert_equals_signs
assert _transformation[10] == function_exponentiation
assert _transformation[11] == rationalize
assert T[:5] == T[0,1,2,3,4] == standard_transformations
t = _transformation
assert T[-1, 0] == (t[len(t) - 1], t[0])
assert T[:5, 8] == standard_transformations + (t[8],)
assert parse_expr('0.3x^2', transformations='all') == 3*x**2/10
assert parse_expr('sin 3x', transformations='implicit') == sin(3*x)
def test_builtins():
cases = [
('abs(x)', 'Abs(x)'),
('max(x, y)', 'Max(x, y)'),
('min(x, y)', 'Min(x, y)'),
('pow(x, y)', 'Pow(x, y)'),
]
for built_in_func_call, sympy_func_call in cases:
assert parse_expr(built_in_func_call) == parse_expr(sympy_func_call)
assert str(parse_expr('pow(38, -1, 97)')) == '23'
def test_issue_22822():
raises(ValueError, lambda: parse_expr('x', {'': 1}))
data = {'some_parameter': None}
assert parse_expr('some_parameter is None', data) is True