symplecta 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.
@@ -0,0 +1,52 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ # the matrix is what actually backs requires-python = ">=3.10"
12
+ runs-on: ${{ matrix.os }}
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: [ubuntu-latest]
17
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
18
+ include:
19
+ - os: windows-latest
20
+ python-version: "3.12"
21
+
22
+ steps:
23
+ - uses: actions/checkout@v7
24
+
25
+ - uses: actions/setup-python@v7
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+
29
+ - name: Install
30
+ run: python -m pip install --upgrade pip && python -m pip install -e ".[dev]"
31
+
32
+ - name: Lint
33
+ run: ruff check .
34
+
35
+ - name: Test
36
+ run: pytest -q
37
+
38
+ build:
39
+ # a release that does not build is worse than one that does not lint
40
+ runs-on: ubuntu-latest
41
+ steps:
42
+ - uses: actions/checkout@v7
43
+
44
+ - uses: actions/setup-python@v7
45
+ with:
46
+ python-version: "3.12"
47
+
48
+ - name: Build and check the distributions
49
+ run: |
50
+ python -m pip install --upgrade pip build twine
51
+ python -m build
52
+ python -m twine check --strict dist/*
@@ -0,0 +1,62 @@
1
+ name: Release
2
+
3
+ # Publishing a GitHub Release goes to the real index. Running this workflow by
4
+ # hand from the Actions tab goes to TestPyPI, so a dry run cannot reach PyPI
5
+ # even by accident.
6
+ on:
7
+ release:
8
+ types: [published]
9
+ workflow_dispatch:
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v7
16
+
17
+ - uses: actions/setup-python@v7
18
+ with:
19
+ python-version: "3.12"
20
+
21
+ - name: Build and check
22
+ run: |
23
+ python -m pip install --upgrade pip build twine
24
+ python -m build
25
+ python -m twine check --strict dist/*
26
+
27
+ - uses: actions/upload-artifact@v7
28
+ with:
29
+ name: dist
30
+ path: dist/
31
+
32
+ testpypi:
33
+ if: github.event_name == 'workflow_dispatch'
34
+ needs: build
35
+ runs-on: ubuntu-latest
36
+ environment: testpypi
37
+ permissions:
38
+ id-token: write # this is what lets PyPI verify the run; no token stored
39
+ steps:
40
+ - uses: actions/download-artifact@v8
41
+ with:
42
+ name: dist
43
+ path: dist/
44
+
45
+ - uses: pypa/gh-action-pypi-publish@release/v1
46
+ with:
47
+ repository-url: https://test.pypi.org/legacy/
48
+
49
+ pypi:
50
+ if: github.event_name == 'release'
51
+ needs: build
52
+ runs-on: ubuntu-latest
53
+ environment: pypi
54
+ permissions:
55
+ id-token: write
56
+ steps:
57
+ - uses: actions/download-artifact@v8
58
+ with:
59
+ name: dist
60
+ path: dist/
61
+
62
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .eggs/
8
+
9
+ # Environments
10
+ .venv/
11
+ venv/
12
+ env/
13
+
14
+ # Tooling caches
15
+ .pytest_cache/
16
+ .mypy_cache/
17
+ .ruff_cache/
18
+ .coverage
19
+ htmlcov/
20
+
21
+ # Editors / OS
22
+ .idea/
23
+ .vscode/
24
+ .DS_Store
25
+ Thumbs.db
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Román Pérez Santalla
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,207 @@
1
+ Metadata-Version: 2.5
2
+ Name: symplecta
3
+ Version: 0.1.0
4
+ Summary: Symplectic and geometric integrators for Hamiltonian systems, complementing scipy.integrate.solve_ivp
5
+ Project-URL: Homepage, https://github.com/RomanPerezSantalla/symplecta
6
+ Project-URL: Issues, https://github.com/RomanPerezSantalla/symplecta/issues
7
+ Author: Roman
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: RATTLE,SHAKE,hamiltonian,integrator,leapfrog,ode,symplectic,verlet
11
+ Classifier: Development Status :: 2 - Pre-Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
16
+ Classifier: Topic :: Scientific/Engineering :: Physics
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: numpy>=1.24
19
+ Requires-Dist: scipy>=1.10
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=7; extra == 'dev'
22
+ Requires-Dist: ruff>=0.5; extra == 'dev'
23
+ Description-Content-Type: text/markdown
24
+
25
+ # symplecta
26
+
27
+ Fixed-step symplectic and geometric integrators for Hamiltonian systems, in pure NumPy and SciPy.
28
+
29
+ If you have integrated an orbit or a pendulum for a long time with a general-purpose solver, you will have seen
30
+ the energy slowly climb or decay even though there is no driving or dampening. That is not a bug in the
31
+ solver, and it does not go away by taking smaller steps, it is what a method that does not respect the structure
32
+ of Hamilton's equations does. The methods here respect the structure, and their energy error stays in a band instead of
33
+ drifting, however long you run them.
34
+
35
+ `scipy.integrate.solve_ivp` ships only general-purpose adaptive solvers (RK45, RK23, DOP853, Radau, BDF, LSODA)
36
+ and none of them are symplectic. We try to fill that gap while keeping a similar structure to scipy for ease of use.
37
+
38
+ ## Status
39
+
40
+ Pre-alpha. All eight methods and `solve_symplectic_ivp` work, and the test suite checks convergence order, energy
41
+ behavior and constraint satisfaction for each of them. The API may still move.
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ pip install symplecta
47
+ ```
48
+
49
+ ## Using it
50
+
51
+ Describe the Hamiltonian and hand it to the solver. How you describe it decides which methods you can use.
52
+
53
+ For a separable system, $H = T(p) + V(q)$, give a force and a mass:
54
+
55
+ ```python
56
+ import numpy as np
57
+ from symplecta import solve_symplectic_ivp
58
+
59
+ res = solve_symplectic_ivp(
60
+ t_span=(0.0, 100.0),
61
+ q0=np.array([1.0]),
62
+ p0=np.array([0.0]),
63
+ force=lambda t, q: -q,
64
+ potential=lambda t, q: 0.5 * q @ q, # optional, only to report energy
65
+ method="verlet",
66
+ h=0.01,
67
+ )
68
+
69
+ res.t, res.q, res.p, res.energy
70
+ ```
71
+
72
+ For a non-separable one, give the two partials of $H$, or just $H$ itself and let the partials be taken
73
+ numerically:
74
+
75
+ ```python
76
+ res = solve_symplectic_ivp(
77
+ t_span=(0.0, 100.0),
78
+ q0=q0,
79
+ p0=p0,
80
+ hamiltonian=lambda t, q, p: 0.5 * np.sum(p**2 * (1 + q**2)) + 0.5 * np.sum(q**2),
81
+ method="implicit_midpoint",
82
+ h=0.01,
83
+ )
84
+ ```
85
+
86
+ For a constrained system, add the constraint and its Jacobian:
87
+
88
+ ```python
89
+ res = solve_symplectic_ivp(
90
+ t_span=(0.0, 50.0),
91
+ q0=q0,
92
+ p0=p0,
93
+ force=lambda t, q: np.array([0.0, -9.81]),
94
+ constraints={"g": lambda q: np.array([q @ q - 1.0]),
95
+ "jac": lambda q: 2 * q[None, :]},
96
+ method="rattle",
97
+ h=0.005,
98
+ )
99
+ ```
100
+
101
+ The result is shaped after scipy's, with `.t`, `.q`, `.p`, `.y`, `.energy`, `.success` and `.message`, and uses
102
+ the same `(n_dof, n_points)` array layout, so `res.q[0]` is the trajectory of the first coordinate.
103
+
104
+ ## Methods
105
+
106
+ | `method=` | order | needs | cost per step |
107
+ | --- | --- | --- | --- |
108
+ | `symplectic_euler` | 1 | force, mass | 1 force evaluation |
109
+ | `verlet` | 2 | force, mass | 2 force evaluations |
110
+ | `yoshida4` | 4 | force, mass | 3 force evaluations |
111
+ | `yoshida6` | 6 | force, mass | 7 force evaluations |
112
+ | `yoshida8` | 8 | force, mass | 15 force evaluations |
113
+ | `shake` | 2 | force, mass, constraints | 2 force evaluations, one nonlinear solve |
114
+ | `rattle` | 2 | force, mass, constraints | as SHAKE plus one linear solve |
115
+ | `implicit_midpoint` | 2 | dHdq and dHdp, or hamiltonian | several gradient evaluations |
116
+
117
+ `verlet` is the sensible default for a separable system. `yoshida4` costs one more force evaluation and buys
118
+ about three orders of magnitude in the energy band, and `yoshida6` is worth its seven for a long run. Given a
119
+ fixed budget of force evaluations rather than a fixed step, the gains are smaller than that table suggests;
120
+ [the methods document][methods-doc] compares them at equal cost. `rattle` is preferable to `shake` in basically
121
+ every scenario.
122
+
123
+ The solver refuses combinations that would quietly give you the wrong answer, rather than running them.
124
+ Unallowed combinations are: an explicit method on a non-separable Hamiltonian, an unconstrained method on a
125
+ constrained system, or a mass alongside the general form.
126
+
127
+ ## Documentation
128
+
129
+ The documents in [`docs/`][docs-dir] are written to be read in order, and are meant to explain the methods rather
130
+ than just list the API. I wrote them because I found SHAKE and RATTLE genuinely hard to pick up from the original
131
+ papers.
132
+
133
+ - [Introduction][intro-doc] — equations of motion, separable Hamiltonians, and why a regular method
134
+ loses energy while a symplectic one does not.
135
+ - [General methods][methods-doc] — symplectic Euler, velocity Verlet and the Yoshida compositions, all
136
+ built out of drifts and kicks.
137
+ - [Constraint methods][constraints-doc] — Lagrange multipliers from the action, then SHAKE and RATTLE and
138
+ what separates them.
139
+ - [Implicit midpoint][midpoint-doc] — what to do when the Hamiltonian does not split.
140
+
141
+ ## Design decisions
142
+
143
+ - **Fixed step only.** Adaptive stepping breaks the fixed-step map that the energy behavior relies on, so there
144
+ is no adaptive control and no plan to add it.
145
+ - **Structure-aware inputs.** The solver takes `q`, `p` and a force rather than a flattened `fun(t, y)`, because
146
+ the methods need the two halves of $H$ separately.
147
+ - **`t_eval` by interpolation.** Output at arbitrary times comes from interpolating the fixed grid afterwards,
148
+ not from dense output per step.
149
+ - **Small and medium systems.** Few-body mechanics and small molecular toys. Force fields, GPU kernels and
150
+ large-scale MD are [OpenMM's](https://openmm.org/) job, not this one.
151
+
152
+ ## Further reading
153
+
154
+ The two standard texts, in rough order of how gently they start:
155
+
156
+ - Leimkuhler, B. & Reich, S. (2005). *Simulating Hamiltonian Dynamics*. Cambridge University Press.
157
+ [doi:10.1017/CBO9780511614118](https://doi.org/10.1017/CBO9780511614118)
158
+ - Hairer, E., Lubich, C. & Wanner, G. (2006). *Geometric Numerical Integration*, 2nd ed. Springer.
159
+ [doi:10.1007/3-540-30666-8](https://doi.org/10.1007/3-540-30666-8) — the reference for backward error
160
+ analysis and why the energy error stays bounded.
161
+ - Sanz-Serna, J. M. (1992). Symplectic integrators for Hamiltonian problems: an overview. *Acta Numerica* **1**,
162
+ 243–286. [doi:10.1017/S0962492900002282](https://doi.org/10.1017/S0962492900002282) — a shorter survey if a
163
+ whole book is too much.
164
+
165
+ The original papers for the methods here:
166
+
167
+ - Verlet, L. (1967). Computer "experiments" on classical fluids. *Physical Review* **159**, 98–103.
168
+ [doi:10.1103/PhysRev.159.98](https://doi.org/10.1103/PhysRev.159.98)
169
+ - Ryckaert, J.-P., Ciccotti, G. & Berendsen, H. J. C. (1977). Numerical integration of the cartesian equations of
170
+ motion of a system with constraints: molecular dynamics of n-alkanes. *Journal of Computational Physics* **23**,
171
+ 327–341. [doi:10.1016/0021-9991(77)90098-5](https://doi.org/10.1016/0021-9991%2877%2990098-5) — SHAKE.
172
+ - Andersen, H. C. (1983). RATTLE: a "velocity" version of the SHAKE algorithm for molecular dynamics
173
+ calculations. *Journal of Computational Physics* **52**, 24–34.
174
+ [doi:10.1016/0021-9991(83)90014-1](https://doi.org/10.1016/0021-9991%2883%2990014-1)
175
+ - Yoshida, H. (1990). Construction of higher order symplectic integrators. *Physics Letters A* **150**, 262–268.
176
+ [doi:10.1016/0375-9601(90)90092-3](https://doi.org/10.1016/0375-9601%2890%2990092-3)
177
+
178
+ ## Development
179
+
180
+ Clone the repository, then install it in editable mode with the test and lint tools:
181
+
182
+ ```bash
183
+ pip install -e ".[dev]"
184
+ ```
185
+
186
+ ```bash
187
+ pytest
188
+ ```
189
+
190
+ ```bash
191
+ ruff check .
192
+ ```
193
+
194
+ ## AI Usage
195
+ AI has been used to design a scaffolding for the project, design tests, correct bugs and revise the documents. Most of
196
+ the code and the text from the docs is original.
197
+
198
+ ## License
199
+
200
+ MIT
201
+
202
+ <!-- PyPI renders the README outside the repo, so these must be absolute -->
203
+ [docs-dir]: https://github.com/RomanPerezSantalla/symplecta/tree/main/docs
204
+ [intro-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/introduction.md
205
+ [methods-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/methods.md
206
+ [constraints-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/constraints.md
207
+ [midpoint-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/implicit_midpoint.md
@@ -0,0 +1,183 @@
1
+ # symplecta
2
+
3
+ Fixed-step symplectic and geometric integrators for Hamiltonian systems, in pure NumPy and SciPy.
4
+
5
+ If you have integrated an orbit or a pendulum for a long time with a general-purpose solver, you will have seen
6
+ the energy slowly climb or decay even though there is no driving or dampening. That is not a bug in the
7
+ solver, and it does not go away by taking smaller steps, it is what a method that does not respect the structure
8
+ of Hamilton's equations does. The methods here respect the structure, and their energy error stays in a band instead of
9
+ drifting, however long you run them.
10
+
11
+ `scipy.integrate.solve_ivp` ships only general-purpose adaptive solvers (RK45, RK23, DOP853, Radau, BDF, LSODA)
12
+ and none of them are symplectic. We try to fill that gap while keeping a similar structure to scipy for ease of use.
13
+
14
+ ## Status
15
+
16
+ Pre-alpha. All eight methods and `solve_symplectic_ivp` work, and the test suite checks convergence order, energy
17
+ behavior and constraint satisfaction for each of them. The API may still move.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install symplecta
23
+ ```
24
+
25
+ ## Using it
26
+
27
+ Describe the Hamiltonian and hand it to the solver. How you describe it decides which methods you can use.
28
+
29
+ For a separable system, $H = T(p) + V(q)$, give a force and a mass:
30
+
31
+ ```python
32
+ import numpy as np
33
+ from symplecta import solve_symplectic_ivp
34
+
35
+ res = solve_symplectic_ivp(
36
+ t_span=(0.0, 100.0),
37
+ q0=np.array([1.0]),
38
+ p0=np.array([0.0]),
39
+ force=lambda t, q: -q,
40
+ potential=lambda t, q: 0.5 * q @ q, # optional, only to report energy
41
+ method="verlet",
42
+ h=0.01,
43
+ )
44
+
45
+ res.t, res.q, res.p, res.energy
46
+ ```
47
+
48
+ For a non-separable one, give the two partials of $H$, or just $H$ itself and let the partials be taken
49
+ numerically:
50
+
51
+ ```python
52
+ res = solve_symplectic_ivp(
53
+ t_span=(0.0, 100.0),
54
+ q0=q0,
55
+ p0=p0,
56
+ hamiltonian=lambda t, q, p: 0.5 * np.sum(p**2 * (1 + q**2)) + 0.5 * np.sum(q**2),
57
+ method="implicit_midpoint",
58
+ h=0.01,
59
+ )
60
+ ```
61
+
62
+ For a constrained system, add the constraint and its Jacobian:
63
+
64
+ ```python
65
+ res = solve_symplectic_ivp(
66
+ t_span=(0.0, 50.0),
67
+ q0=q0,
68
+ p0=p0,
69
+ force=lambda t, q: np.array([0.0, -9.81]),
70
+ constraints={"g": lambda q: np.array([q @ q - 1.0]),
71
+ "jac": lambda q: 2 * q[None, :]},
72
+ method="rattle",
73
+ h=0.005,
74
+ )
75
+ ```
76
+
77
+ The result is shaped after scipy's, with `.t`, `.q`, `.p`, `.y`, `.energy`, `.success` and `.message`, and uses
78
+ the same `(n_dof, n_points)` array layout, so `res.q[0]` is the trajectory of the first coordinate.
79
+
80
+ ## Methods
81
+
82
+ | `method=` | order | needs | cost per step |
83
+ | --- | --- | --- | --- |
84
+ | `symplectic_euler` | 1 | force, mass | 1 force evaluation |
85
+ | `verlet` | 2 | force, mass | 2 force evaluations |
86
+ | `yoshida4` | 4 | force, mass | 3 force evaluations |
87
+ | `yoshida6` | 6 | force, mass | 7 force evaluations |
88
+ | `yoshida8` | 8 | force, mass | 15 force evaluations |
89
+ | `shake` | 2 | force, mass, constraints | 2 force evaluations, one nonlinear solve |
90
+ | `rattle` | 2 | force, mass, constraints | as SHAKE plus one linear solve |
91
+ | `implicit_midpoint` | 2 | dHdq and dHdp, or hamiltonian | several gradient evaluations |
92
+
93
+ `verlet` is the sensible default for a separable system. `yoshida4` costs one more force evaluation and buys
94
+ about three orders of magnitude in the energy band, and `yoshida6` is worth its seven for a long run. Given a
95
+ fixed budget of force evaluations rather than a fixed step, the gains are smaller than that table suggests;
96
+ [the methods document][methods-doc] compares them at equal cost. `rattle` is preferable to `shake` in basically
97
+ every scenario.
98
+
99
+ The solver refuses combinations that would quietly give you the wrong answer, rather than running them.
100
+ Unallowed combinations are: an explicit method on a non-separable Hamiltonian, an unconstrained method on a
101
+ constrained system, or a mass alongside the general form.
102
+
103
+ ## Documentation
104
+
105
+ The documents in [`docs/`][docs-dir] are written to be read in order, and are meant to explain the methods rather
106
+ than just list the API. I wrote them because I found SHAKE and RATTLE genuinely hard to pick up from the original
107
+ papers.
108
+
109
+ - [Introduction][intro-doc] — equations of motion, separable Hamiltonians, and why a regular method
110
+ loses energy while a symplectic one does not.
111
+ - [General methods][methods-doc] — symplectic Euler, velocity Verlet and the Yoshida compositions, all
112
+ built out of drifts and kicks.
113
+ - [Constraint methods][constraints-doc] — Lagrange multipliers from the action, then SHAKE and RATTLE and
114
+ what separates them.
115
+ - [Implicit midpoint][midpoint-doc] — what to do when the Hamiltonian does not split.
116
+
117
+ ## Design decisions
118
+
119
+ - **Fixed step only.** Adaptive stepping breaks the fixed-step map that the energy behavior relies on, so there
120
+ is no adaptive control and no plan to add it.
121
+ - **Structure-aware inputs.** The solver takes `q`, `p` and a force rather than a flattened `fun(t, y)`, because
122
+ the methods need the two halves of $H$ separately.
123
+ - **`t_eval` by interpolation.** Output at arbitrary times comes from interpolating the fixed grid afterwards,
124
+ not from dense output per step.
125
+ - **Small and medium systems.** Few-body mechanics and small molecular toys. Force fields, GPU kernels and
126
+ large-scale MD are [OpenMM's](https://openmm.org/) job, not this one.
127
+
128
+ ## Further reading
129
+
130
+ The two standard texts, in rough order of how gently they start:
131
+
132
+ - Leimkuhler, B. & Reich, S. (2005). *Simulating Hamiltonian Dynamics*. Cambridge University Press.
133
+ [doi:10.1017/CBO9780511614118](https://doi.org/10.1017/CBO9780511614118)
134
+ - Hairer, E., Lubich, C. & Wanner, G. (2006). *Geometric Numerical Integration*, 2nd ed. Springer.
135
+ [doi:10.1007/3-540-30666-8](https://doi.org/10.1007/3-540-30666-8) — the reference for backward error
136
+ analysis and why the energy error stays bounded.
137
+ - Sanz-Serna, J. M. (1992). Symplectic integrators for Hamiltonian problems: an overview. *Acta Numerica* **1**,
138
+ 243–286. [doi:10.1017/S0962492900002282](https://doi.org/10.1017/S0962492900002282) — a shorter survey if a
139
+ whole book is too much.
140
+
141
+ The original papers for the methods here:
142
+
143
+ - Verlet, L. (1967). Computer "experiments" on classical fluids. *Physical Review* **159**, 98–103.
144
+ [doi:10.1103/PhysRev.159.98](https://doi.org/10.1103/PhysRev.159.98)
145
+ - Ryckaert, J.-P., Ciccotti, G. & Berendsen, H. J. C. (1977). Numerical integration of the cartesian equations of
146
+ motion of a system with constraints: molecular dynamics of n-alkanes. *Journal of Computational Physics* **23**,
147
+ 327–341. [doi:10.1016/0021-9991(77)90098-5](https://doi.org/10.1016/0021-9991%2877%2990098-5) — SHAKE.
148
+ - Andersen, H. C. (1983). RATTLE: a "velocity" version of the SHAKE algorithm for molecular dynamics
149
+ calculations. *Journal of Computational Physics* **52**, 24–34.
150
+ [doi:10.1016/0021-9991(83)90014-1](https://doi.org/10.1016/0021-9991%2883%2990014-1)
151
+ - Yoshida, H. (1990). Construction of higher order symplectic integrators. *Physics Letters A* **150**, 262–268.
152
+ [doi:10.1016/0375-9601(90)90092-3](https://doi.org/10.1016/0375-9601%2890%2990092-3)
153
+
154
+ ## Development
155
+
156
+ Clone the repository, then install it in editable mode with the test and lint tools:
157
+
158
+ ```bash
159
+ pip install -e ".[dev]"
160
+ ```
161
+
162
+ ```bash
163
+ pytest
164
+ ```
165
+
166
+ ```bash
167
+ ruff check .
168
+ ```
169
+
170
+ ## AI Usage
171
+ AI has been used to design a scaffolding for the project, design tests, correct bugs and revise the documents. Most of
172
+ the code and the text from the docs is original.
173
+
174
+ ## License
175
+
176
+ MIT
177
+
178
+ <!-- PyPI renders the README outside the repo, so these must be absolute -->
179
+ [docs-dir]: https://github.com/RomanPerezSantalla/symplecta/tree/main/docs
180
+ [intro-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/introduction.md
181
+ [methods-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/methods.md
182
+ [constraints-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/constraints.md
183
+ [midpoint-doc]: https://github.com/RomanPerezSantalla/symplecta/blob/main/docs/implicit_midpoint.md