Source code for pyamg.gallery.laplacian

"""Discretizations of the Poisson problem."""
# pylint: disable=redefined-builtin

import numpy as np
import scipy as sp

from .stencil import stencil_grid


[docs] def poisson(grid, dtype=float, format=None, type='FD'): """Return a sparse matrix for the N-dimensional Poisson problem. The matrix represents a finite Difference approximation to the Poisson problem on a regular n-dimensional grid with unit grid spacing and Dirichlet boundary conditions. The last dimension is iterated over first: z, then y, then x. This should be used with np.mgrid() or np.ndenumerate. Parameters ---------- grid : tuple of integers grid dimensions e.g. (100,100) Notes ----- The matrix is symmetric and positive definite (SPD). Examples -------- >>> from pyamg.gallery import poisson >>> # 4 nodes in one dimension >>> poisson((4,)).toarray() array([[ 2., -1., 0., 0.], [-1., 2., -1., 0.], [ 0., -1., 2., -1.], [ 0., 0., -1., 2.]]) >>> # rectangular two dimensional grid >>> poisson((2,3)).toarray() array([[ 4., -1., 0., -1., 0., 0.], [-1., 4., -1., 0., -1., 0.], [ 0., -1., 4., 0., 0., -1.], [-1., 0., 0., 4., -1., 0.], [ 0., -1., 0., -1., 4., -1.], [ 0., 0., -1., 0., -1., 4.]]) """ grid = tuple(grid) N = len(grid) # grid dimension if N < 1 or min(grid) < 1: raise ValueError(f'Invalid grid shape: {grid}') # create N-dimension Laplacian stencil if type == 'FD': stencil = np.zeros((3,) * N, dtype=dtype) for i in range(N): stencil[(1,)*i + (0,) + (1,)*(N-i-1)] = -1 stencil[(1,)*i + (2,) + (1,)*(N-i-1)] = -1 stencil[(1,)*N] = 2*N if type == 'FE': stencil = -np.ones((3,) * N, dtype=dtype) stencil[(1,)*N] = 3**N - 1 return stencil_grid(stencil, grid, format=format)
[docs] def gauge_laplacian(npts, spacing=1.0, beta=0.1): """Construct a Gauge Laplacian from Quantum Chromodynamics for regular 2D grids. Note that this function is not written efficiently, but should be fine for N x N grids where N is in the low hundreds. Parameters ---------- npts : int number of pts in x and y directions spacing : float grid spacing between points beta : float temperature Note that if beta=0, then we get the typical 5pt Laplacian stencil Returns ------- A : csr matrix A is Hermitian positive definite for beta > 0.0 A is Symmetric semi-definite for beta = 0.0 Examples -------- >>> from pyamg.gallery import gauge_laplacian >>> A = gauge_laplacian(10) References ---------- .. [1] MacLachlan, S. and Oosterlee, C., "Algebraic Multigrid Solvers for Complex-Valued Matrices", Vol. 30, SIAM J. Sci. Comp, 2008 """ # The gauge Laplacian has the same sparsity structure as a normal # Laplacian, so we start out with a Poisson Operator N = npts A = poisson((N, N), format='coo', dtype=complex) # alpha is a random function of a point's integer position # on a 1-D grid along the x or y direction. e.g. the first # point at (0,0) would be evaluate at alpha_*[0], while the # last point at (N*spacing, N*spacing) would evaluate at alpha_*[-1] alpha_x = 1.0j * 2.0 * np.pi * beta * np.random.randn(N*N) alpha_y = 1.0j * 2.0 * np.pi * beta * np.random.randn(N*N) # Replace off diagonals of A for i in range(A.nnz): r = A.row[i] c = A.col[i] diff = np.abs(r - c) index = min(r, c) if r > c: s = -1.0 else: s = 1.0 if diff == 1: # differencing in the x-direction A.data[i] = -1.0 * np.exp(s * alpha_x[index]) if diff == N: # differencing in the y-direction A.data[i] = -1.0 * np.exp(s * alpha_y[index]) # Handle periodic BCs alpha_x = 1.0j * 2.0 * np.pi * beta * np.random.randn(N*N) alpha_y = 1.0j * 2.0 * np.pi * beta * np.random.randn(N*N) new_r = [] new_c = [] new_data = [] new_diff = [] for i in range(0, N): new_r.append(i) new_c.append(i + N*N - N) new_diff.append(N) for i in range(N*N - N, N*N): new_r.append(i) new_c.append(i - N*N + N) new_diff.append(N) for i in range(0, N*N-1, N): new_r.append(i) new_c.append(i + N - 1) new_diff.append(1) for i in range(N-1, N*N, N): new_r.append(i) new_c.append(i - N + 1) new_diff.append(1) for i, (r, c) in enumerate(zip(new_r, new_c)): diff = new_diff[i] index = min(r, c) if r > c: s = -1.0 else: s = 1.0 if diff == 1: # differencing in the x-direction new_data.append(-1.0 * np.exp(s * alpha_x[index])) if diff == N: # differencing in the y-direction new_data.append(-1.0 * np.exp(s * alpha_y[index])) # Construct Final Matrix data = np.hstack((A.data, np.array(new_data))) row = np.hstack((A.row, np.array(new_r))) col = np.hstack((A.col, np.array(new_c))) A = sp.sparse.coo_matrix((data, (row, col)), shape=(N*N, N*N)).tocsr() return (1.0/spacing**2)*A