def ConstrainedMaximize(Q, l, m): dim = l.shape[0] if dim == 1: return (l[0] + np.sqrt(Q[0, 0])) / m[0] # Discard infinite values, handled afterwards pos_bad = l.min(axis=0) == -np.inf L = l.copy() L[:, pos_bad] = 0 # Solve the quadratic equation A = lp.inverse(Q) lAl = lp.dot_VAV(L, A, L) lAm = lp.dot_VAV(L, A, m) mAm = lp.dot_VAV(m, A, m) delta = lAm**2 - (lAl - 1.) * mAm pos_bad = np.logical_or(pos_bad, delta <= 0) delta[pos_bad] = 1. mu = (lAm + np.sqrt(delta)) / mAm # Check the positivity # v = dot_AV(A,mu*m-L) rm_ad = np.array v = lp.dot_AV(rm_ad(A), rm_ad(mu) * rm_ad(m) - rm_ad(L)) pos_bad = np.logical_or(pos_bad, np.any(v < 0, axis=0)) result = mu result[pos_bad] = -np.inf # Solve the lower dimensional sub-problems # We could restrict to the bad positions, and avoid repeating computations for i in range(dim): axes = np.full((dim), True) axes[i] = False res = ConstrainedMaximize(Q[axes][:, axes], l[axes], m[axes]) result = np.maximum(result, res) return result
def Scheme(a, b, d2u, stencil): delta = d2u - lp.dot_VAV( np.expand_dims(stencil.V1, (2, 3)), np.expand_dims(a, 2), np.expand_dims(stencil.V1, (2, 3)), ) spad_sum(b) spad_sum(delta) # For now, replace `b` with one when it is zero, to prevent errors during automatic # differentiation. b_zero = b == 0 b = np.where(b_zero, 1, b) residue = -np.inf for i in range(stencil.V3.shape[2]): residue = np.maximum( residue, H3( stencil.Q[:, :, i, np.newaxis, np.newaxis], stencil.w[:, i, np.newaxis, np.newaxis], b, delta[stencil.V3_indices[:, i]], ), ) for i in range(stencil.V2.shape[2]): residue = np.maximum( residue, H2( stencil.omega0[i, np.newaxis, np.newaxis], stencil.omega1[:, i, np.newaxis, np.newaxis], stencil.omega2[:, i, np.newaxis, np.newaxis], b, delta[stencil.V2_indices[:, i]], ), ) # Reset residue to minus infinity where `b` should have been zero. residue = np.where(b_zero, -np.inf, residue) for i in range(stencil.V1.shape[1]): residue = np.maximum( residue, H1(stencil.V1[:, i, np.newaxis, np.newaxis], delta[i])) return residue
def NextAngleAndSuperbase(theta, sb, D): pairs = np.stack([(1, 2), (2, 0), (0, 1)], axis=1) scals = lp.dot_VAV(np.expand_dims(sb[:, pairs[0]], axis=1), np.expand_dims(D, axis=-1), np.expand_dims(sb[:, pairs[1]], axis=1)) phi = np.arctan2(scals[2], scals[1]) cst = -scals[0] / np.sqrt(scals[1]**2 + scals[2]**2) theta_max = np.pi * np.ones(3) mask = cst < 1 theta_max[mask] = (phi[mask] - np.arccos(cst[mask])) / 2 theta_max[theta_max <= 0] += np.pi theta_max[theta_max <= theta] = np.pi k = np.argmin(theta_max) i, j = (k + 1) % 3, (k + 2) % 3 return (theta_max[k], np.stack([sb[:, i], -sb[:, j], sb[:, j] - sb[:, i]], axis=1))
def MinimizeTrace(u, alpha, bc, sqrt_relax=1e-16): # Compute the tensor decompositions D = MakeD(alpha) theta, sb = AnglesAndSuperbases(D) theta = np.array([theta[:-1], theta[1:]]) # Compute the second order differences in the direction orthogonal to the superbase sb_rotated = np.array([-sb[1], sb[0]]) d2u = bc.Diff2(u, sb_rotated) d2u[..., bc.not_interior] = 0. # Placeholder values to silent NaNs # Compute the coefficients of the tensor decompositions sb1, sb2 = np.roll(sb, 1, axis=1), np.roll(sb, 2, axis=1) sb1, sb2 = (e.reshape((2, 3, 1) + sb.shape[2:]) for e in (sb1, sb2)) D = D.reshape((2, 2, 1, 3, 1) + D.shape[3:]) # Axes of D are space,space,index of superbase element, index of D, index of superbase, and possibly shape of u scals = lp.dot_VAV(sb1, D, sb2) # Compute the coefficients of the trigonometric polynomial scals, theta = (bc.as_field(e) for e in (scals, theta)) coefs = -lp.dot_VV(scals, np.expand_dims(d2u, axis=1)) # Optimality condition for the trigonometric polynomial in the interior value = coefs[0] - np.sqrt( np.maximum(coefs[1]**2 + coefs[2]**2, sqrt_relax)) coefs_ = np.array(coefs) # removed AD information angle = np.arctan2(-coefs_[2], -coefs_[1]) / 2. angle[angle < 0] += np.pi # Boundary conditions for the trigonometric polynomial minimization mask = np.logical_not(np.logical_and(theta[0] <= angle, angle <= theta[1])) t, c = theta[:, mask], coefs[:, mask] value[mask], amin_t = ad.min_argmin(c[0] + c[1] * np.cos(2 * t) + c[2] * np.sin(2 * t), axis=0) # Minimize over superbases value, amin_sb = ad.min_argmin(value, axis=0) # Record the optimal angles for future use angle[mask] = np.take_along_axis(t, np.expand_dims(amin_t, axis=0), axis=0).squeeze(axis=0) # Min over bc angle = np.take_along_axis(angle, np.expand_dims(amin_sb, axis=0), axis=0) # Min over superbases return value, angle
def SchemeLinear(u, x, f, bc): coef, offsets = Selling.Decomposition(D(x)) # coef_min = np.min(coef) # offsets_norm2 = lp.dot_VV(offsets, offsets) # offsets_max2 = np.max(np.where(coef < 1e-13, 0, offsets_norm2)) # print(f"h: {bc.gridscale}, c: {coef_min}, e2: {offsets_max2}") du = bc.DiffCentered(u, offsets) d2u = bc.Diff2(u, offsets) return np.where( bc.interior, -lp.dot_VAV(omega(x), lp.inverse(D(x)), np.sum(coef * du * offsets, axis=1)) - lp.dot_VV(coef, d2u) - f, u - bc.grid_values, )
def F(g): return lp.dot_VAV(g - omega, D, g - omega)