odecast 0.1.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.
- odecast-0.1.0/.gitignore +59 -0
- odecast-0.1.0/PKG-INFO +288 -0
- odecast-0.1.0/README.md +262 -0
- odecast-0.1.0/pyproject.toml +36 -0
- odecast-0.1.0/src/odecast/__init__.py +10 -0
- odecast-0.1.0/src/odecast/analyze.py +132 -0
- odecast-0.1.0/src/odecast/api.py +253 -0
- odecast-0.1.0/src/odecast/backends/__init__.py +5 -0
- odecast-0.1.0/src/odecast/backends/scipy_bvp.py +46 -0
- odecast-0.1.0/src/odecast/backends/scipy_ivp.py +134 -0
- odecast-0.1.0/src/odecast/backends/sympy_backend.py +144 -0
- odecast-0.1.0/src/odecast/compile.py +145 -0
- odecast-0.1.0/src/odecast/equation.py +188 -0
- odecast-0.1.0/src/odecast/errors.py +45 -0
- odecast-0.1.0/src/odecast/reduce.py +359 -0
- odecast-0.1.0/src/odecast/solution.py +201 -0
- odecast-0.1.0/src/odecast/symbols.py +482 -0
- odecast-0.1.0/src/odecast/validate.py +191 -0
- odecast-0.1.0/tests/conftest.py +22 -0
- odecast-0.1.0/tests/test_analyze.py +117 -0
- odecast-0.1.0/tests/test_api_bvp.py +15 -0
- odecast-0.1.0/tests/test_api_smoke.py +68 -0
- odecast-0.1.0/tests/test_api_usage_ivp.py +29 -0
- odecast-0.1.0/tests/test_api_usage_sympy.py +13 -0
- odecast-0.1.0/tests/test_api_validation.py +17 -0
- odecast-0.1.0/tests/test_api_vector.py +81 -0
- odecast-0.1.0/tests/test_compilation.py +219 -0
- odecast-0.1.0/tests/test_reduce.py +252 -0
- odecast-0.1.0/tests/test_sympy_backend.py +223 -0
- odecast-0.1.0/tests/test_validation.py +295 -0
- odecast-0.1.0/tests/test_validation_comprehensive.py +263 -0
odecast-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.so
|
|
5
|
+
*.egg
|
|
6
|
+
*.egg-info/
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.pyo
|
|
10
|
+
*.pyd
|
|
11
|
+
*.db
|
|
12
|
+
*.sqlite3
|
|
13
|
+
|
|
14
|
+
# Hatchling/Packaging
|
|
15
|
+
*.wheel
|
|
16
|
+
*.egg-info/
|
|
17
|
+
*.eggs/
|
|
18
|
+
*.manifest
|
|
19
|
+
*.spec
|
|
20
|
+
|
|
21
|
+
# Jupyter Notebooks
|
|
22
|
+
.ipynb_checkpoints/
|
|
23
|
+
|
|
24
|
+
# VS Code
|
|
25
|
+
.vscode/
|
|
26
|
+
.history/
|
|
27
|
+
|
|
28
|
+
# Test/coverage
|
|
29
|
+
.coverage
|
|
30
|
+
htmlcov/
|
|
31
|
+
.tox/
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
.cache/
|
|
34
|
+
.nox/
|
|
35
|
+
.mypy_cache/
|
|
36
|
+
.dmypy.json
|
|
37
|
+
.pyre/
|
|
38
|
+
*.cover
|
|
39
|
+
*.log
|
|
40
|
+
|
|
41
|
+
# Environment
|
|
42
|
+
.env
|
|
43
|
+
.venv
|
|
44
|
+
env/
|
|
45
|
+
venv/
|
|
46
|
+
ENV/
|
|
47
|
+
env.bak/
|
|
48
|
+
venv.bak/
|
|
49
|
+
|
|
50
|
+
# OS
|
|
51
|
+
.DS_Store
|
|
52
|
+
Thumbs.db
|
|
53
|
+
|
|
54
|
+
# Misc
|
|
55
|
+
*.bak
|
|
56
|
+
*.swp
|
|
57
|
+
*.tmp
|
|
58
|
+
*.orig
|
|
59
|
+
TODOs.md
|
odecast-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: odecast
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python library for solving higher-order ordinary differential equations (ODEs) without manual reduction.
|
|
5
|
+
Project-URL: Homepage, https://github.com/maroba/odecast
|
|
6
|
+
Project-URL: Repository, https://github.com/maroba/odecast
|
|
7
|
+
Author-email: Matthias Baer <matthias.r.baer@googlemail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
19
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
20
|
+
Provides-Extra: math
|
|
21
|
+
Requires-Dist: matplotlib; extra == 'math'
|
|
22
|
+
Requires-Dist: numpy; extra == 'math'
|
|
23
|
+
Requires-Dist: scipy; extra == 'math'
|
|
24
|
+
Requires-Dist: sympy; extra == 'math'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# <img src="https://github.com/maroba/odecast/raw/main/docs/odecast-logo.png" alt="Odecast Logo" width="120" align="left" />
|
|
29
|
+
|
|
30
|
+
# Odecast
|
|
31
|
+
|
|
32
|
+
[](https://pypi.org/project/odecast/)
|
|
33
|
+
[](https://github.com/maroba/odecast/blob/main/LICENSE)
|
|
34
|
+
[](https://github.com/maroba/odecast/actions)
|
|
35
|
+
[](https://github.com/psf/black)
|
|
36
|
+
|
|
37
|
+
A Python library for solving higher-order ordinary differential equations (ODEs) without manual reduction. Write equations in natural mathematical form (e.g. `y.d(2) + 0.3*y.d() + y = 0`) and let odecast handle order inference, validation, conversion to first-order systems, and solving via SymPy (symbolic) or SciPy (numeric).
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- **Intuitive syntax**: Write ODEs as they appear in textbooks using `y.d(2)` for derivatives
|
|
42
|
+
- **Vector/Matrix variables**: Support for systems with `var("u", shape=2)` and component access `u[0]`, `u[1]`
|
|
43
|
+
- **Multiple backends**: Symbolic solutions via SymPy, numeric solutions via SciPy
|
|
44
|
+
- **Automatic order inference**: No need to manually convert to first-order systems
|
|
45
|
+
- **Automatic equation expansion**: Vector equations automatically expand to component equations
|
|
46
|
+
- **Comprehensive validation**: Clear error messages for missing or inconsistent conditions
|
|
47
|
+
- **Flexible interface**: Support for IVP (initial value problems) and BVP (boundary value problems)
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install -e ".[math,dev]"
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from odecast import t, var, Eq, solve
|
|
59
|
+
|
|
60
|
+
# Define a variable
|
|
61
|
+
y = var("y")
|
|
62
|
+
|
|
63
|
+
# Create an equation: y'' + 0.3*y' + y = 0 (damped harmonic oscillator)
|
|
64
|
+
eq = Eq(y.d(2) + 0.3*y.d() + y, 0)
|
|
65
|
+
|
|
66
|
+
# Solve with initial conditions y(0) = 1, y'(0) = 0
|
|
67
|
+
sol = solve(eq, ivp={y: 1.0, y.d(): 0.0}, tspan=(0.0, 10.0), backend="scipy")
|
|
68
|
+
|
|
69
|
+
# Access solution data
|
|
70
|
+
y_values = sol[y] # Position over time
|
|
71
|
+
velocity = sol[y.d()] # Velocity over time
|
|
72
|
+
times = sol.t # Time points
|
|
73
|
+
|
|
74
|
+
# Evaluate at specific times
|
|
75
|
+
position_at_5s = sol.eval(y, 5.0)
|
|
76
|
+
|
|
77
|
+
# Vector variables for systems
|
|
78
|
+
u = var("u", shape=2) # 2D vector variable
|
|
79
|
+
eq_vector = Eq(u.d(2) + u, 0) # Vector harmonic oscillator
|
|
80
|
+
sol_vector = solve(eq_vector,
|
|
81
|
+
ivp={u: [1.0, 0.0], u.d(): [0.0, 1.0]},
|
|
82
|
+
tspan=(0, 5), backend="scipy")
|
|
83
|
+
positions = sol_vector[u] # 2×N array of [x, y] positions
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Vector/Matrix Variables
|
|
87
|
+
|
|
88
|
+
Odecast supports vector and matrix variables for systems of equations, making it easy to work with multi-dimensional problems.
|
|
89
|
+
|
|
90
|
+
### Creating Vector Variables
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from odecast import t, var, Eq, solve
|
|
94
|
+
|
|
95
|
+
# Create a 2D vector variable
|
|
96
|
+
u = var("u", shape=2) # Creates u with components u[0], u[1]
|
|
97
|
+
|
|
98
|
+
# Create a 3D vector variable
|
|
99
|
+
v = var("v", shape=3) # Creates v with components v[0], v[1], v[2]
|
|
100
|
+
|
|
101
|
+
# Matrix variables (coming soon)
|
|
102
|
+
# A = var("A", shape=(2, 2)) # 2x2 matrix
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Component Access
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
u = var("u", shape=2)
|
|
109
|
+
|
|
110
|
+
# Access individual components
|
|
111
|
+
u0 = u[0] # First component
|
|
112
|
+
u1 = u[1] # Second component
|
|
113
|
+
|
|
114
|
+
# Components behave like regular variables
|
|
115
|
+
eq1 = Eq(u[0].d(2) + u[0], 0) # u₀'' + u₀ = 0
|
|
116
|
+
eq2 = Eq(u[1].d() + u[0], 0) # u₁' + u₀ = 0
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Vector Operations
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
u = var("u", shape=2)
|
|
123
|
+
|
|
124
|
+
# Vector derivatives
|
|
125
|
+
u_dot = u.d() # First derivative: [u[0]', u[1]']
|
|
126
|
+
u_ddot = u.d(2) # Second derivative: [u[0]'', u[1]'']
|
|
127
|
+
|
|
128
|
+
# Vector equations automatically expand to component equations
|
|
129
|
+
eq = Eq(u.d(2) + u, 0) # Becomes: u[0]'' + u[0] = 0, u[1]'' + u[1] = 0
|
|
130
|
+
|
|
131
|
+
# Vector arithmetic
|
|
132
|
+
position = var("r", shape=2)
|
|
133
|
+
velocity = var("v", shape=2)
|
|
134
|
+
eq1 = Eq(position.d() - velocity, 0) # r' = v
|
|
135
|
+
eq2 = Eq(velocity.d() + 0.1*velocity, 0) # v' + 0.1v = 0 (damping)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Vector Initial Conditions
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
u = var("u", shape=2)
|
|
142
|
+
eq = Eq(u.d(2) + u, 0) # 2D harmonic oscillator
|
|
143
|
+
|
|
144
|
+
# Vector-style initial conditions
|
|
145
|
+
ivp = {
|
|
146
|
+
u: [1.0, 0.5], # u(0) = [1.0, 0.5]
|
|
147
|
+
u.d(): [0.0, -0.2] # u'(0) = [0.0, -0.2]
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
sol = solve(eq, ivp=ivp, tspan=(0, 10), backend="scipy")
|
|
151
|
+
|
|
152
|
+
# Access vector solution (returns 2×N array)
|
|
153
|
+
u_trajectory = sol[u] # Shape: (2, num_timepoints)
|
|
154
|
+
|
|
155
|
+
# Access individual components (returns 1D arrays)
|
|
156
|
+
u0_trajectory = sol[u[0]]
|
|
157
|
+
u1_trajectory = sol[u[1]]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Mixed Vector/Scalar Systems
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
# Coupled system with both scalar and vector variables
|
|
164
|
+
x = var("x") # Scalar variable
|
|
165
|
+
u = var("u", shape=2) # Vector variable
|
|
166
|
+
|
|
167
|
+
eqs = [
|
|
168
|
+
Eq(x.d(2) + x - u[0], 0), # x'' + x - u₀ = 0
|
|
169
|
+
Eq(u[0].d() + u[1], x), # u₀' + u₁ = x
|
|
170
|
+
Eq(u[1].d() + u[0], 0), # u₁' + u₀ = 0
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
ivp = {
|
|
174
|
+
x: 1.0, # x(0) = 1
|
|
175
|
+
x.d(): 0.0, # x'(0) = 0
|
|
176
|
+
u: [0.5, 0.0], # u(0) = [0.5, 0.0]
|
|
177
|
+
u.d(): [0.0, 0.0] # u'(0) = [0.0, 0.0] (automatically filtered for first-order components)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
sol = solve(eqs, ivp=ivp, tspan=(0, 5), backend="scipy")
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Examples
|
|
184
|
+
|
|
185
|
+
The `examples/` directory contains comprehensive examples:
|
|
186
|
+
|
|
187
|
+
- `01_ivp_damped_oscillator.py` - Numeric IVP solving with visualization
|
|
188
|
+
- `02_symbolic_simple.py` - Symbolic solutions using SymPy backend
|
|
189
|
+
- `03_mixed_orders.py` - Coupled systems with mixed derivative orders
|
|
190
|
+
- `04_vector_harmonic_oscillator.py` - 2D harmonic oscillator using vector variables
|
|
191
|
+
- `05_vector_mixed_system.py` - Mixed vector/scalar systems
|
|
192
|
+
- `06_vector_simple.py` - Simple vector variable introduction
|
|
193
|
+
|
|
194
|
+
Run any example:
|
|
195
|
+
```bash
|
|
196
|
+
python examples/01_ivp_damped_oscillator.py
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Backends
|
|
200
|
+
|
|
201
|
+
### SciPy (Numeric)
|
|
202
|
+
```python
|
|
203
|
+
# Solve numerically over a time span
|
|
204
|
+
sol = solve(eq, ivp=conditions, tspan=(0, 10), backend="scipy")
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### SymPy (Symbolic)
|
|
208
|
+
```python
|
|
209
|
+
# Get exact symbolic solution
|
|
210
|
+
sol = solve(eq, backend="sympy")
|
|
211
|
+
expr = sol.as_expr(y) # Returns SymPy expression
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Auto Backend Selection
|
|
215
|
+
```python
|
|
216
|
+
# Try symbolic first, fall back to numeric
|
|
217
|
+
sol = solve(eq, ivp=conditions, tspan=(0, 10), backend="auto")
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## API Reference
|
|
221
|
+
|
|
222
|
+
### Variables and Equations
|
|
223
|
+
- `var(name, order=None)` - Create a scalar dependent variable
|
|
224
|
+
- `var(name, shape=n)` - Create a vector variable with n components
|
|
225
|
+
- `var(name, shape=(m,n))` - Create a matrix variable (coming soon)
|
|
226
|
+
- `y.d(n)` - n-th derivative of variable y
|
|
227
|
+
- `u[i]` - Access i-th component of vector variable u
|
|
228
|
+
- `u.d(n)` - n-th derivative of vector variable (returns vector derivative)
|
|
229
|
+
- `Eq(lhs, rhs)` - Create an equation
|
|
230
|
+
- `t` - Independent variable (time)
|
|
231
|
+
|
|
232
|
+
### Solving
|
|
233
|
+
- `solve(equations, ivp=None, bvp=None, tspan=None, backend="auto")`
|
|
234
|
+
|
|
235
|
+
### Solution Objects
|
|
236
|
+
- `sol[y]` - Access solution values for variable y
|
|
237
|
+
- `sol[y.d()]` - Access derivative values
|
|
238
|
+
- `sol[u]` - Access vector solution (returns 2D array for vector variables)
|
|
239
|
+
- `sol[u[i]]` - Access i-th component solution (returns 1D array)
|
|
240
|
+
- `sol.eval(target, time_points)` - Evaluate at specific times
|
|
241
|
+
- `sol.as_first_order()` - Inspect first-order system representation
|
|
242
|
+
|
|
243
|
+
## Contributing
|
|
244
|
+
|
|
245
|
+
This project follows a test-driven development approach. All functionality is defined by comprehensive tests.
|
|
246
|
+
|
|
247
|
+
### Development Setup
|
|
248
|
+
```bash
|
|
249
|
+
git clone https://github.com/maroba/odecast.git
|
|
250
|
+
cd odecast
|
|
251
|
+
pip install -e ".[dev]"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Running Tests
|
|
255
|
+
```bash
|
|
256
|
+
pytest # Run all tests
|
|
257
|
+
pytest -v # Verbose output
|
|
258
|
+
pytest tests/test_api* # Run specific test files
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Code Quality
|
|
262
|
+
```bash
|
|
263
|
+
ruff check . # Linting
|
|
264
|
+
black . # Formatting
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Contributing Guidelines
|
|
268
|
+
1. All new features must have comprehensive tests
|
|
269
|
+
2. Follow the existing API patterns and naming conventions
|
|
270
|
+
3. Add examples for significant new functionality
|
|
271
|
+
4. Update documentation for user-facing changes
|
|
272
|
+
5. Ensure all tests pass before submitting PRs
|
|
273
|
+
|
|
274
|
+
## Status
|
|
275
|
+
|
|
276
|
+
Current implementation status:
|
|
277
|
+
- ✅ Core DSL (variables, equations, derivatives)
|
|
278
|
+
- ✅ Vector/Matrix variables with automatic equation expansion
|
|
279
|
+
- ✅ SciPy numeric backend (IVP)
|
|
280
|
+
- ✅ SymPy symbolic backend (decoupled systems)
|
|
281
|
+
- ✅ Automatic order inference and validation
|
|
282
|
+
- ✅ Comprehensive error handling
|
|
283
|
+
- ✅ Mixed-order coupled systems
|
|
284
|
+
- 🚧 BVP support (boundary value problems) - coming soon
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
MIT License - see LICENSE file for details.
|
odecast-0.1.0/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
|
|
2
|
+
# <img src="https://github.com/maroba/odecast/raw/main/docs/odecast-logo.png" alt="Odecast Logo" width="120" align="left" />
|
|
3
|
+
|
|
4
|
+
# Odecast
|
|
5
|
+
|
|
6
|
+
[](https://pypi.org/project/odecast/)
|
|
7
|
+
[](https://github.com/maroba/odecast/blob/main/LICENSE)
|
|
8
|
+
[](https://github.com/maroba/odecast/actions)
|
|
9
|
+
[](https://github.com/psf/black)
|
|
10
|
+
|
|
11
|
+
A Python library for solving higher-order ordinary differential equations (ODEs) without manual reduction. Write equations in natural mathematical form (e.g. `y.d(2) + 0.3*y.d() + y = 0`) and let odecast handle order inference, validation, conversion to first-order systems, and solving via SymPy (symbolic) or SciPy (numeric).
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Intuitive syntax**: Write ODEs as they appear in textbooks using `y.d(2)` for derivatives
|
|
16
|
+
- **Vector/Matrix variables**: Support for systems with `var("u", shape=2)` and component access `u[0]`, `u[1]`
|
|
17
|
+
- **Multiple backends**: Symbolic solutions via SymPy, numeric solutions via SciPy
|
|
18
|
+
- **Automatic order inference**: No need to manually convert to first-order systems
|
|
19
|
+
- **Automatic equation expansion**: Vector equations automatically expand to component equations
|
|
20
|
+
- **Comprehensive validation**: Clear error messages for missing or inconsistent conditions
|
|
21
|
+
- **Flexible interface**: Support for IVP (initial value problems) and BVP (boundary value problems)
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install -e ".[math,dev]"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from odecast import t, var, Eq, solve
|
|
33
|
+
|
|
34
|
+
# Define a variable
|
|
35
|
+
y = var("y")
|
|
36
|
+
|
|
37
|
+
# Create an equation: y'' + 0.3*y' + y = 0 (damped harmonic oscillator)
|
|
38
|
+
eq = Eq(y.d(2) + 0.3*y.d() + y, 0)
|
|
39
|
+
|
|
40
|
+
# Solve with initial conditions y(0) = 1, y'(0) = 0
|
|
41
|
+
sol = solve(eq, ivp={y: 1.0, y.d(): 0.0}, tspan=(0.0, 10.0), backend="scipy")
|
|
42
|
+
|
|
43
|
+
# Access solution data
|
|
44
|
+
y_values = sol[y] # Position over time
|
|
45
|
+
velocity = sol[y.d()] # Velocity over time
|
|
46
|
+
times = sol.t # Time points
|
|
47
|
+
|
|
48
|
+
# Evaluate at specific times
|
|
49
|
+
position_at_5s = sol.eval(y, 5.0)
|
|
50
|
+
|
|
51
|
+
# Vector variables for systems
|
|
52
|
+
u = var("u", shape=2) # 2D vector variable
|
|
53
|
+
eq_vector = Eq(u.d(2) + u, 0) # Vector harmonic oscillator
|
|
54
|
+
sol_vector = solve(eq_vector,
|
|
55
|
+
ivp={u: [1.0, 0.0], u.d(): [0.0, 1.0]},
|
|
56
|
+
tspan=(0, 5), backend="scipy")
|
|
57
|
+
positions = sol_vector[u] # 2×N array of [x, y] positions
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Vector/Matrix Variables
|
|
61
|
+
|
|
62
|
+
Odecast supports vector and matrix variables for systems of equations, making it easy to work with multi-dimensional problems.
|
|
63
|
+
|
|
64
|
+
### Creating Vector Variables
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from odecast import t, var, Eq, solve
|
|
68
|
+
|
|
69
|
+
# Create a 2D vector variable
|
|
70
|
+
u = var("u", shape=2) # Creates u with components u[0], u[1]
|
|
71
|
+
|
|
72
|
+
# Create a 3D vector variable
|
|
73
|
+
v = var("v", shape=3) # Creates v with components v[0], v[1], v[2]
|
|
74
|
+
|
|
75
|
+
# Matrix variables (coming soon)
|
|
76
|
+
# A = var("A", shape=(2, 2)) # 2x2 matrix
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Component Access
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
u = var("u", shape=2)
|
|
83
|
+
|
|
84
|
+
# Access individual components
|
|
85
|
+
u0 = u[0] # First component
|
|
86
|
+
u1 = u[1] # Second component
|
|
87
|
+
|
|
88
|
+
# Components behave like regular variables
|
|
89
|
+
eq1 = Eq(u[0].d(2) + u[0], 0) # u₀'' + u₀ = 0
|
|
90
|
+
eq2 = Eq(u[1].d() + u[0], 0) # u₁' + u₀ = 0
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Vector Operations
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
u = var("u", shape=2)
|
|
97
|
+
|
|
98
|
+
# Vector derivatives
|
|
99
|
+
u_dot = u.d() # First derivative: [u[0]', u[1]']
|
|
100
|
+
u_ddot = u.d(2) # Second derivative: [u[0]'', u[1]'']
|
|
101
|
+
|
|
102
|
+
# Vector equations automatically expand to component equations
|
|
103
|
+
eq = Eq(u.d(2) + u, 0) # Becomes: u[0]'' + u[0] = 0, u[1]'' + u[1] = 0
|
|
104
|
+
|
|
105
|
+
# Vector arithmetic
|
|
106
|
+
position = var("r", shape=2)
|
|
107
|
+
velocity = var("v", shape=2)
|
|
108
|
+
eq1 = Eq(position.d() - velocity, 0) # r' = v
|
|
109
|
+
eq2 = Eq(velocity.d() + 0.1*velocity, 0) # v' + 0.1v = 0 (damping)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Vector Initial Conditions
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
u = var("u", shape=2)
|
|
116
|
+
eq = Eq(u.d(2) + u, 0) # 2D harmonic oscillator
|
|
117
|
+
|
|
118
|
+
# Vector-style initial conditions
|
|
119
|
+
ivp = {
|
|
120
|
+
u: [1.0, 0.5], # u(0) = [1.0, 0.5]
|
|
121
|
+
u.d(): [0.0, -0.2] # u'(0) = [0.0, -0.2]
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
sol = solve(eq, ivp=ivp, tspan=(0, 10), backend="scipy")
|
|
125
|
+
|
|
126
|
+
# Access vector solution (returns 2×N array)
|
|
127
|
+
u_trajectory = sol[u] # Shape: (2, num_timepoints)
|
|
128
|
+
|
|
129
|
+
# Access individual components (returns 1D arrays)
|
|
130
|
+
u0_trajectory = sol[u[0]]
|
|
131
|
+
u1_trajectory = sol[u[1]]
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Mixed Vector/Scalar Systems
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# Coupled system with both scalar and vector variables
|
|
138
|
+
x = var("x") # Scalar variable
|
|
139
|
+
u = var("u", shape=2) # Vector variable
|
|
140
|
+
|
|
141
|
+
eqs = [
|
|
142
|
+
Eq(x.d(2) + x - u[0], 0), # x'' + x - u₀ = 0
|
|
143
|
+
Eq(u[0].d() + u[1], x), # u₀' + u₁ = x
|
|
144
|
+
Eq(u[1].d() + u[0], 0), # u₁' + u₀ = 0
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
ivp = {
|
|
148
|
+
x: 1.0, # x(0) = 1
|
|
149
|
+
x.d(): 0.0, # x'(0) = 0
|
|
150
|
+
u: [0.5, 0.0], # u(0) = [0.5, 0.0]
|
|
151
|
+
u.d(): [0.0, 0.0] # u'(0) = [0.0, 0.0] (automatically filtered for first-order components)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
sol = solve(eqs, ivp=ivp, tspan=(0, 5), backend="scipy")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Examples
|
|
158
|
+
|
|
159
|
+
The `examples/` directory contains comprehensive examples:
|
|
160
|
+
|
|
161
|
+
- `01_ivp_damped_oscillator.py` - Numeric IVP solving with visualization
|
|
162
|
+
- `02_symbolic_simple.py` - Symbolic solutions using SymPy backend
|
|
163
|
+
- `03_mixed_orders.py` - Coupled systems with mixed derivative orders
|
|
164
|
+
- `04_vector_harmonic_oscillator.py` - 2D harmonic oscillator using vector variables
|
|
165
|
+
- `05_vector_mixed_system.py` - Mixed vector/scalar systems
|
|
166
|
+
- `06_vector_simple.py` - Simple vector variable introduction
|
|
167
|
+
|
|
168
|
+
Run any example:
|
|
169
|
+
```bash
|
|
170
|
+
python examples/01_ivp_damped_oscillator.py
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Backends
|
|
174
|
+
|
|
175
|
+
### SciPy (Numeric)
|
|
176
|
+
```python
|
|
177
|
+
# Solve numerically over a time span
|
|
178
|
+
sol = solve(eq, ivp=conditions, tspan=(0, 10), backend="scipy")
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### SymPy (Symbolic)
|
|
182
|
+
```python
|
|
183
|
+
# Get exact symbolic solution
|
|
184
|
+
sol = solve(eq, backend="sympy")
|
|
185
|
+
expr = sol.as_expr(y) # Returns SymPy expression
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Auto Backend Selection
|
|
189
|
+
```python
|
|
190
|
+
# Try symbolic first, fall back to numeric
|
|
191
|
+
sol = solve(eq, ivp=conditions, tspan=(0, 10), backend="auto")
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## API Reference
|
|
195
|
+
|
|
196
|
+
### Variables and Equations
|
|
197
|
+
- `var(name, order=None)` - Create a scalar dependent variable
|
|
198
|
+
- `var(name, shape=n)` - Create a vector variable with n components
|
|
199
|
+
- `var(name, shape=(m,n))` - Create a matrix variable (coming soon)
|
|
200
|
+
- `y.d(n)` - n-th derivative of variable y
|
|
201
|
+
- `u[i]` - Access i-th component of vector variable u
|
|
202
|
+
- `u.d(n)` - n-th derivative of vector variable (returns vector derivative)
|
|
203
|
+
- `Eq(lhs, rhs)` - Create an equation
|
|
204
|
+
- `t` - Independent variable (time)
|
|
205
|
+
|
|
206
|
+
### Solving
|
|
207
|
+
- `solve(equations, ivp=None, bvp=None, tspan=None, backend="auto")`
|
|
208
|
+
|
|
209
|
+
### Solution Objects
|
|
210
|
+
- `sol[y]` - Access solution values for variable y
|
|
211
|
+
- `sol[y.d()]` - Access derivative values
|
|
212
|
+
- `sol[u]` - Access vector solution (returns 2D array for vector variables)
|
|
213
|
+
- `sol[u[i]]` - Access i-th component solution (returns 1D array)
|
|
214
|
+
- `sol.eval(target, time_points)` - Evaluate at specific times
|
|
215
|
+
- `sol.as_first_order()` - Inspect first-order system representation
|
|
216
|
+
|
|
217
|
+
## Contributing
|
|
218
|
+
|
|
219
|
+
This project follows a test-driven development approach. All functionality is defined by comprehensive tests.
|
|
220
|
+
|
|
221
|
+
### Development Setup
|
|
222
|
+
```bash
|
|
223
|
+
git clone https://github.com/maroba/odecast.git
|
|
224
|
+
cd odecast
|
|
225
|
+
pip install -e ".[dev]"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Running Tests
|
|
229
|
+
```bash
|
|
230
|
+
pytest # Run all tests
|
|
231
|
+
pytest -v # Verbose output
|
|
232
|
+
pytest tests/test_api* # Run specific test files
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Code Quality
|
|
236
|
+
```bash
|
|
237
|
+
ruff check . # Linting
|
|
238
|
+
black . # Formatting
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Contributing Guidelines
|
|
242
|
+
1. All new features must have comprehensive tests
|
|
243
|
+
2. Follow the existing API patterns and naming conventions
|
|
244
|
+
3. Add examples for significant new functionality
|
|
245
|
+
4. Update documentation for user-facing changes
|
|
246
|
+
5. Ensure all tests pass before submitting PRs
|
|
247
|
+
|
|
248
|
+
## Status
|
|
249
|
+
|
|
250
|
+
Current implementation status:
|
|
251
|
+
- ✅ Core DSL (variables, equations, derivatives)
|
|
252
|
+
- ✅ Vector/Matrix variables with automatic equation expansion
|
|
253
|
+
- ✅ SciPy numeric backend (IVP)
|
|
254
|
+
- ✅ SymPy symbolic backend (decoupled systems)
|
|
255
|
+
- ✅ Automatic order inference and validation
|
|
256
|
+
- ✅ Comprehensive error handling
|
|
257
|
+
- ✅ Mixed-order coupled systems
|
|
258
|
+
- 🚧 BVP support (boundary value problems) - coming soon
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
MIT License - see LICENSE file for details.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "odecast"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A Python library for solving higher-order ordinary differential equations (ODEs) without manual reduction."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "Matthias Baer", email = "matthias.r.baer@googlemail.com" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Developers",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
]
|
|
22
|
+
dependencies = []
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
math = ["sympy", "scipy", "numpy", "matplotlib"]
|
|
26
|
+
dev = ["pytest", "pytest-cov"]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/maroba/odecast"
|
|
30
|
+
Repository = "https://github.com/maroba/odecast"
|
|
31
|
+
|
|
32
|
+
[tool.hatch.build.targets.wheel]
|
|
33
|
+
packages = ["src/odecast"]
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.sdist]
|
|
36
|
+
include = ["/src", "/tests"]
|