rbflab 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.
- rbflab-0.1.0/CHANGELOG.md +12 -0
- rbflab-0.1.0/LICENSE +21 -0
- rbflab-0.1.0/MANIFEST.in +7 -0
- rbflab-0.1.0/PKG-INFO +85 -0
- rbflab-0.1.0/README.md +129 -0
- rbflab-0.1.0/README_PYPI.md +49 -0
- rbflab-0.1.0/RELEASE_CHECKLIST.md +51 -0
- rbflab-0.1.0/cpp/README.md +60 -0
- rbflab-0.1.0/cpp/cuda_lhi.cu +78 -0
- rbflab-0.1.0/cpp/eigen_solver.hpp +92 -0
- rbflab-0.1.0/cpp/generated_hybrid.hpp +2101 -0
- rbflab-0.1.0/cpp/generated_imq.hpp +493 -0
- rbflab-0.1.0/cpp/lhi_mpfr.cpp +78 -0
- rbflab-0.1.0/cpp/operators_local.hpp +62 -0
- rbflab-0.1.0/cpp/prepare_dependencies.sh +9 -0
- rbflab-0.1.0/cpp/real.hpp +49 -0
- rbflab-0.1.0/cpp/real_double.hpp +36 -0
- rbflab-0.1.0/cpp/scalar_local.hpp +68 -0
- rbflab-0.1.0/docs/CAPABILITIES.md +24 -0
- rbflab-0.1.0/docs/INSTALL.md +74 -0
- rbflab-0.1.0/docs/VISUALIZATION.md +48 -0
- rbflab-0.1.0/docs/assets/heat_diffusion.gif +0 -0
- rbflab-0.1.0/docs/assets/heat_diffusion.png +0 -0
- rbflab-0.1.0/docs/assets/navier_stokes_cavity.gif +0 -0
- rbflab-0.1.0/docs/assets/navier_stokes_cavity.png +0 -0
- rbflab-0.1.0/docs/assets/stokes_velocity.png +0 -0
- rbflab-0.1.0/docs/guides/LID_DRIVEN_CAVITY.md +29 -0
- rbflab-0.1.0/examples/build_cpp_backend.py +42 -0
- rbflab-0.1.0/examples/custom_kernel.py +36 -0
- rbflab-0.1.0/examples/generate_cpp_hybrid.py +33 -0
- rbflab-0.1.0/examples/make_gallery.py +72 -0
- rbflab-0.1.0/examples/navier_stokes_cavity.py +128 -0
- rbflab-0.1.0/examples/operators_3d.py +39 -0
- rbflab-0.1.0/examples/stokes_spaces.py +63 -0
- rbflab-0.1.0/examples/symbolic_boundary.py +32 -0
- rbflab-0.1.0/examples/symbolic_heat.py +28 -0
- rbflab-0.1.0/examples/symbolic_poisson.py +46 -0
- rbflab-0.1.0/pyproject.toml +41 -0
- rbflab-0.1.0/python/rbflab/__init__.py +70 -0
- rbflab-0.1.0/python/rbflab/assembly.py +73 -0
- rbflab-0.1.0/python/rbflab/block_methods.py +292 -0
- rbflab-0.1.0/python/rbflab/cuda_backend.py +85 -0
- rbflab-0.1.0/python/rbflab/descriptor.py +36 -0
- rbflab-0.1.0/python/rbflab/diagnostics.py +56 -0
- rbflab-0.1.0/python/rbflab/differentiable_lhi.py +147 -0
- rbflab-0.1.0/python/rbflab/discrete_operators.py +227 -0
- rbflab-0.1.0/python/rbflab/eigen_build.py +15 -0
- rbflab-0.1.0/python/rbflab/evolution.py +378 -0
- rbflab-0.1.0/python/rbflab/geometry.py +190 -0
- rbflab-0.1.0/python/rbflab/hermite.py +39 -0
- rbflab-0.1.0/python/rbflab/kernel_compiler.py +283 -0
- rbflab-0.1.0/python/rbflab/kernels.py +187 -0
- rbflab-0.1.0/python/rbflab/legacy_cpp.py +120 -0
- rbflab-0.1.0/python/rbflab/lhi_backends.py +293 -0
- rbflab-0.1.0/python/rbflab/lhi_stokes.py +367 -0
- rbflab-0.1.0/python/rbflab/mesh_adapters.py +43 -0
- rbflab-0.1.0/python/rbflab/methods.py +302 -0
- rbflab-0.1.0/python/rbflab/nodal.py +233 -0
- rbflab-0.1.0/python/rbflab/operator_backends.py +128 -0
- rbflab-0.1.0/python/rbflab/operators.py +168 -0
- rbflab-0.1.0/python/rbflab/precision.py +263 -0
- rbflab-0.1.0/python/rbflab/problems.py +101 -0
- rbflab-0.1.0/python/rbflab/rbf_fd.py +148 -0
- rbflab-0.1.0/python/rbflab/rbf_ra.py +76 -0
- rbflab-0.1.0/python/rbflab/reference.py +131 -0
- rbflab-0.1.0/python/rbflab/scalar_backends.py +186 -0
- rbflab-0.1.0/python/rbflab/scalar_fd.py +55 -0
- rbflab-0.1.0/python/rbflab/scaling.py +30 -0
- rbflab-0.1.0/python/rbflab/space_stokes.py +328 -0
- rbflab-0.1.0/python/rbflab/spaces.py +126 -0
- rbflab-0.1.0/python/rbflab/sparse_precision.py +175 -0
- rbflab-0.1.0/python/rbflab/stencils.py +86 -0
- rbflab-0.1.0/python/rbflab/stokes.py +166 -0
- rbflab-0.1.0/python/rbflab/stokes_polynomials.py +53 -0
- rbflab-0.1.0/python/rbflab/stokes_ra.py +98 -0
- rbflab-0.1.0/python/rbflab/strategies.py +45 -0
- rbflab-0.1.0/python/rbflab/symbolic.py +275 -0
- rbflab-0.1.0/python/rbflab/symbolic_kernel.py +172 -0
- rbflab-0.1.0/python/rbflab/symbolic_system.py +149 -0
- rbflab-0.1.0/python/rbflab/time_data.py +62 -0
- rbflab-0.1.0/python/rbflab/torch_backend.py +277 -0
- rbflab-0.1.0/python/rbflab/unsteady_stokes.py +167 -0
- rbflab-0.1.0/python/rbflab/viz.py +216 -0
- rbflab-0.1.0/python/rbflab.egg-info/PKG-INFO +85 -0
- rbflab-0.1.0/python/rbflab.egg-info/SOURCES.txt +87 -0
- rbflab-0.1.0/python/rbflab.egg-info/dependency_links.txt +1 -0
- rbflab-0.1.0/python/rbflab.egg-info/requires.txt +21 -0
- rbflab-0.1.0/python/rbflab.egg-info/top_level.txt +1 -0
- rbflab-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Curated six symbolic and numerical starting examples.
|
|
6
|
+
- Added `unit_box_grid` for small 2D/3D problems without Gmsh.
|
|
7
|
+
- Documented installation, optional backends, precision stages, and current
|
|
8
|
+
capability limits.
|
|
9
|
+
- Added optional 2D field plots and GIF animation, with reproducible README
|
|
10
|
+
images generated from numerical heat and Stokes solutions.
|
|
11
|
+
- Added a standalone Re=100 staggered RBF-FD Navier–Stokes cavity example and
|
|
12
|
+
an optional nodal-velocity GIF/streamline plotting helper.
|
rbflab-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Louis Breton
|
|
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.
|
rbflab-0.1.0/MANIFEST.in
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
include README.md README_PYPI.md CHANGELOG.md LICENSE RELEASE_CHECKLIST.md
|
|
2
|
+
include docs/INSTALL.md docs/CAPABILITIES.md docs/VISUALIZATION.md docs/guides/LID_DRIVEN_CAVITY.md
|
|
3
|
+
recursive-include docs/assets *.png *.gif
|
|
4
|
+
recursive-include examples *.py
|
|
5
|
+
recursive-include cpp *.cpp *.hpp *.cu *.sh *.md
|
|
6
|
+
prune tests
|
|
7
|
+
global-exclude __pycache__ *.py[cod] *.exe
|
rbflab-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rbflab
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: RBFLAB: radial basis functions for interpolation and PDEs
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/LDBreton/RBFLAB
|
|
7
|
+
Project-URL: Repository, https://github.com/LDBreton/RBFLAB
|
|
8
|
+
Project-URL: Issues, https://github.com/LDBreton/RBFLAB/issues
|
|
9
|
+
Keywords: radial basis functions,meshless,PDE,RBF-FD,collocation
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: numpy>=1.26
|
|
20
|
+
Requires-Dist: scipy>=1.11
|
|
21
|
+
Requires-Dist: sympy>=1.12
|
|
22
|
+
Requires-Dist: mpmath>=1.3
|
|
23
|
+
Requires-Dist: threadpoolctl>=3.5
|
|
24
|
+
Provides-Extra: torch
|
|
25
|
+
Requires-Dist: torch>=2.2; extra == "torch"
|
|
26
|
+
Provides-Extra: fast
|
|
27
|
+
Requires-Dist: gmpy2>=2.2; extra == "fast"
|
|
28
|
+
Provides-Extra: mesh
|
|
29
|
+
Requires-Dist: gmsh>=4.12; extra == "mesh"
|
|
30
|
+
Provides-Extra: test
|
|
31
|
+
Requires-Dist: pytest>=8; extra == "test"
|
|
32
|
+
Provides-Extra: examples
|
|
33
|
+
Requires-Dist: matplotlib>=3.8; extra == "examples"
|
|
34
|
+
Requires-Dist: pillow>=10; extra == "examples"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# RBFLAB
|
|
38
|
+
|
|
39
|
+
RBFLAB is a Python library for radial basis function interpolation and PDEs on
|
|
40
|
+
point clouds. It supports global collocation, local Hermite interpolation
|
|
41
|
+
(LHI), RBF-FD, symbolic linear operators and boundary conditions, and
|
|
42
|
+
divergence-free velocity spaces. Scalar operators work in 2D and 3D.
|
|
43
|
+
|
|
44
|
+
Source and examples: https://github.com/LDBreton/RBFLAB
|
|
45
|
+
|
|
46
|
+
Install the Python core with:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
python -m pip install rbflab
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Optional extras include `rbflab[examples]` for Matplotlib plots and GIFs,
|
|
53
|
+
`rbflab[mesh]` for Gmsh, and `rbflab[torch]` for the documented CPU Float64
|
|
54
|
+
tensor backend. The C++ double/MPFR backends are source-build options.
|
|
55
|
+
|
|
56
|
+
Here is a symbolic Poisson problem:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import sympy as sp
|
|
60
|
+
import rbflab as rbf
|
|
61
|
+
|
|
62
|
+
model = rbf.SymbolicScalar(2)
|
|
63
|
+
u = model.field
|
|
64
|
+
x, y = model.coordinates
|
|
65
|
+
exact = sp.sin(sp.pi*x) * sp.sin(sp.pi*y)
|
|
66
|
+
lhs = -model.laplacian(u)
|
|
67
|
+
problem = model.stationary(
|
|
68
|
+
sp.Eq(lhs, lhs.subs(u, exact).doit()),
|
|
69
|
+
boundary=[model.bc("boundary", sp.Eq(u, 0))],
|
|
70
|
+
)
|
|
71
|
+
cloud = rbf.unit_box_grid(5)
|
|
72
|
+
solution = problem.solve(
|
|
73
|
+
cloud, rbf.GlobalCollocation(rbf.IMQ(2), scheme="asymmetric")
|
|
74
|
+
)
|
|
75
|
+
print(solution.evaluate([[0.3, 0.4]]))
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The source repository also contains short heat, Stokes, 3D operator, custom
|
|
79
|
+
kernel, and Re=100 lid-driven cavity examples. The cavity example is a
|
|
80
|
+
showcase with a documented nonzero discrete divergence defect; it does not
|
|
81
|
+
validate a general Navier–Stokes solver. LHI off-node reconstruction has its
|
|
82
|
+
own accuracy limitations, and MPFR local weights do not automatically make a
|
|
83
|
+
global sparse solve extended precision.
|
|
84
|
+
|
|
85
|
+
RBFLAB is licensed under MIT.
|
rbflab-0.1.0/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# RBFLAB
|
|
2
|
+
|
|
3
|
+
RBFLAB solves interpolation and linear PDE problems on point clouds with radial
|
|
4
|
+
basis functions. Write an equation with SymPy, choose global collocation,
|
|
5
|
+
local Hermite interpolation (LHI), or RBF-FD, then inspect the result.
|
|
6
|
+
|
|
7
|
+
The default installation uses Python, NumPy, and SciPy. C++ and PyTorch are
|
|
8
|
+
optional numerical backends for supported local methods. The package supports
|
|
9
|
+
2D and 3D scalar methods and divergence-free approximation spaces.
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
*The heat pulse comes from a symbolic PDE solved with global RBF collocation;
|
|
14
|
+
the faint dots in the first panel are the collocation nodes. The display grid
|
|
15
|
+
only samples the computed solution.*
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
From this source checkout:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
python -m pip install .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The default installation does not require Gmsh, a compiler, or PyTorch. See
|
|
26
|
+
[the installation guide](docs/INSTALL.md) for optional features. A PyPI
|
|
27
|
+
release will add `python -m pip install rbflab`.
|
|
28
|
+
|
|
29
|
+
## A symbolic PDE in a few lines
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import sympy as sp
|
|
33
|
+
import rbflab as rbf
|
|
34
|
+
|
|
35
|
+
model = rbf.SymbolicScalar(2)
|
|
36
|
+
u = model.field
|
|
37
|
+
x, y = model.coordinates
|
|
38
|
+
exact = sp.sin(sp.pi*x) * sp.sin(sp.pi*y)
|
|
39
|
+
lhs = -model.laplacian(u)
|
|
40
|
+
problem = model.stationary(
|
|
41
|
+
sp.Eq(lhs, lhs.subs(u, exact).doit()),
|
|
42
|
+
boundary=[model.bc("boundary", sp.Eq(u, 0))],
|
|
43
|
+
)
|
|
44
|
+
cloud = rbf.unit_box_grid(5)
|
|
45
|
+
solution = problem.solve(cloud, rbf.GlobalCollocation(rbf.IMQ(2), scheme="asymmetric"))
|
|
46
|
+
print(solution.evaluate([[0.3, 0.4]]))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Change the last method to `rbf.LHI(rbf.PHS(5), 20, polynomial_degree=2)`
|
|
50
|
+
or `rbf.RBFFD(rbf.PHS(5), 20, polynomial_degree=2)` to compare methods on
|
|
51
|
+
the same equation and cloud. The complete example reports errors against the
|
|
52
|
+
known solution.
|
|
53
|
+
|
|
54
|
+
## Six starting examples
|
|
55
|
+
|
|
56
|
+
| Example | What it shows |
|
|
57
|
+
|---|---|
|
|
58
|
+
| [Symbolic Poisson](examples/symbolic_poisson.py) | One equation with global, LHI, and RBF-FD |
|
|
59
|
+
| [Mixed boundary](examples/symbolic_boundary.py) | Variable diffusion and a Robin condition |
|
|
60
|
+
| [Heat](examples/symbolic_heat.py) | Symbolic time evolution |
|
|
61
|
+
| [Stokes spaces](examples/stokes_spaces.py) | Divergence-free velocity and pressure gradient |
|
|
62
|
+
| [3D operators](examples/operators_3d.py) | Sparse matrices, local reconstruction, backend choice |
|
|
63
|
+
| [Custom kernel](examples/custom_kernel.py) | Symbolic kernel definition and optional C++ cache |
|
|
64
|
+
|
|
65
|
+
Run, for example, `python examples/symbolic_poisson.py --method lhi` after
|
|
66
|
+
installing the package. The [capability table](docs/CAPABILITIES.md) identifies
|
|
67
|
+
which methods and backends are supported. These small examples use deterministic
|
|
68
|
+
points and do not require mesh generation.
|
|
69
|
+
|
|
70
|
+
## Plot a result
|
|
71
|
+
|
|
72
|
+
Install the optional plotting tools with `python -m pip install "rbflab[examples]"`.
|
|
73
|
+
The plotting code is separate from the numerical solver:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
from rbflab import viz
|
|
77
|
+
|
|
78
|
+
fig, ax = viz.plot_scalar(solution, title="My solution")
|
|
79
|
+
fig.savefig("solution.png", dpi=160)
|
|
80
|
+
# For a time-dependent result: viz.animate_scalar(trajectory, "heat.gif")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+

