60 """5-point per-volume operator on an n x n level, identity rows at pinned cells."""
62 idx =
lambda i, j: (i % n) * n + (j % n)
63 rows, cols, vals = [], [], []
64 diag = np.array(W, dtype=float).ravel().copy()
70 for (a, ii, jj)
in ((ax[i + 1, j], i + 1, j), (ax[i, j], i - 1, j), (ay[i, j + 1], i, j + 1), (ay[i, j], i, j - 1)):
73 inside = 0 <= ii < n
and 0 <= jj < n
74 if inside
or periodic:
75 if unknown[ii % n, jj % n]:
76 diag[r] += a / h**2; rows.append(r); cols.append(idx(ii, jj)); vals.append(-a / h**2)
78 diag[r] += 2 * a / h**2
83 rows += list(range(n * n)); cols += list(range(n * n)); vals += list(diag)
84 return sp.csr_matrix((vals, (rows, cols)), shape=(n * n, n * n))
88 """R: average of 4 children (nc^2 x n^2); P: cell-centred bilinear (n^2 x nc^2)."""
90 R = sp.lil_matrix((nc * nc, n * n)); P = sp.lil_matrix((n * n, nc * nc))
95 R[I * nc + J, (2 * I + di) * n + 2 * J + dj] = 0.25
99 si = 1
if i % 2
else -1; sj = 1
if j % 2
else -1
100 for (II, JJ, w)
in ((I, J, 9 / 16), (I + si, J, 3 / 16), (I, J + sj, 3 / 16), (I + si, J + sj, 1 / 16)):
102 P[i * n + j, (II % nc) * nc + JJ % nc] += w
103 elif 0 <= II < nc
and 0 <= JJ < nc:
104 P[i * n + j, II * nc + JJ] += w
105 return R.tocsr(), P.tocsr()
111 ax, ay, unk = G.ax, G.ay, G.unknown
112 W0 = G.alpha / np.maximum(1.1 * G.supp * h, 1e-300) * (G.alpha > 0)
113 A =
level_op(ax, ay, unk, h, G.periodic, W0)
116 levels.append(dict(A=A, unk=unk.ravel(), n=n))
120 levels[-1].update(R=R, P=P)
121 nc, H = n // 2, h * 2 ** (L + 1)
122 unk_c = unk.reshape(nc, 2, nc, 2).any(axis=(1, 3))
124 Ac = (R @ A @ P).tolil()
125 for k
in np.nonzero(~unk_c.ravel())[0]:
126 Ac.rows[k] = [k]; Ac.data[k] = [1.0]
129 axc = 0.5 * (ax[0::2, :][:, 0::2] + ax[0::2, :][:, 1::2])
130 ayc = 0.5 * (ay[:, 0::2][0::2, :] + ay[:, 0::2][1::2, :])
132 if mode ==
"redisc-sL":
133 s = 1.1 * G.supp * h * f
136 Wf = np.where(G.alpha > 0, G.alpha / np.maximum(s, 1e-300), 0.0)
137 Wc = Wf.reshape(nc, f, nc, f).
mean(axis=(1, 3))
138 A =
level_op(axc, ayc, unk_c, H, G.periodic, Wc)
140 unk = unk_c; n = nc; L += 1
158 lv = levels[l]; A = lv[
"A"]; n = lv[
"n"]
161 x = np.linalg.lstsq(A.toarray(), b, rcond=
None)[0]
162 x[lv[
"unk"]] -= x[lv[
"unk"]].
mean();
return x
163 return spla.spsolve(A.tocsc(), b)
167 x =
smooth(A, x, b, sets, (0, 1, 2, 3))
171 u = levels[l + 1][
"unk"]; rc[u] -= rc[u].
mean(); rc[~u] = 0
172 ec =
vcycle(levels, l + 1, rc, singular)
176 x =
smooth(A, x, b, sets, (3, 2, 1, 0))
181 A = levels[0][
"A"]; u = levels[0][
"unk"]
182 rng = np.random.default_rng(3)
183 e = rng.standard_normal(A.shape[0]); e[~u] = 0
188 nrm = np.linalg.norm(e)
189 e = e -
vcycle(levels, 0, A @ e, singular)
193 hist.append(np.linalg.norm(e) / nrm)
194 if not np.isfinite(hist[-1])
or hist[-1] > 1e6:
196 return float(np.exp(np.mean(np.log(hist[-10:]))))
200 A = levels[0][
"A"]; u = levels[0][
"unk"]
201 rng = np.random.default_rng(5)
202 b = np.where(u, 1 + 0.3 * rng.standard_normal(A.shape[0]), 0.0)
205 M = spla.LinearOperator(A.shape, matvec=
lambda r:
vcycle(levels, 0, r.copy(), singular))
207 x, info = spla.bicgstab(A, b, M=M, rtol=1e-10, maxiter=300, callback=
lambda _: cnt.__setitem__(0, cnt[0] + 1))
208 return cnt[0]
if info == 0
else f
"{cnt[0]}!"
227 "box+disc R=L/4 (G1-like)": (
disc_union([(0.5 + off, 0.5 + off)], 0.25, L),
False,
False),
228 "periodic+disc R=L/8": (
disc_union([(0.5 + off, 0.5 + off)], 0.125, L),
True,
False),
229 "periodic+disc R=L/32": (
disc_union([(0.5 + off, 0.5 + off)], 1 / 32, L),
True,
False),