levycas 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- levycas-1.0.0/LICENSE.txt +21 -0
- levycas-1.0.0/MANIFEST.in +1 -0
- levycas-1.0.0/PKG-INFO +230 -0
- levycas-1.0.0/README.md +207 -0
- levycas-1.0.0/pyproject.toml +54 -0
- levycas-1.0.0/setup.cfg +4 -0
- levycas-1.0.0/src/levycas/__init__.py +13 -0
- levycas-1.0.0/src/levycas/__main__.py +160 -0
- levycas-1.0.0/src/levycas/cli/__init__.py +1 -0
- levycas-1.0.0/src/levycas/cli/__main__.py +59 -0
- levycas-1.0.0/src/levycas/cli/screens/__init__.py +5 -0
- levycas-1.0.0/src/levycas/cli/screens/graph.py +418 -0
- levycas-1.0.0/src/levycas/cli/screens/script.py +108 -0
- levycas-1.0.0/src/levycas/cli/screens/styles/graphing.tcss +118 -0
- levycas-1.0.0/src/levycas/cli/screens/styles/scripting.tcss +92 -0
- levycas-1.0.0/src/levycas/cli/screens/styles/welcome.tcss +24 -0
- levycas-1.0.0/src/levycas/cli/screens/welcome.py +16 -0
- levycas-1.0.0/src/levycas/expressions/__init__.py +33 -0
- levycas-1.0.0/src/levycas/expressions/exp.py +59 -0
- levycas-1.0.0/src/levycas/expressions/expression.py +872 -0
- levycas-1.0.0/src/levycas/expressions/trig.py +71 -0
- levycas-1.0.0/src/levycas/operations/__init__.py +101 -0
- levycas-1.0.0/src/levycas/operations/algebraic_ops.py +287 -0
- levycas-1.0.0/src/levycas/operations/calculus_ops.py +583 -0
- levycas-1.0.0/src/levycas/operations/equation_ops.py +41 -0
- levycas-1.0.0/src/levycas/operations/exponential_ops.py +156 -0
- levycas-1.0.0/src/levycas/operations/expression_ops.py +214 -0
- levycas-1.0.0/src/levycas/operations/factorization_ops.py +546 -0
- levycas-1.0.0/src/levycas/operations/numerical_ops.py +212 -0
- levycas-1.0.0/src/levycas/operations/polynomial_ops.py +896 -0
- levycas-1.0.0/src/levycas/operations/simplification_ops.py +461 -0
- levycas-1.0.0/src/levycas/operations/trig_ops.py +351 -0
- levycas-1.0.0/src/levycas/parser/__init__.py +8 -0
- levycas-1.0.0/src/levycas/parser/lexer.py +50 -0
- levycas-1.0.0/src/levycas/parser/parser.py +245 -0
- levycas-1.0.0/src/levycas/scripting/__init__.py +13 -0
- levycas-1.0.0/src/levycas/scripting/errors.py +7 -0
- levycas-1.0.0/src/levycas/scripting/execution.py +253 -0
- levycas-1.0.0/src/levycas/scripting/scripting.py +438 -0
- levycas-1.0.0/src/levycas.egg-info/PKG-INFO +230 -0
- levycas-1.0.0/src/levycas.egg-info/SOURCES.txt +58 -0
- levycas-1.0.0/src/levycas.egg-info/dependency_links.txt +1 -0
- levycas-1.0.0/src/levycas.egg-info/entry_points.txt +2 -0
- levycas-1.0.0/src/levycas.egg-info/requires.txt +12 -0
- levycas-1.0.0/src/levycas.egg-info/top_level.txt +1 -0
- levycas-1.0.0/tests/test_algebraic.py +65 -0
- levycas-1.0.0/tests/test_calculus.py +444 -0
- levycas-1.0.0/tests/test_exp.py +91 -0
- levycas-1.0.0/tests/test_expansion.py +76 -0
- levycas-1.0.0/tests/test_expression_ops.py +68 -0
- levycas-1.0.0/tests/test_factorization.py +122 -0
- levycas-1.0.0/tests/test_numerical.py +112 -0
- levycas-1.0.0/tests/test_ordering.py +81 -0
- levycas-1.0.0/tests/test_parse.py +92 -0
- levycas-1.0.0/tests/test_polynomials.py +406 -0
- levycas-1.0.0/tests/test_scripting.py +190 -0
- levycas-1.0.0/tests/test_simplification.py +167 -0
- levycas-1.0.0/tests/test_trig.py +196 -0
- levycas-1.0.0/tests/test_tui_graphing.py +28 -0
- levycas-1.0.0/tests/test_tui_scripting.py +35 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Alex Levy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include src/levycas/cli/screens/styles/*
|
levycas-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: levycas
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A computer algebra system written in python, with an interactive text-based user interface complete with a built-in scripting language and graphing directly in the terminal.
|
|
5
|
+
Author-email: Alex Levy <ajlevy246@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: homepage, https://github.com/ajlevy246/LevyCAS
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE.txt
|
|
12
|
+
Provides-Extra: tui
|
|
13
|
+
Requires-Dist: textual; extra == "tui"
|
|
14
|
+
Requires-Dist: textual-plot; extra == "tui"
|
|
15
|
+
Provides-Extra: dev
|
|
16
|
+
Requires-Dist: textual; extra == "dev"
|
|
17
|
+
Requires-Dist: textual-plot; extra == "dev"
|
|
18
|
+
Requires-Dist: textual-dev; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-textual-snapshot; extra == "dev"
|
|
20
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
22
|
+
Dynamic: license-file
|
|
23
|
+
|
|
24
|
+
# Welcome to LevyCAS!
|
|
25
|
+
|
|
26
|
+
[](https://github.com/ajlevy246/LevyCAS/actions/workflows/python-package.yml)
|
|
27
|
+
[](https://codecov.io/gh/ajlevy246/LevyCAS)
|
|
28
|
+
|
|
29
|
+
**LevyCAS** is a lightweight symbolic computer algebra system (CAS) written in Python. It focuses on parsing natural mathematical expressions into symbolic objects and performing symbolic manipulation, calculus, simplification, and number-theoretic computations.
|
|
30
|
+
|
|
31
|
+
<img src="https://github.com/ajlevy246/LevyCAS/blob/main/assets/graph.png?raw=true" width=1000 alt="A screenshot of the LevyCAS graphing textual user interface. Expressions are entered on the left hand side, and an interactive graph is plotted on the right.">
|
|
32
|
+
|
|
33
|
+
<br/>
|
|
34
|
+
|
|
35
|
+
LevyCAS remains an educational project, exploring symbolic computation, expression trees, Pratt parsing, grammars, and terminal UIs. It's designed to combine the best of existing computational tools that I regularly use: Desmos, Sympy, and Matlab.
|
|
36
|
+
|
|
37
|
+
## Features
|
|
38
|
+
- Parse natural mathematical expressions.
|
|
39
|
+
- Write scripts with a custom scripting language.
|
|
40
|
+
- Symbolic integrals & derivatives.
|
|
41
|
+
- Expression simplification & normalization.
|
|
42
|
+
- Integer factorization & number-theoretic operations.
|
|
43
|
+
- Multivariate polynomial operations: factoring, partial fractions, rationalization.
|
|
44
|
+
- Interactive Textual-based graphing & scripting interface.
|
|
45
|
+
- Python library core with no required dependencies.
|
|
46
|
+
|
|
47
|
+
<br/>
|
|
48
|
+
|
|
49
|
+
# Installation & Quick Start:
|
|
50
|
+
|
|
51
|
+
While the base package has no dependencies, it is designed for Python3.10+
|
|
52
|
+
|
|
53
|
+
## Core package & CLI
|
|
54
|
+
LevyCAS is uploaded as a Python package on the TestPyPi index [here](https://test.pypi.org/project/levycas/). Install with pip:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
$ python3 -m pip install levycas --index-url https://test.pypi.org/simple/
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Then, get started by launching python and running:
|
|
61
|
+
```python
|
|
62
|
+
from levycas import symbols, parse, derivative, integrate
|
|
63
|
+
|
|
64
|
+
x, y = symbols("x y")
|
|
65
|
+
expr = parse("sin(x)cos(y)")
|
|
66
|
+
print(expr)
|
|
67
|
+
|
|
68
|
+
print(derivative(expr, x))
|
|
69
|
+
print(integrate(expr, x))
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or through the command-line interface:
|
|
73
|
+
```bash
|
|
74
|
+
$ levycas integrate "xsin(x^2)"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Terminal-Based TUI
|
|
78
|
+
To use the textual user interface, install with the `tui` extra. This extra depends on the [`Textual`](https://textual.textualize.io/) library, as well as the `textual-plot` package.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
$ python3 -m pip install levycas[tui] --extra-index-url https://test.pypi.org/simple/
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Launch the TUI directly in your terminal:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
$ levycas
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Or jump straight to the graphing interface:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
$ levycas graph "sin(x)" "x^2" "lnx" "exp(x)"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
<br/>
|
|
97
|
+
|
|
98
|
+
# Textual User Interface
|
|
99
|
+
|
|
100
|
+
By default, installing the `tui` extra adds the `levycas` script to PATH. Launch the powerful interface to write scripts or plot expressions:
|
|
101
|
+
|
|
102
|
+
<img src="https://github.com/ajlevy246/LevyCAS/blob/main/assets/script.png?raw=true" width=1000 alt="A screenshot of the LevyCAS scripting textual user interface. A script is entered on the left hand side, while output is printed on the right.">
|
|
103
|
+
|
|
104
|
+
<br/>
|
|
105
|
+
|
|
106
|
+
# Project Structure
|
|
107
|
+
|
|
108
|
+
The base package consists of a set of core expression objects, along with a set of routines acting on them.
|
|
109
|
+
|
|
110
|
+
In addition, LevyCAS includes a native Pratt parser capable of interpreting natural mathematical syntax such as `sin(x)cosx`, `1/2x`, and `3ln(x^2)` into symbolic expression trees.
|
|
111
|
+
|
|
112
|
+
### [`src/levycas/`](./src/levycas/__init__.py)
|
|
113
|
+
- [`expressions/`](./src/levycas/expressions/__init__.py): Defines the symbolic expression tree classes used through LevyCAS. These classes make extensive use of Python's operator overloading and inheritance to provide a simple interface.
|
|
114
|
+
|
|
115
|
+
- [`operations/`](./src/levycas/operations/__init__.py): The routines consist of both the fundamental simplification operations (`simplify`, `sym_eval`), as well as useful symbolic computations like `derivative` and `integrate`.
|
|
116
|
+
|
|
117
|
+
- [`parser/`](./src/levycas/parser/__init__.py): All of the Pratt parsing logic is contained here. Lexing converts and input string into tokens, and the parsing logic converts it to native objects.
|
|
118
|
+
|
|
119
|
+
- [`cli/`](./src/levycas/cli/__init__.py): All of the logic for the textual user interface is contained here. This module does not expose any external functions, but the scripts here may be interesting to those building simple Textual apps themselves.
|
|
120
|
+
|
|
121
|
+
- [`scripting/`](./src/levycas/scripting/__init__.py): The custom scripting language logic is written here. See the grammar and examples in [`GRAMMAR.md`](./src/levycas/scripting/GRAMMAR.md). Use the TUI to run scripts or load them from disk.
|
|
122
|
+
|
|
123
|
+
<br/>
|
|
124
|
+
|
|
125
|
+
# Examples:
|
|
126
|
+
|
|
127
|
+
### Parse
|
|
128
|
+
```python
|
|
129
|
+
>>> from levycas import parse
|
|
130
|
+
|
|
131
|
+
# implicit multiplication & function args
|
|
132
|
+
>>> parse("1/2sin(x)cosx")
|
|
133
|
+
(1/2)Sin(x)Cos(x)
|
|
134
|
+
|
|
135
|
+
# symbol generation
|
|
136
|
+
>>> parse("ax^2 + bx + c")
|
|
137
|
+
ax² + bx + c
|
|
138
|
+
|
|
139
|
+
# automatic simplification/normalization
|
|
140
|
+
>>> parse("2(x+3) - x - 6y/y")
|
|
141
|
+
x
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Polynomial Factoring
|
|
145
|
+
```python
|
|
146
|
+
>>> from levycas import Variable, factor
|
|
147
|
+
>>> x = Variable("x")
|
|
148
|
+
|
|
149
|
+
# repeated irreducible factors
|
|
150
|
+
>>> factor(x**8 + 2*x**6 - 6*x**5 - 12*x**3 + 9*x**2 + 18, x)
|
|
151
|
+
[1, x² + 2, (x³ - 3)^2]
|
|
152
|
+
|
|
153
|
+
# generalized polynomial variables
|
|
154
|
+
>>> factor(4*Ln(x)**2 + 10*Ln(x) + 6, Ln(x))
|
|
155
|
+
[2, Ln(x) + 1, 2Ln(x) + 3]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Symbolic Integration
|
|
159
|
+
```python
|
|
160
|
+
>>> from levycas.expressions import Variable, Sin, Cos, Ln,
|
|
161
|
+
>>> from levycas.operations import integrate, collect_terms
|
|
162
|
+
>>> x = Variable("x")
|
|
163
|
+
|
|
164
|
+
# u-substitution
|
|
165
|
+
>>> integrate(x*Sin(x**2), x)
|
|
166
|
+
-(1/2)(Cos(x)^2)
|
|
167
|
+
|
|
168
|
+
# partial fractions via Hermite reduction
|
|
169
|
+
>>> integrate(1 / ((x**2+1)*(x-2)), x)
|
|
170
|
+
-(1/10)Ln(x² + 1) + (1/5)Ln(x - 2) - (2/5)Arctan(x)
|
|
171
|
+
|
|
172
|
+
# integration by parts
|
|
173
|
+
>>> integrate(x**3 * Exp(x), x)
|
|
174
|
+
>>> collect_terms(_, Exp(x))
|
|
175
|
+
(x³ - 3x² + 6x - 6)Exp(x)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Symbolic Derivatives
|
|
179
|
+
```python
|
|
180
|
+
>>> from levycas.expression import Variable, Cos, Ln
|
|
181
|
+
>>> from levycas.operations import derivative, trig_simplify
|
|
182
|
+
>>> x = Variable('x')
|
|
183
|
+
|
|
184
|
+
>>> derivative(x**4 + x**3 + x**2 + x, x)
|
|
185
|
+
1 + 2x + 3x² + 4x³
|
|
186
|
+
|
|
187
|
+
>>> derivative((Cos(x)+Sin(x))**2, x)
|
|
188
|
+
>>> trig_simplify(_)
|
|
189
|
+
2Cos(2x)
|
|
190
|
+
|
|
191
|
+
>>> derivative(x*Ln(x) - x, x)
|
|
192
|
+
Ln(x)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Algebraic Operations
|
|
196
|
+
```python
|
|
197
|
+
>>> from levycas.expressions import Variable
|
|
198
|
+
>>> from levycas.operations import rationalize, partial_fractions as partial
|
|
199
|
+
>>> x = Variable('x')
|
|
200
|
+
|
|
201
|
+
# partial fractions
|
|
202
|
+
>>> partial(8*x+7, [x+2, x-1], x)
|
|
203
|
+
3/(x+2) + 5/(x-1)
|
|
204
|
+
|
|
205
|
+
# rationalization
|
|
206
|
+
>>> rationalize(3/(x+2) + 5/(x-1))
|
|
207
|
+
(8x+7)/((x+2)*(x-1))
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Integer Operations
|
|
211
|
+
```python
|
|
212
|
+
>>> from levycas import is_prime, factor_integer, gcd
|
|
213
|
+
|
|
214
|
+
>>> gcd(2**4 * 3**5 * 5**3, 7**4 * 11**4 * 13**5)
|
|
215
|
+
1
|
|
216
|
+
|
|
217
|
+
>>> factor_integer(2**4 * 3**5 * 5**3 * 7**9)
|
|
218
|
+
{2: 4, 3: 5, 5: 3, 7: 9}
|
|
219
|
+
|
|
220
|
+
# integer radical
|
|
221
|
+
>>> def rad(n: Integer) -> Integer:
|
|
222
|
+
>>> rad, factors = 1, factor_integer(n).keys()
|
|
223
|
+
>>> for factor in factors:
|
|
224
|
+
>>> rad *= factor
|
|
225
|
+
>>> return rad
|
|
226
|
+
|
|
227
|
+
>>> rad(18)
|
|
228
|
+
6
|
|
229
|
+
```
|
|
230
|
+
|
levycas-1.0.0/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# Welcome to LevyCAS!
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ajlevy246/LevyCAS/actions/workflows/python-package.yml)
|
|
4
|
+
[](https://codecov.io/gh/ajlevy246/LevyCAS)
|
|
5
|
+
|
|
6
|
+
**LevyCAS** is a lightweight symbolic computer algebra system (CAS) written in Python. It focuses on parsing natural mathematical expressions into symbolic objects and performing symbolic manipulation, calculus, simplification, and number-theoretic computations.
|
|
7
|
+
|
|
8
|
+
<img src="https://github.com/ajlevy246/LevyCAS/blob/main/assets/graph.png?raw=true" width=1000 alt="A screenshot of the LevyCAS graphing textual user interface. Expressions are entered on the left hand side, and an interactive graph is plotted on the right.">
|
|
9
|
+
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+
LevyCAS remains an educational project, exploring symbolic computation, expression trees, Pratt parsing, grammars, and terminal UIs. It's designed to combine the best of existing computational tools that I regularly use: Desmos, Sympy, and Matlab.
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
- Parse natural mathematical expressions.
|
|
16
|
+
- Write scripts with a custom scripting language.
|
|
17
|
+
- Symbolic integrals & derivatives.
|
|
18
|
+
- Expression simplification & normalization.
|
|
19
|
+
- Integer factorization & number-theoretic operations.
|
|
20
|
+
- Multivariate polynomial operations: factoring, partial fractions, rationalization.
|
|
21
|
+
- Interactive Textual-based graphing & scripting interface.
|
|
22
|
+
- Python library core with no required dependencies.
|
|
23
|
+
|
|
24
|
+
<br/>
|
|
25
|
+
|
|
26
|
+
# Installation & Quick Start:
|
|
27
|
+
|
|
28
|
+
While the base package has no dependencies, it is designed for Python3.10+
|
|
29
|
+
|
|
30
|
+
## Core package & CLI
|
|
31
|
+
LevyCAS is uploaded as a Python package on the TestPyPi index [here](https://test.pypi.org/project/levycas/). Install with pip:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
$ python3 -m pip install levycas --index-url https://test.pypi.org/simple/
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then, get started by launching python and running:
|
|
38
|
+
```python
|
|
39
|
+
from levycas import symbols, parse, derivative, integrate
|
|
40
|
+
|
|
41
|
+
x, y = symbols("x y")
|
|
42
|
+
expr = parse("sin(x)cos(y)")
|
|
43
|
+
print(expr)
|
|
44
|
+
|
|
45
|
+
print(derivative(expr, x))
|
|
46
|
+
print(integrate(expr, x))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Or through the command-line interface:
|
|
50
|
+
```bash
|
|
51
|
+
$ levycas integrate "xsin(x^2)"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Terminal-Based TUI
|
|
55
|
+
To use the textual user interface, install with the `tui` extra. This extra depends on the [`Textual`](https://textual.textualize.io/) library, as well as the `textual-plot` package.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
$ python3 -m pip install levycas[tui] --extra-index-url https://test.pypi.org/simple/
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Launch the TUI directly in your terminal:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
$ levycas
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Or jump straight to the graphing interface:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
$ levycas graph "sin(x)" "x^2" "lnx" "exp(x)"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
<br/>
|
|
74
|
+
|
|
75
|
+
# Textual User Interface
|
|
76
|
+
|
|
77
|
+
By default, installing the `tui` extra adds the `levycas` script to PATH. Launch the powerful interface to write scripts or plot expressions:
|
|
78
|
+
|
|
79
|
+
<img src="https://github.com/ajlevy246/LevyCAS/blob/main/assets/script.png?raw=true" width=1000 alt="A screenshot of the LevyCAS scripting textual user interface. A script is entered on the left hand side, while output is printed on the right.">
|
|
80
|
+
|
|
81
|
+
<br/>
|
|
82
|
+
|
|
83
|
+
# Project Structure
|
|
84
|
+
|
|
85
|
+
The base package consists of a set of core expression objects, along with a set of routines acting on them.
|
|
86
|
+
|
|
87
|
+
In addition, LevyCAS includes a native Pratt parser capable of interpreting natural mathematical syntax such as `sin(x)cosx`, `1/2x`, and `3ln(x^2)` into symbolic expression trees.
|
|
88
|
+
|
|
89
|
+
### [`src/levycas/`](./src/levycas/__init__.py)
|
|
90
|
+
- [`expressions/`](./src/levycas/expressions/__init__.py): Defines the symbolic expression tree classes used through LevyCAS. These classes make extensive use of Python's operator overloading and inheritance to provide a simple interface.
|
|
91
|
+
|
|
92
|
+
- [`operations/`](./src/levycas/operations/__init__.py): The routines consist of both the fundamental simplification operations (`simplify`, `sym_eval`), as well as useful symbolic computations like `derivative` and `integrate`.
|
|
93
|
+
|
|
94
|
+
- [`parser/`](./src/levycas/parser/__init__.py): All of the Pratt parsing logic is contained here. Lexing converts and input string into tokens, and the parsing logic converts it to native objects.
|
|
95
|
+
|
|
96
|
+
- [`cli/`](./src/levycas/cli/__init__.py): All of the logic for the textual user interface is contained here. This module does not expose any external functions, but the scripts here may be interesting to those building simple Textual apps themselves.
|
|
97
|
+
|
|
98
|
+
- [`scripting/`](./src/levycas/scripting/__init__.py): The custom scripting language logic is written here. See the grammar and examples in [`GRAMMAR.md`](./src/levycas/scripting/GRAMMAR.md). Use the TUI to run scripts or load them from disk.
|
|
99
|
+
|
|
100
|
+
<br/>
|
|
101
|
+
|
|
102
|
+
# Examples:
|
|
103
|
+
|
|
104
|
+
### Parse
|
|
105
|
+
```python
|
|
106
|
+
>>> from levycas import parse
|
|
107
|
+
|
|
108
|
+
# implicit multiplication & function args
|
|
109
|
+
>>> parse("1/2sin(x)cosx")
|
|
110
|
+
(1/2)Sin(x)Cos(x)
|
|
111
|
+
|
|
112
|
+
# symbol generation
|
|
113
|
+
>>> parse("ax^2 + bx + c")
|
|
114
|
+
ax² + bx + c
|
|
115
|
+
|
|
116
|
+
# automatic simplification/normalization
|
|
117
|
+
>>> parse("2(x+3) - x - 6y/y")
|
|
118
|
+
x
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Polynomial Factoring
|
|
122
|
+
```python
|
|
123
|
+
>>> from levycas import Variable, factor
|
|
124
|
+
>>> x = Variable("x")
|
|
125
|
+
|
|
126
|
+
# repeated irreducible factors
|
|
127
|
+
>>> factor(x**8 + 2*x**6 - 6*x**5 - 12*x**3 + 9*x**2 + 18, x)
|
|
128
|
+
[1, x² + 2, (x³ - 3)^2]
|
|
129
|
+
|
|
130
|
+
# generalized polynomial variables
|
|
131
|
+
>>> factor(4*Ln(x)**2 + 10*Ln(x) + 6, Ln(x))
|
|
132
|
+
[2, Ln(x) + 1, 2Ln(x) + 3]
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Symbolic Integration
|
|
136
|
+
```python
|
|
137
|
+
>>> from levycas.expressions import Variable, Sin, Cos, Ln,
|
|
138
|
+
>>> from levycas.operations import integrate, collect_terms
|
|
139
|
+
>>> x = Variable("x")
|
|
140
|
+
|
|
141
|
+
# u-substitution
|
|
142
|
+
>>> integrate(x*Sin(x**2), x)
|
|
143
|
+
-(1/2)(Cos(x)^2)
|
|
144
|
+
|
|
145
|
+
# partial fractions via Hermite reduction
|
|
146
|
+
>>> integrate(1 / ((x**2+1)*(x-2)), x)
|
|
147
|
+
-(1/10)Ln(x² + 1) + (1/5)Ln(x - 2) - (2/5)Arctan(x)
|
|
148
|
+
|
|
149
|
+
# integration by parts
|
|
150
|
+
>>> integrate(x**3 * Exp(x), x)
|
|
151
|
+
>>> collect_terms(_, Exp(x))
|
|
152
|
+
(x³ - 3x² + 6x - 6)Exp(x)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Symbolic Derivatives
|
|
156
|
+
```python
|
|
157
|
+
>>> from levycas.expression import Variable, Cos, Ln
|
|
158
|
+
>>> from levycas.operations import derivative, trig_simplify
|
|
159
|
+
>>> x = Variable('x')
|
|
160
|
+
|
|
161
|
+
>>> derivative(x**4 + x**3 + x**2 + x, x)
|
|
162
|
+
1 + 2x + 3x² + 4x³
|
|
163
|
+
|
|
164
|
+
>>> derivative((Cos(x)+Sin(x))**2, x)
|
|
165
|
+
>>> trig_simplify(_)
|
|
166
|
+
2Cos(2x)
|
|
167
|
+
|
|
168
|
+
>>> derivative(x*Ln(x) - x, x)
|
|
169
|
+
Ln(x)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Algebraic Operations
|
|
173
|
+
```python
|
|
174
|
+
>>> from levycas.expressions import Variable
|
|
175
|
+
>>> from levycas.operations import rationalize, partial_fractions as partial
|
|
176
|
+
>>> x = Variable('x')
|
|
177
|
+
|
|
178
|
+
# partial fractions
|
|
179
|
+
>>> partial(8*x+7, [x+2, x-1], x)
|
|
180
|
+
3/(x+2) + 5/(x-1)
|
|
181
|
+
|
|
182
|
+
# rationalization
|
|
183
|
+
>>> rationalize(3/(x+2) + 5/(x-1))
|
|
184
|
+
(8x+7)/((x+2)*(x-1))
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Integer Operations
|
|
188
|
+
```python
|
|
189
|
+
>>> from levycas import is_prime, factor_integer, gcd
|
|
190
|
+
|
|
191
|
+
>>> gcd(2**4 * 3**5 * 5**3, 7**4 * 11**4 * 13**5)
|
|
192
|
+
1
|
|
193
|
+
|
|
194
|
+
>>> factor_integer(2**4 * 3**5 * 5**3 * 7**9)
|
|
195
|
+
{2: 4, 3: 5, 5: 3, 7: 9}
|
|
196
|
+
|
|
197
|
+
# integer radical
|
|
198
|
+
>>> def rad(n: Integer) -> Integer:
|
|
199
|
+
>>> rad, factors = 1, factor_integer(n).keys()
|
|
200
|
+
>>> for factor in factors:
|
|
201
|
+
>>> rad *= factor
|
|
202
|
+
>>> return rad
|
|
203
|
+
|
|
204
|
+
>>> rad(18)
|
|
205
|
+
6
|
|
206
|
+
```
|
|
207
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "levycas"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
authors = [
|
|
5
|
+
{name = "Alex Levy", email = "ajlevy246@gmail.com"},
|
|
6
|
+
]
|
|
7
|
+
description = "A computer algebra system written in python, with an interactive text-based user interface complete with a built-in scripting language and graphing directly in the terminal."
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
classifiers = [
|
|
10
|
+
"Programming Language :: Python :: 3",
|
|
11
|
+
"Operating System :: OS Independent",
|
|
12
|
+
]
|
|
13
|
+
license = "MIT"
|
|
14
|
+
license-files = ["LICENSE.txt"]
|
|
15
|
+
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
tui = [
|
|
18
|
+
"textual",
|
|
19
|
+
"textual-plot",
|
|
20
|
+
]
|
|
21
|
+
dev = [
|
|
22
|
+
"textual",
|
|
23
|
+
"textual-plot",
|
|
24
|
+
"textual-dev",
|
|
25
|
+
"pytest-textual-snapshot",
|
|
26
|
+
"pytest-cov",
|
|
27
|
+
"pytest-asyncio",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
homepage = "https://github.com/ajlevy246/LevyCAS"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
levycas = "levycas.__main__:main"
|
|
35
|
+
|
|
36
|
+
[build-system]
|
|
37
|
+
requires = ["setuptools"]
|
|
38
|
+
build-backend = "setuptools.build_meta"
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
asyncio_mode = "auto"
|
|
45
|
+
|
|
46
|
+
[tool.coverage.run]
|
|
47
|
+
source = ["levycas"]
|
|
48
|
+
|
|
49
|
+
[tool.coverage.report]
|
|
50
|
+
omit = [
|
|
51
|
+
"src/levycas/cli/*",
|
|
52
|
+
]
|
|
53
|
+
show_missing = true
|
|
54
|
+
skip_covered = true
|
levycas-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""LevyCAS is an implementation of a Computer Algebra System written
|
|
2
|
+
in pure Python. It requires only the built-in regex (re) and math modules.
|
|
3
|
+
|
|
4
|
+
Install with the `tui` extra and run `levycas` for the interactive Textual user interface.
|
|
5
|
+
Or, check out https://alexlevy.me for an online LevyCAS demo, hosted with Gradio on Huggingface.
|
|
6
|
+
|
|
7
|
+
- Alex Levy (2025)
|
|
8
|
+
"""
|
|
9
|
+
from .expressions import *
|
|
10
|
+
from .operations import *
|
|
11
|
+
from .parser import *
|
|
12
|
+
from .scripting import *
|
|
13
|
+
from .cli import *
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from argparse import ArgumentParser
|
|
2
|
+
|
|
3
|
+
from levycas.parser import parse
|
|
4
|
+
from levycas.operations import derivative, integrate, get_symbols, factor, simplify
|
|
5
|
+
from levycas.expressions import Product
|
|
6
|
+
|
|
7
|
+
def print_version():
|
|
8
|
+
try:
|
|
9
|
+
from importlib.metadata import version
|
|
10
|
+
print(f"LevyCAS: version=={version('levycas')}")
|
|
11
|
+
except ImportError:
|
|
12
|
+
print("importlib not found in standard library... LevyCAS may not support this Python version.")
|
|
13
|
+
|
|
14
|
+
def launch_terminal_session():
|
|
15
|
+
raise NotImplementedError("Terminal session not yet implemented... ")
|
|
16
|
+
|
|
17
|
+
def integrate_action(args) -> None:
|
|
18
|
+
expr, wrt = parse(args.expr), parse(args.wrt)
|
|
19
|
+
print(f"{integrate(expr, wrt)}")
|
|
20
|
+
|
|
21
|
+
def diff_action(args) -> None:
|
|
22
|
+
expr, wrt = parse(args.expr), parse(args.wrt)
|
|
23
|
+
print(f"{derivative(expr, wrt)}")
|
|
24
|
+
|
|
25
|
+
def factor_action(args) -> None:
|
|
26
|
+
poly, var = parse(args.poly), parse(args.var)
|
|
27
|
+
factors = factor(poly, var)
|
|
28
|
+
print(simplify(Product(*factors)))
|
|
29
|
+
|
|
30
|
+
def graph_action(args) -> None:
|
|
31
|
+
try:
|
|
32
|
+
import textual, textual_plot
|
|
33
|
+
except ImportError as e:
|
|
34
|
+
raise ImportError(
|
|
35
|
+
f"The LevyCAS TUI requires the '{e.name}' package. "
|
|
36
|
+
f"Please see installation instructions in the README."
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
assert len(args.exprs) <= 4, f"Support for more than four graphs at once is not yet available."
|
|
40
|
+
from levycas.cli.__main__ import LevyCasApp
|
|
41
|
+
app = LevyCasApp(graphing=True, exprs=args.exprs)
|
|
42
|
+
app.run()
|
|
43
|
+
|
|
44
|
+
def launch_tui_action() -> None:
|
|
45
|
+
try:
|
|
46
|
+
import textual, textual_plot
|
|
47
|
+
except ImportError as e:
|
|
48
|
+
raise ImportError(
|
|
49
|
+
f"The LevyCAS TUI requires the '{e.name}' package. "
|
|
50
|
+
f"Please see installation instructions in the README."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
from levycas.cli.__main__ import main as launch_tui
|
|
54
|
+
launch_tui()
|
|
55
|
+
|
|
56
|
+
def build_parser() -> ArgumentParser:
|
|
57
|
+
# Help opens with `levycas -h or --help`
|
|
58
|
+
parser = ArgumentParser(
|
|
59
|
+
prog="levycas",
|
|
60
|
+
description=(
|
|
61
|
+
"A set of symbolic computation tools, with a powerful text-based UI built with Textual. "
|
|
62
|
+
"Do calculus, simplification, graphing, scripting, and more. "
|
|
63
|
+
),
|
|
64
|
+
epilog=(
|
|
65
|
+
"See github.com/ajlevy246/LevyCAS/ for examples and source. "
|
|
66
|
+
"Run with no arguments to launch the TUI."
|
|
67
|
+
)
|
|
68
|
+
)
|
|
69
|
+
parser.add_argument("-v", "--version", help="print the levycas version and exit.", action='store_true')
|
|
70
|
+
parser.add_argument("-i", "--interactive", help="launch an interactive python session with common commands already run.", action='store_true')
|
|
71
|
+
|
|
72
|
+
subparsers = parser.add_subparsers(
|
|
73
|
+
dest="command",
|
|
74
|
+
description="The specific command to run. Run with no command to launch the TUI.")
|
|
75
|
+
|
|
76
|
+
# Differentiate with `levycas diff ...`
|
|
77
|
+
diff_parser = subparsers.add_parser(
|
|
78
|
+
"diff",
|
|
79
|
+
help="compute the derivative of an expression.",
|
|
80
|
+
)
|
|
81
|
+
diff_parser.add_argument(
|
|
82
|
+
"expr", metavar="EXPR",
|
|
83
|
+
help="the expression to compute the derivative of, e.g. 'x^2ln(x)'",
|
|
84
|
+
)
|
|
85
|
+
diff_parser.add_argument(
|
|
86
|
+
"wrt", metavar="VAR",
|
|
87
|
+
nargs="?", default="x",
|
|
88
|
+
help="the variable of differentiation; 'x' by default.",
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Integrate with `levycas integrate ...`
|
|
92
|
+
int_parser = subparsers.add_parser(
|
|
93
|
+
"integrate",
|
|
94
|
+
help="compute the integral of an expression.",
|
|
95
|
+
)
|
|
96
|
+
int_parser.add_argument(
|
|
97
|
+
"expr", metavar="EXPR",
|
|
98
|
+
help="the expression to compute the integral of, e.g. 'xsin(x^2)",
|
|
99
|
+
)
|
|
100
|
+
int_parser.add_argument(
|
|
101
|
+
"wrt", metavar="VAR",
|
|
102
|
+
nargs="?", default="x",
|
|
103
|
+
help="the variable of integration; 'x' by default.",
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
# Factor with `levycas factor ...`
|
|
107
|
+
fact_parser = subparsers.add_parser(
|
|
108
|
+
"factor",
|
|
109
|
+
help="factor a univariate polynomial with rational coefficients.",
|
|
110
|
+
)
|
|
111
|
+
fact_parser.add_argument(
|
|
112
|
+
"poly", metavar="POLY",
|
|
113
|
+
help="the polynomial to factor, e.g. 'x^2 + 3x + 2'",
|
|
114
|
+
)
|
|
115
|
+
fact_parser.add_argument(
|
|
116
|
+
"var", metavar="VAR",
|
|
117
|
+
nargs="?", default="x",
|
|
118
|
+
help="the polynomial variable; 'x' by default.",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Graph with `levycas graph ...`
|
|
122
|
+
graph_parser = subparsers.add_parser(
|
|
123
|
+
"graph",
|
|
124
|
+
help="graph up to four expressions.",
|
|
125
|
+
)
|
|
126
|
+
graph_parser.add_argument(
|
|
127
|
+
"exprs", metavar="EXPRS",
|
|
128
|
+
nargs="*",
|
|
129
|
+
help="enter up to four expressions to plot, e.g. 'sin(x)'",
|
|
130
|
+
)
|
|
131
|
+
return parser
|
|
132
|
+
|
|
133
|
+
def main():
|
|
134
|
+
parser = build_parser()
|
|
135
|
+
args = parser.parse_args()
|
|
136
|
+
|
|
137
|
+
# -v or -i launch actions and exit
|
|
138
|
+
if args.version:
|
|
139
|
+
print_version()
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
if args.interactive:
|
|
143
|
+
launch_terminal_session()
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
# otherwise, expect a command + optional arguments
|
|
147
|
+
command = args.command
|
|
148
|
+
if command is None:
|
|
149
|
+
launch_tui_action()
|
|
150
|
+
elif command == "integrate":
|
|
151
|
+
integrate_action(args)
|
|
152
|
+
elif command == "diff":
|
|
153
|
+
diff_action(args)
|
|
154
|
+
elif command == "factor":
|
|
155
|
+
factor_action(args)
|
|
156
|
+
elif command == "graph":
|
|
157
|
+
graph_action(args)
|
|
158
|
+
|
|
159
|
+
if __name__ == "__main__":
|
|
160
|
+
main()
|