|
|
84
|
+
|
|
85
|
+
The [gallery generator](examples/make_gallery.py) rebuilds this animation and
|
|
86
|
+
the figures with `python -m examples.make_gallery`. It also plots the solved
|
|
87
|
+
divergence-free Stokes velocity field:
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
See the short [plotting guide](docs/VISUALIZATION.md) for the plotting calls and
|
|
92
|
+
their 2D scope.
|
|
93
|
+
|
|
94
|
+
## A moving fluid: Navier–Stokes cavity
|
|
95
|
+
|
|
96
|
+
From a source checkout, the [cavity example](examples/navier_stokes_cavity.py)
|
|
97
|
+
solves a Re=100 lid-driven flow with staggered RBF-FD and makes an animation
|
|
98
|
+
in one command:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
python -m examples.navier_stokes_cavity
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+

|
|
105
|
+
|
|
106
|
+

|
|
107
|
+
|
|
108
|
+
It uses a unit square built directly from NumPy, PHS7 kernels with degree-three
|
|
109
|
+
polynomials, and a coupled velocity–pressure time step. The
|
|
110
|
+
[cavity guide](docs/guides/LID_DRIVEN_CAVITY.md) reports the refinement comparison and the
|
|
111
|
+
remaining divergence defect. The animation interpolates nodal values solely
|
|
112
|
+
for display. The final frame is also saved as a PNG.
|
|
113
|
+
|
|
114
|
+
The core library lives in `python/rbflab/`; runnable examples are in
|
|
115
|
+
`examples/`, and focused contributor tests are in `tests/`.
|
|
116
|
+
|
|
117
|
+
## Numerical scope
|
|
118
|
+
|
|
119
|
+
The Stokes example uses a divergence-free velocity kernel: incompressibility
|
|
120
|
+
is built into the approximation space. LHI reconstructs pressure gradients
|
|
121
|
+
locally; it does not automatically produce a globally normalized pressure.
|
|
122
|
+
Off-node LHI evaluation currently uses the nearest stencil and can have a
|
|
123
|
+
different error from nodal values. Report solution error, algebraic residual,
|
|
124
|
+
and PDE residual separately.
|
|
125
|
+
|
|
126
|
+
The cavity showcase is not validation of a general Navier–Stokes solver.
|
|
127
|
+
|
|
128
|
+
See [installation](docs/INSTALL.md), [capabilities](docs/CAPABILITIES.md),
|
|
129
|
+
the [MIT license](LICENSE), and the [release checklist](RELEASE_CHECKLIST.md).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# RBFLAB
|
|
2
|
+
|
|
3
|
+
RBFLAB is a Python library for radial basis function interpolation and PDEs on
|
|
4
|
+
point clouds. It supports global collocation, local Hermite interpolation
|
|
5
|
+
(LHI), RBF-FD, symbolic linear operators and boundary conditions, and
|
|
6
|
+
divergence-free velocity spaces. Scalar operators work in 2D and 3D.
|
|
7
|
+
|
|
8
|
+
Source and examples: https://github.com/LDBreton/RBFLAB
|
|
9
|
+
|
|
10
|
+
Install the Python core with:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
python -m pip install rbflab
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Optional extras include `rbflab[examples]` for Matplotlib plots and GIFs,
|
|
17
|
+
`rbflab[mesh]` for Gmsh, and `rbflab[torch]` for the documented CPU Float64
|
|
18
|
+
tensor backend. The C++ double/MPFR backends are source-build options.
|
|
19
|
+
|
|
20
|
+
Here is a symbolic Poisson problem:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
import sympy as sp
|
|
24
|
+
import rbflab as rbf
|
|
25
|
+
|
|
26
|
+
model = rbf.SymbolicScalar(2)
|
|
27
|
+
u = model.field
|
|
28
|
+
x, y = model.coordinates
|
|
29
|
+
exact = sp.sin(sp.pi*x) * sp.sin(sp.pi*y)
|
|
30
|
+
lhs = -model.laplacian(u)
|
|
31
|
+
problem = model.stationary(
|
|
32
|
+
sp.Eq(lhs, lhs.subs(u, exact).doit()),
|
|
33
|
+
boundary=[model.bc("boundary", sp.Eq(u, 0))],
|
|
34
|
+
)
|
|
35
|
+
cloud = rbf.unit_box_grid(5)
|
|
36
|
+
solution = problem.solve(
|
|
37
|
+
cloud, rbf.GlobalCollocation(rbf.IMQ(2), scheme="asymmetric")
|
|
38
|
+
)
|
|
39
|
+
print(solution.evaluate([[0.3, 0.4]]))
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The source repository also contains short heat, Stokes, 3D operator, custom
|
|
43
|
+
kernel, and Re=100 lid-driven cavity examples. The cavity example is a
|
|
44
|
+
showcase with a documented nonzero discrete divergence defect; it does not
|
|
45
|
+
validate a general Navier–Stokes solver. LHI off-node reconstruction has its
|
|
46
|
+
own accuracy limitations, and MPFR local weights do not automatically make a
|
|
47
|
+
global sparse solve extended precision.
|
|
48
|
+
|
|
49
|
+
RBFLAB is licensed under MIT.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# First PyPI release checklist
|
|
2
|
+
|
|
3
|
+
This directory is a fresh public source snapshot. It has no commit history from the
|
|
4
|
+
private research repository and contains no legacy reference code, experiments,
|
|
5
|
+
or historical research outputs. The README media are regenerated by the
|
|
6
|
+
included examples.
|
|
7
|
+
|
|
8
|
+
## Prepared
|
|
9
|
+
|
|
10
|
+
- MIT text in `LICENSE` and SPDX metadata in `pyproject.toml`.
|
|
11
|
+
- Core package, optional C++ source, focused examples/tests, and README media.
|
|
12
|
+
- Separate text-only `README_PYPI.md` keeps local image paths out of PyPI
|
|
13
|
+
metadata while the repository URL is not yet known.
|
|
14
|
+
- `.github/workflows/publish-pypi.yml` builds, tests, checks metadata, and
|
|
15
|
+
publishes a matching non-prerelease GitHub release through PyPI Trusted
|
|
16
|
+
Publishing. It has no API token in the repository.
|
|
17
|
+
- Windows Python 3.12: fresh install, source archive and wheel builds, and
|
|
18
|
+
focused tests passed (60 passed; 29 optional checks skipped).
|
|
19
|
+
- WSL Ubuntu Python 3.12: editable install and focused tests passed (60 passed;
|
|
20
|
+
29 optional checks skipped). Both final distributions passed `twine check`;
|
|
21
|
+
the PyPI README example ran successfully. A fresh Linux wheel install ran
|
|
22
|
+
symbolic Poisson, divergence-free Stokes, and 3D operator examples.
|
|
23
|
+
- The selected source has one Git author in the private working history and no
|
|
24
|
+
embedded third-party license notices; that is evidence, not a legal rights
|
|
25
|
+
determination for any material adapted from older projects.
|
|
26
|
+
- PyPI's `rbflab` JSON endpoint returned HTTP 404 on 2026-10-08. This is not a
|
|
27
|
+
reservation or guarantee of availability.
|
|
28
|
+
- The public repository is `https://github.com/LDBreton/RBFLAB`; package
|
|
29
|
+
metadata points to that URL. The user confirmed MIT release rights for the
|
|
30
|
+
selected code and generated images on 2026-10-08.
|
|
31
|
+
- Its [first GitHub release smoke run](https://github.com/LDBreton/RBFLAB/actions/runs/37827256816)
|
|
32
|
+
passed all six jobs: Python 3.11/3.12
|
|
33
|
+
installed-wheel examples on Windows and Ubuntu, native C++ local operators,
|
|
34
|
+
and gallery generation. The workflows were then updated to current
|
|
35
|
+
Node 24-based actions and a pinned Ubuntu 24.04 runner. The
|
|
36
|
+
[updated six-job run](https://github.com/LDBreton/RBFLAB/actions/runs/37827767996)
|
|
37
|
+
also passed.
|
|
38
|
+
- The GitHub `pypi` environment requires `LDBreton` as a reviewer. A private
|
|
39
|
+
draft `v0.1.0` release contains the wheel and source archive from the
|
|
40
|
+
CI-tested commit.
|
|
41
|
+
|
|
42
|
+
## Before PyPI upload
|
|
43
|
+
|
|
44
|
+
- Confirm the `rbflab` name remains available on PyPI.
|
|
45
|
+
- In PyPI, configure a pending Trusted Publisher for package `rbflab`, owner
|
|
46
|
+
`LDBreton`, repository `RBFLAB`, workflow `publish-pypi.yml`, and environment
|
|
47
|
+
`pypi`.
|
|
48
|
+
- Review the draft release and publish it only after the Trusted Publisher is
|
|
49
|
+
configured. Publishing the GitHub Release triggers the PyPI workflow; approve
|
|
50
|
+
its `pypi` environment job when ready. Then verify the uploaded distributions
|
|
51
|
+
and update the repository README installation text.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# C++ local numerical backend
|
|
2
|
+
|
|
3
|
+
The production LHI local factorization is Eigen `PartialPivLU`. Float64 uses
|
|
4
|
+
`Eigen::Matrix<double,...>`; arbitrary precision uses the existing MPFR `Real`
|
|
5
|
+
wrapper with `Eigen::NumTraits<Real>`. No conversion through double occurs in
|
|
6
|
+
MPFR factorization or triangular solves. Shape policy, kernel assembly,
|
|
7
|
+
symmetric row-max equilibration and reconstruction formulas are unchanged.
|
|
8
|
+
|
|
9
|
+
OpenMP parallelizes stencils. Eigen internal parallelism is disabled. MPFR
|
|
10
|
+
precision is thread-local and set in each worker before allocating matrices.
|
|
11
|
+
A factorization serves all four reconstruction RHS and the optional inverse
|
|
12
|
+
used to estimate the infinity-norm condition number. Exact zero pivots raise
|
|
13
|
+
an error; LU is not a rank-revealing solver and does not regularize ill-conditioned
|
|
14
|
+
matrices. Eigen cannot recover digits lost to an excessively flat Float64 kernel.
|
|
15
|
+
|
|
16
|
+
## Dependencies and build
|
|
17
|
+
|
|
18
|
+
GCC, OpenMP, MPFR/GMP runtime libraries and development headers are required.
|
|
19
|
+
Eigen 3.4.0 was validated. On Ubuntu/WSL, extract the distribution's
|
|
20
|
+
development packages into the ignored project dependency directory:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
bash cpp/prepare_dependencies.sh
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
From the repository root, using its Python environment:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
python -m examples.build_cpp_backend
|
|
30
|
+
RBFLAB_CPP_TESTS=1 python -m pytest tests/test_discrete_operators.py tests/test_symbolic_kernel.py -q
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
On PowerShell set `$env:RBFLAB_CPP_TESTS='1'` before invoking pytest.
|
|
34
|
+
The public `CppBackend(threads=..., compute_condition=...)` API is unchanged.
|
|
35
|
+
`Precision(local_digits=None)` selects Float64; `Precision(local_digits=80)`
|
|
36
|
+
selects MPFR for local assembly/solves. Global precision remains separately
|
|
37
|
+
controlled by `global_dtype`.
|
|
38
|
+
|
|
39
|
+
Custom symbolic Stokes kernels compile against the same Eigen solver.
|
|
40
|
+
The adapter and Eigen header content hashes participate in cache identity;
|
|
41
|
+
previous custom binaries cannot be reused accidentally. Build manifests record
|
|
42
|
+
the Eigen header hash. Dependencies and generated executables remain untracked.
|
|
43
|
+
Off-node lazy Python reconstruction and global sparse solvers are unchanged.
|
|
44
|
+
|
|
45
|
+
## Optional Float64 SVD
|
|
46
|
+
|
|
47
|
+
`CppBackend(local_solver="svd", svd_rcond=None)` selects Eigen JacobiSVD.
|
|
48
|
+
`svd_rcond` is the relative singular-value cutoff; `None` uses Eigen's default
|
|
49
|
+
(matrix dimension times machine epsilon). Set an explicit value such as
|
|
50
|
+
`1e-12` when testing regularization. MPFR currently supports LU only.
|
|
51
|
+
SVD solves the equilibrated system with a truncated pseudoinverse; this can
|
|
52
|
+
change the discretization substantially. It is not an automatic fallback.
|
|
53
|
+
|
|
54
|
+
Diagnostics include each stencil's retained rank, dimension, relative cutoff,
|
|
55
|
+
and condition number from the untruncated computed singular values. If condition
|
|
56
|
+
reporting is enabled, SVD reports the 2-norm condition, whereas LU reports the
|
|
57
|
+
infinity-norm estimate. Tiny singular values of an unresolved Float64 matrix
|
|
58
|
+
cannot certify its mathematical condition number. Lazy off-node reconstruction
|
|
59
|
+
uses NumPy SVD with the same scaling and cutoff; kernel evaluation/rounding may
|
|
60
|
+
differ from C++, as in the existing Python reconstruction path.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Experimental Float64 local Stokes assembly + cuBLAS batched pivoted LU.
|
|
2
|
+
#include <cuda_runtime.h>
|
|
3
|
+
#include <cublas_v2.h>
|
|
4
|
+
#include <vector>
|
|
5
|
+
#include <map>
|
|
6
|
+
#include <fstream>
|
|
7
|
+
#include <iostream>
|
|
8
|
+
#include <iomanip>
|
|
9
|
+
#include <cmath>
|
|
10
|
+
#include <chrono>
|
|
11
|
+
#include <stdexcept>
|
|
12
|
+
#include <algorithm>
|
|
13
|
+
#include <string>
|
|
14
|
+
// RBFLAB_GENERATED_SPACE
|
|
15
|
+
void check(cudaError_t e){if(e!=cudaSuccess)throw std::runtime_error(cudaGetErrorString(e));}
|
|
16
|
+
void blas(cublasStatus_t e){if(e!=CUBLAS_STATUS_SUCCESS)throw std::runtime_error("cuBLAS status "+std::to_string(e));}
|
|
17
|
+
using Clock=std::chrono::steady_clock;
|
|
18
|
+
double elapsed(Clock::time_point t){return std::chrono::duration<double>(Clock::now()-t).count();}
|
|
19
|
+
struct Node{int op;double x,y;};
|
|
20
|
+
struct Task{double mu,x,y;std::vector<double> v,p;std::vector<Node> nodes;};
|
|
21
|
+
template<class T>struct Device{
|
|
22
|
+
T* p=nullptr;size_t count;
|
|
23
|
+
explicit Device(size_t n):count(n){check(cudaMalloc((void**)&p,sizeof(T)*n));}
|
|
24
|
+
~Device(){if(p)cudaFree(p);}
|
|
25
|
+
Device(const Device&)=delete;Device& operator=(const Device&)=delete;
|
|
26
|
+
void upload(const T* src){check(cudaMemcpy(p,src,count*sizeof(T),cudaMemcpyHostToDevice));}
|
|
27
|
+
void download(T* dst){check(cudaMemcpy(dst,p,count*sizeof(T),cudaMemcpyDeviceToHost));}
|
|
28
|
+
};
|
|
29
|
+
__global__ void assemble(int n,int count,int nv,int np,const Node* nodes,const double* params,const double* meta,double* G,double* B){
|
|
30
|
+
int total=count*n*(n+4);
|
|
31
|
+
for(int index=blockIdx.x*blockDim.x+threadIdx.x;index<total;index+=blockDim.x*gridDim.x){
|
|
32
|
+
int t=index/(n*(n+4)),entry=index%(n*(n+4));
|
|
33
|
+
KernelSpec v{params+t*(nv+np)},p{params+t*(nv+np)+nv};const Node* pts=nodes+t*n;
|
|
34
|
+
if(entry<n*n){int row=entry%n,col=entry/n;if(row<col)continue;
|
|
35
|
+
double value=custom_eval(pts[col].op,pts[row].op,pts[row].x-pts[col].x,pts[row].y-pts[col].y,meta[3*t],v,p);
|
|
36
|
+
G[t*n*n+col*n+row]=value;G[t*n*n+row*n+col]=value;
|
|
37
|
+
}else{int j=entry-n*n,row=j%n,target=j/n;
|
|
38
|
+
B[t*n*4+target*n+row]=custom_eval(pts[row].op,target+5,meta[3*t+1]-pts[row].x,meta[3*t+2]-pts[row].y,meta[3*t],v,p);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
__global__ void scales(int n,int count,const double* G,double* scale){
|
|
43
|
+
for(int i=blockIdx.x*blockDim.x+threadIdx.x;i<count*n;i+=gridDim.x*blockDim.x){int t=i/n,row=i%n;double mx=0;for(int j=0;j<n;j++)mx=fmax(mx,fabs(G[t*n*n+j*n+row]));scale[i]=mx>0?1/sqrt(mx):nan("");}
|
|
44
|
+
}
|
|
45
|
+
__global__ void equilibrate(int n,int count,const double* G,const double* B,const double* scale,double* A,double* R){
|
|
46
|
+
for(int i=blockIdx.x*blockDim.x+threadIdx.x;i<count*n*(n+4);i+=gridDim.x*blockDim.x){int t=i/(n*(n+4)),j=i%(n*(n+4));if(j<n*n)A[t*n*n+j]=G[t*n*n+j]*scale[t*n+j%n]*scale[t*n+j/n];else{j-=n*n;R[t*n*4+j]=B[t*n*4+j]*scale[t*n+j%n];}}
|
|
47
|
+
}
|
|
48
|
+
__global__ void unscale(int n,int count,const double* scale,double* W){for(int i=blockIdx.x*blockDim.x+threadIdx.x;i<count*n*4;i+=gridDim.x*blockDim.x){int t=i/(n*4);W[i]*=scale[t*n+i%n];}}
|
|
49
|
+
__global__ void residuals(int n,int count,const double* G,const double* B,const double* W,double* error,double* denom){
|
|
50
|
+
for(int i=blockIdx.x*blockDim.x+threadIdx.x;i<count*n*4;i+=gridDim.x*blockDim.x){int t=i/(n*4),j=i%(n*4),row=j%n,k=j/n;double d=-B[i];for(int col=0;col<n;col++)d+=G[t*n*n+col*n+row]*W[t*n*4+k*n+col];double a=isfinite(d)?fabs(d):INFINITY;atomicMax((unsigned long long*)(error+t),(unsigned long long)__double_as_longlong(a));atomicMax((unsigned long long*)(denom+t),(unsigned long long)__double_as_longlong(fabs(B[i])));}
|
|
51
|
+
}
|
|
52
|
+
int main(int argc,char**argv){try{
|
|
53
|
+
if(argc!=3)throw std::runtime_error("Usage: cuda_lhi INPUT OUTPUT");
|
|
54
|
+
std::ifstream in(argv[1]);int count,chunk,nv,np;if(!(in>>count>>chunk>>nv>>np)||count<1||chunk<1||nv<0||np<0)throw std::runtime_error("Invalid header");
|
|
55
|
+
std::vector<Task> tasks(count);std::map<int,std::vector<int>> groups;
|
|
56
|
+
for(int i=0;i<count;i++){auto&t=tasks[i];int n;in>>n>>t.mu>>t.x>>t.y;if(n<1||n>4096)throw std::runtime_error("Invalid matrix size");t.v.resize(nv);t.p.resize(np);for(auto&v:t.v)in>>v;for(auto&v:t.p)in>>v;t.nodes.resize(n);for(auto&v:t.nodes)in>>v.op>>v.x>>v.y;if(!in)throw std::runtime_error("Invalid task input");groups[n].push_back(i);}
|
|
57
|
+
auto initial=Clock::now();check(cudaFree(0));cudaDeviceProp prop;check(cudaGetDeviceProperties(&prop,0));cublasHandle_t handle;blas(cublasCreate(&handle));double init=elapsed(initial);
|
|
58
|
+
std::vector<std::vector<double>> weights(count);std::vector<double> residual(count);double h2d=0,assembly=0,solve=0,d2h=0;int batches=0;
|
|
59
|
+
auto begin=Clock::now();
|
|
60
|
+
for(auto&group:groups){int n=group.first;auto&ids=group.second;
|
|
61
|
+
for(size_t offset=0;offset<ids.size();offset+=chunk){int b=std::min(size_t(chunk),ids.size()-offset);batches++;
|
|
62
|
+
std::vector<Node> nodes;std::vector<double> params,meta;
|
|
63
|
+
for(int j=0;j<b;j++){auto&t=tasks[ids[offset+j]];nodes.insert(nodes.end(),t.nodes.begin(),t.nodes.end());params.insert(params.end(),t.v.begin(),t.v.end());params.insert(params.end(),t.p.begin(),t.p.end());meta.insert(meta.end(),{t.mu,t.x,t.y});}
|
|
64
|
+
Device<Node> dn(b*n);Device<double> dp(std::max(1,b*(nv+np))),dm(b*3),G(size_t(b)*n*n),A(size_t(b)*n*n),B(b*n*4),W(b*n*4),scale(b*n),error(b),denom(b);Device<int> piv(b*n),info(b);Device<double*> aa(b),ww(b);
|
|
65
|
+
std::vector<double*> ap(b),wp(b);for(int j=0;j<b;j++){ap[j]=A.p+size_t(j)*n*n;wp[j]=W.p+j*n*4;}
|
|
66
|
+
auto t=Clock::now();dn.upload(nodes.data());if(!params.empty())check(cudaMemcpy(dp.p,params.data(),params.size()*sizeof(double),cudaMemcpyHostToDevice));dm.upload(meta.data());aa.upload(ap.data());ww.upload(wp.data());check(cudaMemset(error.p,0,b*sizeof(double)));check(cudaMemset(denom.p,0,b*sizeof(double)));h2d+=elapsed(t);
|
|
67
|
+
int blocks=std::min(4096,(b*n*(n+4)+127)/128);t=Clock::now();
|
|
68
|
+
assemble<<<blocks,128>>>(n,b,nv,np,dn.p,dp.p,dm.p,G.p,B.p);scales<<<blocks,128>>>(n,b,G.p,scale.p);equilibrate<<<blocks,128>>>(n,b,G.p,B.p,scale.p,A.p,W.p);check(cudaGetLastError());check(cudaDeviceSynchronize());assembly+=elapsed(t);
|
|
69
|
+
t=Clock::now();blas(cublasDgetrfBatched(handle,n,aa.p,n,piv.p,info.p,b));std::vector<int> status(b);info.download(status.data());for(int j=0;j<b;j++)if(status[j])throw std::runtime_error("LU failure at stencil "+std::to_string(ids[offset+j])+", info="+std::to_string(status[j]));int result=0;blas(cublasDgetrsBatched(handle,CUBLAS_OP_N,n,4,(const double**)aa.p,n,piv.p,ww.p,n,&result,b));if(result)throw std::runtime_error("Batched solve failed");unscale<<<blocks,128>>>(n,b,scale.p,W.p);residuals<<<blocks,128>>>(n,b,G.p,B.p,W.p,error.p,denom.p);check(cudaGetLastError());check(cudaDeviceSynchronize());solve+=elapsed(t);
|
|
70
|
+
t=Clock::now();std::vector<double> host(b*n*4),err(b),norm(b);W.download(host.data());error.download(err.data());denom.download(norm.data());d2h+=elapsed(t);
|
|
71
|
+
for(int j=0;j<b;j++){int id=ids[offset+j];weights[id].assign(host.begin()+j*n*4,host.begin()+(j+1)*n*4);residual[id]=err[j]/(norm[j]?norm[j]:1);if(!std::isfinite(residual[id]))throw std::runtime_error("Nonfinite residual");for(double v:weights[id])if(!std::isfinite(v))throw std::runtime_error("Nonfinite weight");}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
double compute=elapsed(begin);blas(cublasDestroy(handle));
|
|
75
|
+
std::ofstream out(argv[2]);out<<std::setprecision(17);for(int i=0;i<count;i++){out<<i<<" "<<tasks[i].nodes.size()<<" none "<<residual[i];for(double v:weights[i])out<<" "<<v;out<<"\n";}if(!out)throw std::runtime_error("Output write failed");
|
|
76
|
+
std::cout<<std::setprecision(9)<<"{\"device\":\""<<prop.name<<"\",\"context_seconds\":"<<init<<",\"compute_seconds\":"<<compute<<",\"h2d_seconds\":"<<h2d<<",\"assembly_seconds\":"<<assembly<<",\"solve_seconds\":"<<solve<<",\"d2h_seconds\":"<<d2h<<",\"groups\":"<<groups.size()<<",\"batches\":"<<batches<<"}\n";
|
|
77
|
+
return 0;
|
|
78
|
+
}catch(const std::exception&e){std::cerr<<e.what()<<"\n";return 1;}}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
// Keep parallelism across stencils; Eigen must not create nested workers.
|
|
3
|
+
#define EIGEN_DONT_PARALLELIZE
|
|
4
|
+
#include "real.hpp"
|
|
5
|
+
#ifndef RBFLAB_DOUBLE
|
|
6
|
+
inline bool operator!=(const Real&a,const Real&b){return !(a==b);}
|
|
7
|
+
inline bool operator<=(const Real&a,const Real&b){return !(a>b);}
|
|
8
|
+
inline bool operator>=(const Real&a,const Real&b){return !(a<b);}
|
|
9
|
+
inline Real abs2(const Real&a){return a*a;}
|
|
10
|
+
inline Real conj(const Real&a){return a;}
|
|
11
|
+
inline Real real(const Real&a){return a;}
|
|
12
|
+
inline Real imag(const Real&){return Real(0);}
|
|
13
|
+
#endif
|
|
14
|
+
#include <Eigen/Core>
|
|
15
|
+
#ifndef RBFLAB_DOUBLE
|
|
16
|
+
namespace Eigen {
|
|
17
|
+
template<> struct NumTraits<::Real>:GenericNumTraits<::Real> {
|
|
18
|
+
using Real=::Real; using NonInteger=::Real; using Nested=::Real; using Literal=::Real;
|
|
19
|
+
enum {IsComplex=0,IsInteger=0,IsSigned=1,RequireInitialization=1,ReadCost=10,AddCost=20,MulCost=40};
|
|
20
|
+
static Real epsilon(){Real x(1);mpfr_div_2si(x.v,x.v,::Real::bits-1,MPFR_RNDN);return x;}
|
|
21
|
+
static Real dummy_precision(){return sqrt(epsilon());}
|
|
22
|
+
static int digits10(){return int((::Real::bits-1)*0.3010299956639812);}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
#endif
|
|
26
|
+
#include <Eigen/LU>
|
|
27
|
+
#include <vector>
|
|
28
|
+
#ifdef RBFLAB_DOUBLE
|
|
29
|
+
using EigenScalar=double;
|
|
30
|
+
inline double eigen_value(const Real&x){return x.v;}
|
|
31
|
+
#else
|
|
32
|
+
using EigenScalar=Real;
|
|
33
|
+
inline const Real& eigen_value(const Real&x){return x;}
|
|
34
|
+
#endif
|
|
35
|
+
// Own the factorization once and reuse it for all reconstruction/diagnostic RHS.
|
|
36
|
+
struct EigenLocalLU {
|
|
37
|
+
using Dense=Eigen::Matrix<EigenScalar,Eigen::Dynamic,Eigen::Dynamic>;
|
|
38
|
+
using Rows=std::vector<std::vector<Real>>;
|
|
39
|
+
Eigen::PartialPivLU<Dense> factor;
|
|
40
|
+
static Dense dense(const Rows& rows){
|
|
41
|
+
Dense a(rows.size(),rows.at(0).size());
|
|
42
|
+
for(int i=0;i<a.rows();++i)for(int j=0;j<a.cols();++j)a(i,j)=eigen_value(rows[i][j]);
|
|
43
|
+
return a;
|
|
44
|
+
}
|
|
45
|
+
explicit EigenLocalLU(const Rows& a):factor(dense(a)){
|
|
46
|
+
for(int i=0;i<factor.matrixLU().rows();++i)
|
|
47
|
+
if(factor.matrixLU()(i,i)==EigenScalar(0))throw std::runtime_error("Singular local matrix at selected precision");
|
|
48
|
+
}
|
|
49
|
+
Rows solve(const Rows& b)const{
|
|
50
|
+
Dense x=factor.solve(dense(b));
|
|
51
|
+
Rows result(x.rows(),std::vector<Real>(x.cols()));
|
|
52
|
+
for(int i=0;i<x.rows();++i)for(int j=0;j<x.cols();++j)result[i][j]=Real(x(i,j));
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
#include <memory>
|
|
58
|
+
#ifdef RBFLAB_DOUBLE
|
|
59
|
+
#include <Eigen/SVD>
|
|
60
|
+
#endif
|
|
61
|
+
struct EigenLocalSolver {
|
|
62
|
+
using Rows=EigenLocalLU::Rows;
|
|
63
|
+
std::unique_ptr<EigenLocalLU> lu;
|
|
64
|
+
#ifdef RBFLAB_DOUBLE
|
|
65
|
+
Eigen::JacobiSVD<EigenLocalLU::Dense> svd;
|
|
66
|
+
#endif
|
|
67
|
+
int size,rank; double threshold=0,condition2=0;
|
|
68
|
+
EigenLocalSolver(const Rows& a,const std::string& solver,double cutoff):size(a.size()),rank(size){
|
|
69
|
+
if(solver=="lu"){lu=std::make_unique<EigenLocalLU>(a);return;}
|
|
70
|
+
#ifdef RBFLAB_DOUBLE
|
|
71
|
+
svd.compute(EigenLocalLU::dense(a),Eigen::ComputeThinU|Eigen::ComputeThinV);
|
|
72
|
+
if(svd.info()!=Eigen::Success)throw std::runtime_error("Local SVD failed");
|
|
73
|
+
if(cutoff>=0)svd.setThreshold(cutoff);
|
|
74
|
+
threshold=svd.threshold();rank=svd.rank();
|
|
75
|
+
const auto& values=svd.singularValues();
|
|
76
|
+
condition2=values(size-1)>0 ? values(0)/values(size-1) : std::numeric_limits<double>::infinity();
|
|
77
|
+
#else
|
|
78
|
+
throw std::runtime_error("SVD local solver currently requires Float64");
|
|
79
|
+
#endif
|
|
80
|
+
}
|
|
81
|
+
Rows solve(const Rows& b)const{
|
|
82
|
+
if(lu)return lu->solve(b);
|
|
83
|
+
#ifdef RBFLAB_DOUBLE
|
|
84
|
+
EigenLocalLU::Dense x=svd.solve(EigenLocalLU::dense(b));
|
|
85
|
+
Rows result(x.rows(),std::vector<Real>(x.cols()));
|
|
86
|
+
for(int i=0;i<x.rows();++i)for(int j=0;j<x.cols();++j)result[i][j]=Real(x(i,j));
|
|
87
|
+
return result;
|
|
88
|
+
#else
|
|
89
|
+
throw std::runtime_error("SVD local solver currently requires Float64");
|
|
90
|
+
#endif
|
|
91
|
+
}
|
|
92
|
+
};
|