specnn4pde 0.0.1__py3-none-any.whl

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.
specnn4pde/__init__.py ADDED
File without changes
specnn4pde/linalg.py ADDED
@@ -0,0 +1,57 @@
1
+ __all__ = ['ROU_cholesky',
2
+ ]
3
+
4
+ import numpy as np
5
+
6
+ def ROU_cholesky(L, v, alpha=1, beta=1):
7
+ """
8
+ Perform a rank-one update of the Cholesky decomposition of a matrix.
9
+
10
+ Parameters
11
+ ----------
12
+ L : ndarray
13
+ The lower triangular Cholesky factor of the matrix A.
14
+ alpha : float
15
+ The scalar multiplier for the matrix. Must be non-negative.
16
+ beta : float
17
+ The scalar multiplier for the outer product of v. Must be non-negative.
18
+ v : ndarray
19
+ The vector used for the rank-one update.
20
+
21
+ Returns
22
+ ----------
23
+ L_prime : ndarray
24
+ The updated lower triangular Cholesky factor of the matrix
25
+ \tilde{A} = alpha * A + beta * v * v^T.
26
+
27
+ References
28
+ ----------
29
+ 1. https://en.wikipedia.org/wiki/Cholesky_decomposition#Rank-one_update
30
+ 2. Krause Oswin, Igel ChristianA, 2015,
31
+ More Efficient Rank-one Covariance Matrix Update for Evolution Strategies,
32
+ https://christian-igel.github.io/paper/AMERCMAUfES.pdf
33
+
34
+ Example
35
+ ----------
36
+ >>> L = np.array([[1, 0, 0], [2, 1, 0], [3, 2, 1]])
37
+ >>> alpha = 2
38
+ >>> beta = 3
39
+ >>> v = np.array([1, 2, 3])
40
+ >>> L_prime = ROU_cholesky(L, v, alpha, beta)
41
+ >>> print(L_prime)
42
+ """
43
+
44
+ if alpha < 0 or beta < 0:
45
+ raise ValueError("alpha and beta must be non-negative")
46
+
47
+ n = L.shape[0]
48
+ L, x = np.sqrt(alpha) * L, np.sqrt(beta) * v
49
+ for k in range(n):
50
+ r = np.sqrt(L[k, k]**2 + x[k]**2)
51
+ c = r / L[k, k]
52
+ s = x[k] / L[k, k]
53
+ L[k, k] = r
54
+ if k < n - 1:
55
+ L[(k+1):n, k] = (L[(k+1):n, k] + s * x[(k+1):n]) / c
56
+ x[(k+1):n] = c * x[(k+1):n] - s * L[(k+1):n, k]
57
+ return L
specnn4pde/npde.py ADDED
@@ -0,0 +1,338 @@
1
+ __all__ = ['gradients', 'Jacobian', 'partial_derivative', 'partial_derivative_vector',
2
+ 'meshgrid_to_matrix', 'gen_collo',
3
+ ]
4
+
5
+ import torch
6
+ from torch.autograd.functional import jacobian
7
+
8
+
9
+ def gradients(u, x, order=1, retain_graph=False):
10
+ """
11
+ Compute the gradients for d dimensional function. It only supports two kinds of functions:
12
+
13
+ 1. scalar function f(x1, x2, ..., xd)
14
+ The first order gradients are [df/dx1, df/dx2, ..., df/dxd]
15
+ 2. vector function like F(x1,x2,...,xd) = [f1(x1), f2(x2), ..., fd(xd)]
16
+ The first order gradients are [df1/dx1, df2/dx2, ..., dfn/dxd]
17
+
18
+ Higher order gradients are also supported.
19
+
20
+ !!! For functions like F(x1, ..., xd) = [f1(x1, ..., xd), ..., fd(x1, ..., xd)],
21
+ use `partial_derivative_vector` instead.
22
+
23
+ Parameters
24
+ ----------
25
+ u : tensor
26
+ The values of the function at the point x.
27
+ x : Tensor, shape (n, d)
28
+ The point at which to compute the gradients, where n is the number of points and d is the dimension.
29
+ order : int, optional
30
+ The order of the gradients. The default is 1.
31
+ retain_graph : bool, optional
32
+ Whether to retain the computational graph for further computation. Defaults to False.
33
+
34
+ Returns
35
+ ----------
36
+ grads: list of tensors of shape like x
37
+ The gradients up to the order. grads[i] is the (i+1)-th order gradients.
38
+
39
+ Example
40
+ ----------
41
+ >>> def f(x):
42
+ ... return x**2
43
+ >>> x = torch.tensor([[1.0, 2], [3, 4], [5, 6]], requires_grad=True)
44
+ >>> u = f(x)
45
+ >>> gradients(u, x, 2)
46
+ [tensor([[ 2., 4.],
47
+ [ 6., 8.],
48
+ [10., 12.]]),
49
+ tensor([[2., 2.],
50
+ [2., 2.],
51
+ [2., 2.]])]
52
+ """
53
+
54
+ grads = [torch.autograd.grad(u, x, grad_outputs=torch.ones_like(u), create_graph=True)[0]]
55
+ for _ in range(1, order):
56
+ grads.append(torch.autograd.grad(grads[-1], x, grad_outputs=torch.ones_like(grads[-1]), create_graph=True)[0])
57
+ # clean the computational graph of the gradients to save memory
58
+ if not retain_graph:
59
+ grads = [g.detach() for g in grads]
60
+ return grads
61
+
62
+
63
+ def Jacobian(f, x, order=1, create_graph=False):
64
+ """
65
+ Compute the Jacobian of a vector function.
66
+ But only support univariate function. For multivariate vector function,
67
+ use `partial_derivative_vector` instead.
68
+
69
+ !!! Not suitale for high order derivatives, because
70
+ creating computational graphs will consume a lot of time and memory.
71
+
72
+ Parameters
73
+ ----------
74
+ f : function
75
+ The function to compute the Jacobian for.
76
+ x : Tensor
77
+ The point at which to compute the Jacobian.
78
+ order : int, optional
79
+ The order of the derivative. Defaults to 1.
80
+ create_graph : bool, optional
81
+ Whether to create a computational graph. Defaults to False.
82
+
83
+ Returns
84
+ ----------
85
+ Tensor
86
+ The Jacobian of the function at the given point.
87
+
88
+ Example
89
+ ----------
90
+ >>> def f(x):
91
+ ... return torch.cat([x, x**2], dim=1)
92
+ >>> x = torch.tensor([[1.0], [2], [3]])
93
+ >>> Jacobian(f, x)
94
+ tensor([[1., 2.],
95
+ [1., 4.],
96
+ [1., 6.]])
97
+ """
98
+
99
+ def _f(*args, **kwargs):
100
+ return f(*args, **kwargs).sum(dim=0)
101
+ if order == 0:
102
+ return f(x) if create_graph else f(x).detach()
103
+ elif order == 1:
104
+ return jacobian(_f, x, create_graph).squeeze(2).T
105
+ else:
106
+ def _jacobian(x):
107
+ return jacobian(_f, x, True).squeeze(2).T
108
+ return Jacobian(_jacobian, x, order-1, create_graph)
109
+
110
+ def partial_derivative(F, X, Alpha, create_graph=False):
111
+ """
112
+ Compute the partial derivative for vector-valued function but inefficient,
113
+ use `partial_derivative_vector` instead.
114
+
115
+ !!! Not suitale for high order derivatives, because
116
+ creating computational graphs will consume a lot of time and memory.
117
+
118
+ Parameters
119
+ ----------
120
+ F : function
121
+ The function to compute the partial derivative for.
122
+ X : Tensor
123
+ The points at which to compute the partial derivative.
124
+ Alpha : list
125
+ The order of the derivative for each dimension.
126
+ create_graph : bool, optional
127
+ Whether to create a computational graph. Defaults to False.
128
+
129
+ Returns
130
+ ----------
131
+ Tensor
132
+ The partial derivative of the function at the given points.
133
+ """
134
+
135
+ if len(Alpha) == 1:
136
+ return Jacobian(F, X, Alpha[0], create_graph)
137
+ else:
138
+ X_perfix, x_last = X[:, :-1], X[:, -1:]
139
+ def _f(x):
140
+ def _F(X):
141
+ return F(torch.cat([X, x], dim=1))
142
+ return partial_derivative(_F, X_perfix, Alpha[:-1], True)
143
+ return Jacobian(_f, x_last, Alpha[-1], create_graph)
144
+
145
+ def partial_derivative_vector(F, X, Alpha, create_graph=False, batch_size=[15000,1]):
146
+ """
147
+ Compute the partial derivatives for vector-valued function
148
+ F(x1, x2, ..., xn) = [f1(x1, x2, ..., xn), f2(x1, x2, ..., xn), ..., fk(x1, x2, ..., xn)],
149
+ return [\partial^\alpha f1, \partial^\alpha f2, ..., \partial^\alpha fn].
150
+
151
+ !!! Not suitale for high order derivatives, because
152
+ creating computational graphs will consume a lot of time and memory.
153
+
154
+ Parameters
155
+ ----------
156
+ F : function
157
+ The function to compute the partial derivatives for.
158
+ X : tensor, shape (N, d)
159
+ The points at which to compute the partial derivatives, where N is the number of points and d is the dimension.
160
+ Alpha : list
161
+ The order of the derivative for each dimension.
162
+ create_graph : bool, optional
163
+ Whether to create a computational graph. Defaults to False.
164
+ batch_size : list, optional
165
+ The batch size for computing the partial derivatives.
166
+ batch_size[0] is the number of points in each batch,
167
+ and batch_size[1] is the number of functions in each batch.
168
+ Defaults to [15000, 1].
169
+
170
+ Returns
171
+ ----------
172
+ Tensor, shape (k, N, d)
173
+ The partial derivatives of the function at the given points.
174
+
175
+ Example
176
+ ----------
177
+ >>> def F(X):
178
+ ... return X.prod(dim=1).unsqueeze(dim=1).repeat(1, 2)
179
+ >>> X = torch.tensor([[1.0, 2], [3, 4]])
180
+ >>> partial_derivative_vector(F, X, Alpha = [1, 1])
181
+ tensor([[1., 1.],
182
+ [1., 1.]])
183
+ """
184
+
185
+ out_dim = F(X[:1]).shape[1]
186
+ res = []
187
+ for i in range(0, X.shape[0], batch_size[0]):
188
+ res_sub = []
189
+ for j in range(0, out_dim, batch_size[1]):
190
+ def _F(X): return F(X)[:, j:j+batch_size[1]]
191
+ res_sub.append(partial_derivative(_F, X[i:i+batch_size[0]], Alpha, create_graph))
192
+ res.append(torch.cat(res_sub, dim=1))
193
+ return torch.cat(res, dim=0)
194
+
195
+
196
+ def meshgrid_to_matrix(inputs, indexing='xy'):
197
+ """
198
+ Convert the meshgrid to matrix.
199
+
200
+ Parameters
201
+ ----------
202
+ inputs : list of iterables, length d
203
+ The grid points in each dimension.
204
+ indexing : str, optional
205
+ The indexing of the meshgrid. The default is 'xy'.
206
+ The options are 'xy' and 'ij', the same as numpy.meshgrid and torch.meshgrid.
207
+
208
+ Returns
209
+ ----------
210
+ tensor, shape ( n1*n2*...*nd, d)
211
+ The matrix of the grid points, ni is the number of grid points in the i-th dimension.
212
+
213
+ Example
214
+ ----------
215
+ >>> x = torch.linspace(1, 2, 3)
216
+ >>> y = torch.linspace(4, 5, 3)
217
+ >>> meshgrid_to_matrix([x, y], indexing='xy')
218
+ tensor([[1.0000, 4.0000],
219
+ [1.5000, 4.0000],
220
+ [2.0000, 4.0000],
221
+ [1.0000, 4.5000],
222
+ [1.5000, 4.5000],
223
+ [2.0000, 4.5000],
224
+ [1.0000, 5.0000],
225
+ [1.5000, 5.0000],
226
+ [2.0000, 5.0000]])
227
+ >>> meshgrid_to_matrix([x, y], indexing='ij')
228
+ tensor([[1.0000, 4.0000],
229
+ [1.0000, 4.5000],
230
+ [1.0000, 5.0000],
231
+ [1.5000, 4.0000],
232
+ [1.5000, 4.5000],
233
+ [1.5000, 5.0000],
234
+ [2.0000, 4.0000],
235
+ [2.0000, 4.5000],
236
+ [2.0000, 5.0000]])
237
+ """
238
+
239
+ Co = torch.meshgrid(*inputs, indexing=indexing)
240
+ return torch.cat([c.reshape(-1,1) for c in Co], dim=1)
241
+
242
+
243
+ def gen_collo(Domain, grids, temporal = False, corner = True):
244
+ """
245
+ Generate the collocation points for the PDE problem on regular domain.
246
+ If Domain and grids are provided, the uniform grids will be generated automatically as G.
247
+ If Domain and grids are not provided, G should be provided.
248
+
249
+ Parameters
250
+ ----------
251
+ Domain : list of list, optional
252
+ The domain of the problem. eg. [[t_min, x1_min, x2_min, ...], [t_max, x1_max, x2_max, ...]]
253
+ grids : list, optional
254
+ The number of collocations in each dimension. eg. [N_t, N_x1, N_x2, ...]
255
+ temporal : bool, optional
256
+ If the problem is temporal. The default is False.
257
+ corner : bool, optional
258
+ If the collocation points include the corner points. The default is True.
259
+ G : list of tensor, optional
260
+ The tensors in the list are the collocation points in each dimension.
261
+ If Domain and grids are not provided, G should be provided.
262
+
263
+ Returns
264
+ -------
265
+ collo_rs : tensor
266
+ The collocation points in the interior of the domain.
267
+ collo_ic : tensor, optional
268
+ If temporal is set as True. The collocation points on the initial condition.
269
+ collo_bc : tensor
270
+ The collocation points on the boundary condition.
271
+
272
+ Example
273
+ ----------
274
+ >>> domian = [[0, 0, 1], [2, 3, 4]]
275
+ >>> grids = [3, 4, 5]
276
+ >>> gen_collo(domian, grids)
277
+ (tensor([[1.0000, 1.0000, 1.7500],
278
+ [1.0000, 1.0000, 2.5000],
279
+ [1.0000, 1.0000, 3.2500],
280
+ [1.0000, 2.0000, 1.7500],
281
+ [1.0000, 2.0000, 2.5000],
282
+ [1.0000, 2.0000, 3.2500]]),
283
+ tensor([[0.0000, 0.0000, 1.0000],
284
+ [0.0000, 0.0000, 1.7500],
285
+ [0.0000, 0.0000, 2.5000],
286
+ [0.0000, 0.0000, 3.2500],
287
+ [0.0000, 0.0000, 4.0000],
288
+ [2.0000, 0.0000, 1.0000],
289
+ ......
290
+ [1.0000, 1.0000, 4.0000],
291
+ [1.0000, 2.0000, 1.0000],
292
+ [1.0000, 2.0000, 4.0000]]))
293
+ """
294
+
295
+ if G is None:
296
+ dim = len(Domain[0])
297
+ if len(grids) != dim:
298
+ if len(grids) == 1:
299
+ Warning("The number of grids is set as the same for all dimensions.")
300
+ grids = grids * dim
301
+ else:
302
+ raise ValueError("The length of grids should be equal to the dimension of the domain.")
303
+ G = [torch.linspace(l, r, n) for l, r, n in zip(*(Domain + [grids]))]
304
+ dim = len(G)
305
+ if temporal:
306
+ G_rs = [G[0][1:]] + [G[i][1:-1] for i in range(1, dim)]
307
+ G_ic = [G[0][0]] + G[1:]
308
+ collo_rs = meshgrid_to_matrix(G_rs)
309
+ collo_ic = meshgrid_to_matrix(G_ic)
310
+ collo_bc = []
311
+ for i in range(1, dim):
312
+ G_bc = [G[0]]
313
+ for j in range(1, dim):
314
+ if j < i:
315
+ G_bc.append(G[j][1:-1])
316
+ elif j == i:
317
+ G_bc.append(G[j][[0,-1]])
318
+ else:
319
+ G_bc.append(G[j] if corner else G[j][1:-1])
320
+ collo_bc.append(meshgrid_to_matrix(G_bc))
321
+ collo_bc = torch.cat(collo_bc, dim=0)
322
+ return collo_rs, collo_ic, collo_bc
323
+ else:
324
+ G_rs = [G[i][1:-1] for i in range(dim)]
325
+ collo_rs = meshgrid_to_matrix(G_rs)
326
+ collo_bc = []
327
+ for i in range(dim):
328
+ G_bc = []
329
+ for j in range(dim):
330
+ if j < i:
331
+ G_bc.append(G[j][1:-1])
332
+ elif j == i:
333
+ G_bc.append(G[j][[0,-1]])
334
+ else:
335
+ G_bc.append(G[j] if corner else G[j][1:-1])
336
+ collo_bc.append(meshgrid_to_matrix(G_bc))
337
+ collo_bc = torch.cat(collo_bc, dim=0)
338
+ return collo_rs, collo_bc
specnn4pde/spectral.py ADDED
@@ -0,0 +1,464 @@
1
+ __all__ = ['JacobiP', 'Jacobi_Gauss', 'Jacobi_Gauss_Lobatto',
2
+ 'HermiteP', 'HermiteF', 'Hermite_Gauss', 'mapped_Jacobi_Gauss',
3
+ 'glue1D', 'glue_pts_1D',
4
+ ]
5
+
6
+ """
7
+ spectral.py
8
+
9
+ This module provides functions for working with spectral methods.
10
+ The implementation is mainly based on the book:
11
+ Shen, J., Tang, T. & Wang, L.-L. Spectral Methods: Algorithms,
12
+ Analysis and Applications. vol. 41 (Springer Science & Business
13
+ Media, 2011).
14
+ https://link.springer.com/book/10.1007/978-3-540-71041-7
15
+ """
16
+
17
+ import numpy as np
18
+ from scipy.special import roots_hermite, gamma
19
+ from scipy.sparse import diags, eye, lil_matrix, csr_matrix
20
+ from scipy.linalg import eigh, block_diag
21
+ from sympy import symbols, sqrt, atanh, tanh, sinh, log, lambdify, diff
22
+
23
+ def JacobiP(x, alpha, beta, N):
24
+ """
25
+ This function evaluates the orthonormal Jacobi polynomial of order
26
+ up to N with parameters alpha and beta at points x.
27
+
28
+ Parameters
29
+ ----------
30
+ x : array
31
+ Points at which the Jacobi polynomial is to be computed.
32
+ alpha : float
33
+ The alpha parameter of the Jacobi polynomial. Must be greater than -1.
34
+ beta : float
35
+ The beta parameter of the Jacobi polynomial. Must be greater than -1.
36
+ N : int
37
+ The order of the Jacobi polynomial.
38
+
39
+ Returns
40
+ ----------
41
+ PL: ndarray, shape (N + 1, len(x))
42
+ The N-th row of PL is the values of orthonormal Jacobi
43
+ polynomial J_{N}^{alpha, beta}(x) / sqrt(gamma_{N}^{alpha, beta}).
44
+
45
+ References:
46
+ ----------
47
+ 1. Spectral Method P74
48
+ 2. Code-reproduction/Poisson-GPU.ipynb
49
+ """
50
+
51
+ xp = x.copy()
52
+ if len(xp.shape) == 2 and xp.shape[1] == 1:
53
+ xp = xp.T
54
+ PL = np.zeros((N + 1, max(xp.shape)))
55
+ gamma0 = np.power(2, alpha + beta + 1) * gamma(alpha + 1) * gamma(beta + 1) / gamma(alpha + beta + 2)
56
+ PL[0] = 1.0 / np.sqrt(gamma0)
57
+ if N == 0:
58
+ return PL.T
59
+ gamma1 = (alpha + 1) * (beta + 1) / (alpha + beta + 3) * gamma0
60
+ PL[1] = ((alpha + beta + 2) * xp / 2 + (alpha - beta) / 2) / np.sqrt(gamma1)
61
+ aold = 2 / (2 + alpha + beta) * np.sqrt((alpha + 1) * (beta + 1) / (alpha + beta + 3))
62
+ for i in range(1, N):
63
+ h1 = 2 * i + alpha + beta
64
+ anew = 2 / (h1 + 2) * np.sqrt((i + 1) * (i + 1 + alpha + beta) * (i + 1 + alpha) * (i + 1 + beta) / (h1 + 1) / (h1 + 3))
65
+ bnew = -(alpha * alpha - beta * beta) / h1 / (h1 + 2)
66
+ PL[i + 1] = 1 / anew * (-aold * PL[i - 1] + (xp - bnew) * PL[i])
67
+ aold = anew
68
+ return PL
69
+
70
+ def Jacobi_Gauss(alpha, beta, N):
71
+ """
72
+ This function computes the Gauss Jacobi quadrature first order
73
+ derivative matrix, nodes and weights of Jacobi polynomial J_{N}^{alpha, beta}.
74
+
75
+ Parameters
76
+ ----------
77
+ alpha : float
78
+ The alpha parameter of the Gauss Jacobi quadrature. alpha > -1.
79
+ beta : float
80
+ The beta parameter of the Gauss Jacobi quadrature. beta > -1.
81
+ If alpha = beta = 0, the Jacobi polynomial is Legendre polynomial.
82
+ N : int
83
+ The order of the Gauss Jacobi quadrature.
84
+
85
+ Returns
86
+ ----------
87
+ D: ndarray, shape (N, N)
88
+ The first order derivative matrix of the Gauss Jacobi quadrature.
89
+ r: ndarray, shape (N,)
90
+ The Gauss Jacobi quadrature nodes.
91
+ w: ndarray, shape (N,)
92
+ The Gauss Jacobi quadrature weights.
93
+
94
+ References
95
+ ----------
96
+ 1. Spectral Method P84
97
+ """
98
+
99
+ if N == 1:
100
+ D = np.zeros((1,1))
101
+ r = np.array([-(alpha - beta) / (alpha + beta + 2)])
102
+ w = np.array([2])
103
+ return D, r, w
104
+
105
+ h1 = 2.0 * np.arange(N) + alpha + beta
106
+ h11, h12, h13 = h1 + 1, h1 + 2, h1 + 3
107
+ h2 = 1.0 * np.arange(1, N)
108
+ # Adjust h1, h11, h12 values based on alpha and beta
109
+ # to avoid division by zero
110
+ if abs(alpha + beta) < 10 * np.finfo(float).eps:
111
+ h1[0] = 1.0
112
+ elif abs(alpha + beta + 1) < 10 * np.finfo(float).eps:
113
+ h11[0] = 1.0
114
+ elif abs(alpha + beta + 2) < 10 * np.finfo(float).eps:
115
+ h1[1], h12[0] = 1.0, 1.0
116
+
117
+ # equation (3.142) symmetric tridiagonal matrix A_{N+1}
118
+ A = diags(0.5 * (beta**2 - alpha**2) / h12 / h1).toarray() + \
119
+ diags(2 / h12[:-1] * np.sqrt(h2 * (h2 + alpha + beta) * \
120
+ (h2 + alpha) * (h2 + beta) / h11[:-1] / h13[:-1]), 1).toarray()
121
+
122
+ r, V = eigh(A + A.T)
123
+ # equation (3.144)
124
+ w = np.power(V[0, :], 2) * np.power(2, alpha + beta + 1) * \
125
+ gamma(alpha + 1) * gamma(beta + 1) / gamma(alpha + beta + 2)
126
+
127
+ l = JacobiP(r, alpha + 1, alpha + 1, N - 1)[-1]
128
+ # construct first order JG derivative matrix D by equation (3.164)
129
+ Distance = r[:, None] - r[None, :] + np.eye(N)
130
+ D = l[:, None] / l[None, :] / Distance
131
+ np.fill_diagonal(D, 0)
132
+
133
+ # for program stability, we force row sum of D to be 0
134
+ # which ensure the derivative of constants to be zero matrix
135
+ np.fill_diagonal(D, -np.sum(D, axis=1))
136
+ return D, r, w
137
+
138
+
139
+ def Jacobi_Gauss_Lobatto(alpha, beta, N):
140
+ """
141
+ This function computes the Gauss-Lobatto quadrature first order
142
+ derivative matrix, nodes and weights of Jacobi polynomial J_{N}^{alpha, beta}.
143
+ The nodes are {-1, 1, zeros of dx(J_N^{alpha, beta}(x))}
144
+
145
+ Parameters
146
+ ----------
147
+ alpha : float
148
+ The alpha parameter of the Gauss-Lobatto quadrature. alpha > -1.
149
+ beta : float
150
+ The beta parameter of the Gauss-Lobatto quadrature. beta > -1.
151
+ If alpha = beta = 0, the Jacobi polynomial is Legendre polynomial.
152
+ N : int
153
+ The order of the Gauss-Lobatto quadrature.
154
+
155
+ Returns
156
+ ----------
157
+ D: ndarray, shape (N + 1, N + 1)
158
+ The first order derivative matrix of the Gauss-Lobatto quadrature.
159
+ r: ndarray, shape (N + 1,)
160
+ The Gauss-Lobatto quadrature nodes.
161
+ w: ndarray, shape (N + 1,)
162
+ The Gauss-Lobatto quadrature weights.
163
+
164
+ References
165
+ ----------
166
+ 1. Spectral Method P83
167
+ 2. Code-reproduction/Poisson-GPU.ipynb
168
+ """
169
+
170
+ r = np.zeros((N + 1,))
171
+ r[0], r[-1] = -1.0, 1.0
172
+ w = np.zeros((N + 1,))
173
+ w[0] = (beta + 1) * gamma(beta + 1)**2
174
+ w[-1] = (alpha + 1) * gamma(alpha + 1)**2
175
+
176
+ if N > 1:
177
+ # dx(J_N^{alpha, beta}(x)) = C(alpha, beta, N) * J_{N-1}^{alpha+1, beta+1}(x)
178
+ # thus have same zeros
179
+ r[1:-1] = Jacobi_Gauss(alpha + 1, beta + 1, N - 1)[1]
180
+ # equ(3.139)
181
+ cd = 2**(alpha + beta + 1) * gamma(N) / gamma(N + alpha + beta + 2)
182
+ md = gamma(N + alpha + 1) / gamma(N + beta + 1)
183
+ w[0] *= cd * md
184
+ w[-1] *= cd / md
185
+ w[1:-1] = (2 * N + alpha + beta + 1) / (1 - r[1:-1]**2)**2 / (N-1) / (N + alpha + beta + 2) / JacobiP(r[1:-1], alpha + 2, beta + 2, N - 2)[-1]**2
186
+
187
+ # construct first order LGL derivative matrix D
188
+ Distance = r[:, None] - r[None, :] + np.eye(N + 1)
189
+ omega = np.prod(Distance, axis=1)
190
+ # equation (3.75)
191
+ D = diags(omega) @ (1 / Distance) @ diags(1 / omega)
192
+ # for program stability, we force row sum of D to be 0
193
+ # which ensure the derivative of constants to be zero matrix
194
+ np.fill_diagonal(D, 0)
195
+ np.fill_diagonal(D, -np.sum(D, axis=1))
196
+ return D, r, w
197
+
198
+
199
+ def HermiteP(x, N, normalized=False, return_full=True):
200
+ """
201
+ Evaluate orthognormal Hermite polynomial of degree N at x by recurrence relation.
202
+
203
+ Parameters
204
+ ----------
205
+ x : ndarray
206
+ The input array.
207
+ N : int
208
+ The degree of Hermite polynomial.
209
+ normalized : bool, optional
210
+ Whether to normalize PL[N] to have L2 norm 1. Default is False.
211
+ return_full : bool, optional
212
+ Whether to return the full PL array. Default is True.
213
+
214
+ Returns
215
+ ----------
216
+ PL[N] or PL : ndarray, shape (N + 1, len(x))
217
+ The value of Hermite polynomial of degree N at x, or the full PL array if return_full is True.
218
+
219
+ References
220
+ ----------
221
+ Spectral Method P254
222
+ """
223
+
224
+ xp = x.copy()
225
+ if len(xp.shape) == 2 and xp.shape[1] == 1:
226
+ xp = xp.T
227
+ PL = np.ones((N + 1, max(xp.shape)))
228
+ if normalized:
229
+ Norm = np.zeros(N + 1)
230
+ Norm[0] = np.linalg.norm(PL[0])
231
+ if N == 0:
232
+ return PL[0] / Norm[0] if normalized else PL[0]
233
+ PL[1] = 2 * xp
234
+ if normalized:
235
+ Norm[1] = np.linalg.norm(PL[1])
236
+ if N == 1 and normalized:
237
+ PL[0] /= Norm[0]
238
+ for i in range(1, N):
239
+ PL[i + 1] = 2 * xp * PL[i] - 2 * i * PL[i - 1]
240
+ if normalized:
241
+ if i == 1:
242
+ PL[0] /= Norm[0]
243
+ PL[i] /= Norm[i]
244
+ PL[i + 1] /= Norm[i]
245
+ Norm[i + 1] = np.linalg.norm(PL[i + 1])
246
+ if normalized:
247
+ PL[N] /= Norm[N]
248
+ return PL if return_full else PL[N]
249
+
250
+
251
+ def HermiteF(x, N, return_full=True):
252
+ """
253
+ Evaluate modified Hermite Function of degree N at x by recurrence relation.
254
+
255
+ Parameters
256
+ ----------
257
+ x : ndarray
258
+ The input array.
259
+ N : int
260
+ The degree of Hermite polynomial.
261
+ return_full : bool, optional
262
+ Whether to return the full PL array. Default is True.
263
+
264
+ Returns
265
+ ----------
266
+ PL[N] or PL : ndarray, shape (N + 1, len(x))
267
+ The value of modified Hermite polynomial of degree N at x, or the full PL array if return_full is True.
268
+
269
+ References
270
+ ----------
271
+ Spectral Method P256
272
+ """
273
+
274
+ xp = x.copy()
275
+ if len(xp.shape) == 2 and xp.shape[1] == 1:
276
+ xp = xp.T
277
+ PL = np.ones((N + 1, max(xp.shape)))
278
+ PL[0] = np.exp(-xp ** 2 / 2) / np.pi ** 0.25 # underflow may occur for large r
279
+ if N == 0:
280
+ return PL[0]
281
+ PL[1] = np.sqrt(2) * xp * PL[0]
282
+ for i in range(1, N):
283
+ PL[i + 1] = np.sqrt(2 / (i + 1)) * xp * PL[i] - np.sqrt(i / (i + 1)) * PL[i - 1]
284
+ return PL if return_full else PL[N]
285
+
286
+
287
+ def Hermite_Gauss(N, c=1 / np.sqrt(2)):
288
+ """
289
+ Generate the Hermite-Gauss(HG) quadrature points r and weights w w.r.t Hermite function.
290
+
291
+ Parameters
292
+ ----------
293
+ N : int
294
+ Number of points, underflow will occur for N > 740 with default c.
295
+ c : float, optional
296
+ The decay factor for v = exp(-(cx)^2) * u(x). Default is 1 / sqrt(2).
297
+ If c = 0, it degenerates to Hermite-ploynomial case.
298
+
299
+ Returns
300
+ ----------
301
+ D : ndarray, shape (N, N)
302
+ The first order derivative matrix of Hermite-Gauss quadrature points.
303
+ r : ndarray, shape (N,)
304
+ The Hermite-Gauss quadrature points.
305
+ w : ndarray, shape (N,)
306
+ The weights of Hermite-Gauss quadrature points.
307
+
308
+ References
309
+ ----------
310
+ Spectral Method P261
311
+ """
312
+
313
+ r, w = roots_hermite(N)
314
+ if c != 0:
315
+ w = 1 / (HermiteF(r, N - 1, False)**2 * N) # equ(7.81), modified weights for Hermite function
316
+ H = HermiteP(r, N - 1, True, False) * np.exp(-(c * r) ** 2) # equ(7.93), underflow may occur for large r
317
+ dis = r[:, np.newaxis] - r[np.newaxis, :]
318
+ np.fill_diagonal(dis, 1)
319
+ D = H[:, np.newaxis] / H[np.newaxis, :] / dis
320
+ np.fill_diagonal(D, r * (1 - 2 * c**2))
321
+ return D, r, w
322
+
323
+
324
+
325
+ def mapped_Jacobi_Gauss(alpha, beta, N, sf = 1, mapping = 'alg'):
326
+ """
327
+ Mapped Jacobi-Gauss quadrature points, weights and first order derivative matrix.
328
+ The mapping is defined by the function y = map(r, s), where r is the original Jacobi-Gauss quadrature points,
329
+ and s is the scaling factor of the mapping.
330
+
331
+ Parameters
332
+ ----------
333
+ alpha : float
334
+ The alpha parameter of the Jacobi polynomial. alpha > -1.
335
+ beta : float
336
+ The beta parameter of the Jacobi polynomial. beta > -1.
337
+ If alpha = beta = 0, the Jacobi polynomial is Legendre polynomial.
338
+ N : int
339
+ The order of the Jacobi polynomial.
340
+ sf : float, optional
341
+ The scaling factor of the mapping. Default is 1.
342
+ mapping : str, optional
343
+ The mapping function. Default is 'alg', which means using the algebraic mapping (cf. equ(7.159)).
344
+ Other options are 'log' and 'exp'.
345
+
346
+ Returns
347
+ ----------
348
+ D : ndarray, shape (N, N)
349
+ The first order derivative matrix of the mapped Jacobi-Gauss quadrature.
350
+ r : ndarray, shape (N,)
351
+ The mapped Jacobi-Gauss quadrature nodes.
352
+ w : ndarray, shape (N,)
353
+ The modified mapped Jacobi-Gauss quadrature weights.
354
+
355
+ References
356
+ ----------
357
+ 1. Spectral Method P280, P286
358
+ """
359
+
360
+ y, s = symbols('y s')
361
+
362
+ if mapping == 'alg':
363
+ map_expr = s * y / sqrt(1 - y**2)
364
+ diff_map_expr = diff(map_expr, y)
365
+ elif mapping == 'log':
366
+ map_expr = s * atanh(y)
367
+ diff_map_expr = diff(map_expr, y)
368
+ elif mapping == 'exp':
369
+ map_expr = sinh(s * y)
370
+ diff_map_expr = diff(map_expr, y)
371
+ else:
372
+ raise ValueError("Invalid mapping function. Please choose from 'alg', 'log' and 'exp'.")
373
+
374
+ map = lambdify((y, s), map_expr, "numpy")
375
+ diff_map = lambdify((y, s), diff_map_expr, "numpy")
376
+
377
+ D, r, w = Jacobi_Gauss(alpha, beta, N)
378
+ omega = (1 - r)**alpha * (1 + r)**beta
379
+ diff_g = diff_map(r, sf)
380
+ D, r, w = D / diff_g[:, None], map(r, sf), w / omega * diff_g
381
+ return D, r, w
382
+
383
+
384
+
385
+
386
+ def glue1D(interval, Ncell, D, r, w, end_pts = False):
387
+ """
388
+ Glue the differential matrix D, Gauss points r, and quadrature weights w on each cell together.
389
+
390
+ Parameters
391
+ ----------
392
+ interval : list or tuple, length 2
393
+ The left and right edge of the domain.
394
+ Ncell : int
395
+ The number of cells.
396
+ D, r, w : ndarray
397
+ The differential matrix, Gauss points, and quadrature weights on the reference cell [-1, 1],
398
+ which can be generated by `spectral.Jacobi_Gauss`, `spectral.Jacobi_Gauss_Lobatto`.
399
+ end_pts : bool, optional
400
+ Whether the end points -1, 1 are included in r or not. The default is False.
401
+ E.g., if `spectral.Jacobi_Gauss_Lobatto` is used, the end points are included in r.
402
+
403
+ Returns
404
+ ----------
405
+ D, r, w : ndarray
406
+ The differential matrix D, Gauss points r, and quadrature weights w on the interval.
407
+ if end_pts is False, shape (Ncell * Np, Ncell * Np), (Ncell * Np,), (Ncell * Np,)
408
+ if end_pts is True, shape (Ncell * (Np - 1) + 1, Ncell * (Np - 1) + 1), (Ncell * (Np - 1) + 1,), (Ncell * (Np - 1) + 1,)
409
+ """
410
+
411
+ Np = len(r)
412
+ D, r, w = D.copy(), r.copy(), w.copy()
413
+ left, right = interval
414
+ Len = (right - left) / Ncell / 2
415
+ r = (r + 1) * Len + left
416
+ r = r[None, :].repeat(Ncell, axis=0)
417
+ r = r + np.arange(Ncell)[:, None] * Len * 2
418
+ D_blocks = [D / Len] * Ncell
419
+ D, r, w = block_diag(*D_blocks), r.reshape(-1), np.tile(w, Ncell) * Len
420
+ if end_pts:
421
+ Glue = lil_matrix(np.zeros((Ncell * (Np - 1) + 1, Ncell * Np)))
422
+ for j in range(Ncell):
423
+ rowStart, colStart = j * (Np - 1), j * Np
424
+ Glue[rowStart:rowStart+Np, colStart:colStart+Np] = eye(Np)
425
+ D, Glue = csr_matrix(D), Glue.tocsr()
426
+ D = (Glue @ D @ Glue.T).toarray()
427
+ r = np.delete(r, np.arange(Np-1, len(r)-1, Np))
428
+ w = Glue @ w
429
+ return D, r, w
430
+
431
+ def glue_pts_1D(interval, Ncell, r, end_pts = False):
432
+ """
433
+ Glue the points r on each cell together.
434
+
435
+ Parameters
436
+ ----------
437
+ interval : list or tuple, length 2
438
+ The left and right edge of the domain.
439
+ Ncell : int
440
+ The number of cells.
441
+ r : ndarray
442
+ The collocation points on the reference cell [-1, 1],
443
+ end_pts : bool, optional
444
+ Whether the end points -1, 1 are included in r or not. The default is False.
445
+
446
+ Returns
447
+ ----------
448
+ r : ndarray
449
+ The collocation points r on the interval.
450
+ if end_pts is False, shape (Ncell * Np,)
451
+ if end_pts is True, shape (Ncell * (Np - 1) + 1,)
452
+ """
453
+
454
+ Np = len(r)
455
+ r = r.copy()
456
+ left, right = interval
457
+ Len = (right - left) / Ncell / 2
458
+ r = (r + 1) * Len + left
459
+ r = r[None, :].repeat(Ncell, axis=0)
460
+ r = r + np.arange(Ncell)[:, None] * Len * 2
461
+ r = r.reshape(-1)
462
+ if end_pts:
463
+ r = np.delete(r, np.arange(Np-1, len(r)-1, Np))
464
+ return r
specnn4pde/tools.py ADDED
@@ -0,0 +1,201 @@
1
+ __all__ = ['pkg_system_info', 'func_timer', 'timer',
2
+ ]
3
+
4
+ import platform
5
+ import psutil
6
+ import pandas as pd
7
+ from datetime import datetime
8
+ from IPython.display import display, HTML
9
+ import GPUtil
10
+ import importlib
11
+ import subprocess
12
+
13
+ import time
14
+ from functools import wraps
15
+
16
+ def pkg_system_info(packages, show_pkg=True, show_gpu=True, show_system=True):
17
+ """
18
+ This function takes a list of package names as input, imports each package dynamically,
19
+ and displays the version information of each package and the system information.
20
+
21
+ Parameters
22
+ ----------
23
+ packages : list of str
24
+ A list of package names to import and get version information.
25
+ show_pkg : bool
26
+ Whether to show package version information. Default is True.
27
+ show_system : bool
28
+ Whether to show system information. Default is True.
29
+ show_gpu : bool
30
+ Whether to show GPU information. Default is True.
31
+
32
+ Returns
33
+ ----------
34
+ None
35
+
36
+ Example
37
+ ----------
38
+ >>> pkg_system_info(['numpy', 'pandas', 'scipy', 'qiskit'], show_pkg=True, show_gpu=True, show_system=False)
39
+ """
40
+
41
+ def get_cpu_info():
42
+ # Get CPU information on Linux
43
+ cpu_info = subprocess.check_output("lscpu", shell=True).decode()
44
+ architecture = subprocess.check_output("uname -m", shell=True).decode().strip()
45
+ lines = cpu_info.split('\n')
46
+ info_dict = {}
47
+ for line in lines:
48
+ if "Vendor ID:" in line:
49
+ info_dict['Vendor ID'] = line.split(':')[1].strip()
50
+ if "CPU family:" in line:
51
+ info_dict['CPU family'] = line.split(':')[1].strip()
52
+ if "Model:" in line:
53
+ info_dict['Model'] = line.split(':')[1].strip()
54
+ if "Stepping:" in line:
55
+ info_dict['Stepping'] = line.split(':')[1].strip()
56
+ return architecture, info_dict
57
+
58
+
59
+ if show_pkg:
60
+ # Get packages version information
61
+ pkg_versions = []
62
+ for pkg_name in packages:
63
+ try:
64
+ pkg = importlib.import_module(pkg_name)
65
+ version = pkg.__version__
66
+ except AttributeError:
67
+ version = "Version not available"
68
+ pkg_versions.append((pkg.__name__, version))
69
+
70
+ pkg_versions_df = pd.DataFrame(pkg_versions, columns=['Package', 'Version'])
71
+ display(HTML(pkg_versions_df.to_html(index=False)))
72
+
73
+ if show_gpu:
74
+ # Get GPU information
75
+ gpus = GPUtil.getGPUs()
76
+ gpu_info_list = []
77
+ if gpus:
78
+ for gpu in gpus:
79
+ gpu_info = [gpu.name, f"{round(gpu.memoryTotal / 1024, 1)} Gb", 1]
80
+ for existing_gpu_info in gpu_info_list:
81
+ if existing_gpu_info[0] == gpu_info[0] and existing_gpu_info[1] == gpu_info[1]:
82
+ existing_gpu_info[2] += 1
83
+ break
84
+ else:
85
+ gpu_info_list.append(gpu_info)
86
+ else:
87
+ gpu_info_list = [['No GPU detected', 'N/A', 'N/A']]
88
+
89
+ gpu_info_df = pd.DataFrame(gpu_info_list, columns=['GPU Version', 'GPU Memory', 'Count'])
90
+ display(HTML(gpu_info_df.to_html(index=False)))
91
+
92
+ if show_system:
93
+ # Get system information
94
+ system_info = {
95
+ 'Python version': platform.python_version(),
96
+ 'Python compiler': platform.python_compiler(),
97
+ 'Python build': platform.python_build(),
98
+ 'OS': platform.system(),
99
+ 'CPU Version': platform.processor(),
100
+ 'CPU Number': psutil.cpu_count(),
101
+ 'CPU Memory': f"{round(psutil.virtual_memory().total / (1024.0 **3), 1)} Gb",
102
+ 'Time': datetime.now().strftime("%a %b %d %H:%M:%S %Y %Z")
103
+ }
104
+
105
+ if system_info['OS'] == 'Linux':
106
+ architecture, cpu_info = get_cpu_info()
107
+ system_info['CPU Version'] = f"{architecture} Family {cpu_info['CPU family']} Model {cpu_info['Model']} Stepping {cpu_info['Stepping']}, {cpu_info['Vendor ID']}"
108
+
109
+ system_info_df = pd.DataFrame(list(system_info.items()), columns=['System Information', 'Details'])
110
+ display(HTML(system_info_df.to_html(index=False)))
111
+
112
+
113
+
114
+
115
+ def func_timer(function):
116
+ """
117
+ This is a timer decorator. It calculates the execution time of the function.
118
+
119
+ Args
120
+ ----------
121
+ function : callable
122
+ The function to be timed.
123
+
124
+ Returns
125
+ ----------
126
+ function : callable
127
+ The decorated function which will print its execution time when called.
128
+
129
+ Example
130
+ ----------
131
+ >>> @func_timer
132
+ >>> def my_function(n):
133
+ >>> return sum(range(n))
134
+ >>> my_function(1000000)
135
+ """
136
+
137
+ @wraps(function)
138
+ def function_timer(*args, **kwargs):
139
+ t0 = time.time()
140
+ result = function(*args, **kwargs)
141
+ t1 = time.time()
142
+ print ("Running time of %s: %.3e seconds" % (function.__name__, t1-t0))
143
+ return result
144
+ return function_timer
145
+
146
+
147
+ class timer:
148
+ """
149
+ A simple timer class.
150
+
151
+ Attributes
152
+ ----------
153
+ start_time : float
154
+ The time when the timer was started.
155
+ last_lap_time : float
156
+ The time when the last lap was recorded.
157
+
158
+ Methods
159
+ -------
160
+ __init__():
161
+ Initializes the timer.
162
+ __str__():
163
+ Returns a string representation of the timer.
164
+ __repr__():
165
+ Returns a formal string representation of the timer.
166
+ reset():
167
+ Resets the timer.
168
+ update():
169
+ Updates the last lap time without printing anything.
170
+ lap():
171
+ Records a lap time and prints the time difference since the last lap.
172
+ stop():
173
+ Prints the total time.
174
+ """
175
+
176
+ def __init__(self):
177
+ self.start_time = time.time()
178
+ self.last_lap_time = self.start_time
179
+
180
+ def __str__(self):
181
+ return 'Timer(start_time=%.3e, last_lap_time=%.3e)' % (self.start_time, self.last_lap_time)
182
+
183
+ def __repr__(self):
184
+ return self.__str__()
185
+
186
+ def reset(self):
187
+ self.start_time = time.time()
188
+ self.last_lap_time = self.start_time
189
+
190
+ def update(self):
191
+ self.last_lap_time = time.time()
192
+
193
+ def lap(self):
194
+ current_time = time.time()
195
+ lap_time = current_time - self.last_lap_time
196
+ self.last_lap_time = current_time
197
+ print('Lap time: %.3e s' % lap_time)
198
+
199
+ def stop(self):
200
+ total_time = time.time() - self.start_time
201
+ return print('Total time: %.3e s' % total_time)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Mingxing Weng
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,53 @@
1
+ Metadata-Version: 2.1
2
+ Name: specnn4pde
3
+ Version: 0.0.1
4
+ Summary: Solving partial differential equations using spectral methods and neural networks.
5
+ Home-page: https://github.com/mxweng/specnn4pde
6
+ Author: MXWeng
7
+ Author-email: 2431141461@qq.com
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Intended Audience :: Science/Research
18
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
19
+ Requires-Python: >=3.6
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: GPUtil
23
+ Requires-Dist: IPython
24
+ Requires-Dist: numpy
25
+ Requires-Dist: pandas
26
+ Requires-Dist: psutil
27
+ Requires-Dist: scipy
28
+ Requires-Dist: sympy
29
+
30
+ # SpecNN4PDE
31
+ SpecNN4PDE is an under development Python library for solving partial differential equations using spectral methods and neural networks. It consists of the following modules:
32
+
33
+ - `spectral`: Provides functions for working with spectral methods as described in the book [Spectral Methods: Algorithms, Analysis and Applications](https://link.springer.com/book/10.1007/978-3-540-71041-7) by Shen, Tang, and Wang.
34
+ - `linalg`: This module primarily focuses on numerical algebra methods.
35
+ - `tools`: A collection of utility functions for system and package information retrieval, time measurement, etc.
36
+ - `npde`: Functions for solving partial differential equations, e.g., calculating the multivariate derivatives.
37
+
38
+ This project is still in the early stages of development, and the API is subject to change. The library is designed to be used in research and educational settings.
39
+
40
+ ## Dependencies
41
+
42
+ When you install this library using pip, most dependencies will be automatically handled. However, please note that the `npde` module requires PyTorch, which needs to be installed separately.
43
+
44
+ You can install PyTorch by following the instructions on the [official PyTorch website](https://pytorch.org/get-started/locally/). Please ensure that you select the correct installation command based on your operating system, package manager, Python version, and the specifications of your CUDA toolkit if you are planning to use PyTorch with GPU support.
45
+
46
+ If you are not planning to use the `npde` module, you do not need to install PyTorch.
47
+
48
+ ## Installation
49
+
50
+ To install this library, you can use pip:
51
+
52
+ ```bash
53
+ pip install specnn4pde
@@ -0,0 +1,10 @@
1
+ specnn4pde/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ specnn4pde/linalg.py,sha256=8qHJG24RUgwvkSofSYQy-1tm25R0gi6RymJ-zIFzhTY,1755
3
+ specnn4pde/npde.py,sha256=wUBWddAB25eU37UBQCPypMRzUaDb2wjlwR-J9Cd-pKI,12133
4
+ specnn4pde/spectral.py,sha256=txPb70G7_VUIqbJDD-rHYm_UaLsikoGSuz-WLjTKik8,16254
5
+ specnn4pde/tools.py,sha256=hOhyJXVJ7mH_Y_K7bkB0BRT8BcyAxZKRsIKWfne4TW0,6621
6
+ specnn4pde-0.0.1.dist-info/LICENSE,sha256=9tZg7MJu1ISWOuSfR85POKrnVpqeuSCk-1yc6iJOFfg,1091
7
+ specnn4pde-0.0.1.dist-info/METADATA,sha256=BZ3gMyQjKeQNd2cMZv8MHELKwHIxVfDCfXM0COpmtTI,2716
8
+ specnn4pde-0.0.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
9
+ specnn4pde-0.0.1.dist-info/top_level.txt,sha256=fKk-8IFaXpUmX3oazaH2Solv2v9peikEL3WO_ek5lGk,11
10
+ specnn4pde-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.43.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ specnn4pde