agd.ExportedCode.Notebooks_NonDiv.MongeAmpere

  1# Code automatically exported from notebook MongeAmpere.ipynb in directory Notebooks_NonDiv
  2# Do not modify
  3from ... import Selling
  4from ... import Domain
  5from ... import LinearParallel as lp
  6from ... import AutomaticDifferentiation as ad
  7from agd.Plotting import savefig; #savefig.dirName = "Figures/MongeAmpere"
  8
  9import numpy as np
 10from matplotlib import pyplot as plt
 11
 12newton_root = ad.Optimization.newton_root
 13stop    = ad.Optimization.stop_default
 14damping = ad.Optimization.damping_default 
 15norm = ad.Optimization.norm
 16
 17def SchemeNonMonotone(u,f,bc):
 18    # Compute the hessian matrix of u
 19    uxx = bc.Diff2(u,(1,0))
 20    uyy = bc.Diff2(u,(0,1))
 21    uxy = 0.25*(bc.Diff2(u,(1,1)) - bc.Diff2(u,(1,-1)))
 22    
 23    # Numerical scheme
 24    det = uxx*uyy-uxy**2
 25    residue = f - det
 26    
 27    # Boundary conditions
 28    return np.where(bc.interior,residue,u-bc.grid_values)
 29
 30def MALBR_H(d2u):
 31    a,b,c = np.sort(np.maximum(0.,d2u), axis=0)
 32
 33    # General formula, handling infinite values separately
 34    A,B,C = (np.where(e==np.inf,0.,e) for e in (a,b,c))
 35    result = 0.5*(A*B+B*C+C*A)-0.25*(A**2+B**2+C**2)
 36    
 37    pos_inf = np.logical_or.reduce(d2u==np.inf)    
 38    result[pos_inf]=np.inf
 39    
 40    pos_ineq = a+b<c
 41    result[pos_ineq] = (A*B)[pos_ineq]
 42        
 43    return result
 44    
 45def SchemeMALBR(u,SB,f,bc):
 46    # Compute the finite differences along the superbase directions
 47    d2u = bc.Diff2(u,SB)
 48    d2u[...,bc.not_interior] = 0. # Replace NaNs with arbitrary values to silence warnings
 49    
 50    # Numerical scheme
 51    residue = f-MALBR_H(d2u).min(axis=0)
 52    
 53    # Boundary conditions
 54    return np.where(bc.interior,residue,u-bc.grid_values)
 55
 56def InvalidMALBR(u,SB,f,bc):
 57    residue = SchemeMALBR(u,SB,f,bc)
 58    return np.any(residue[bc.interior]>=f/2)
 59
 60def SchemeMALBR_OptInner(u,SB,bc,oracle=None):
 61    # If the active superbases are known, then take only these
 62    if not(oracle is None):
 63        SB = np.take_along_axis(SB,np.broadcast_to(oracle,SB.shape[:2]+(1,)+oracle.shape),axis=2)
 64                
 65    d2u = bc.Diff2(u,SB)
 66    d2u[...,bc.not_interior] = 0. # Placeholder value to silent NaN warnings
 67    # Evaluate the complex non-linear function using dense - sparse composition
 68    result = ad.apply(MALBR_H,d2u,shape_bound=u.shape)
 69    
 70    return ad.min_argmin(result,axis=0)
 71
 72def SchemeMALBR_Opt(u,SB,f,bc):
 73    
 74    # Evaluate using the envelope theorem
 75    result,_ = ad.apply(SchemeMALBR_OptInner, u,bc.as_field(SB),bc, envelope=True)
 76        
 77    # Boundary conditions
 78    return np.where(bc.interior, f - result, u-bc.grid_values)
 79
 80def ConstrainedMaximize(Q,l,m):
 81    dim = l.shape[0]
 82    if dim==1:
 83        return (l[0]+np.sqrt(Q[0,0]))/m[0]
 84    
 85    # Discard infinite values, handled afterwards
 86    pos_bad = l.min(axis=0)==-np.inf
 87    L = l.copy(); L[:,pos_bad]=0
 88    
 89    # Solve the quadratic equation
 90    A = lp.inverse(Q)    
 91    lAl = lp.dot_VAV(L,A,L)
 92    lAm = lp.dot_VAV(L,A,m)
 93    mAm = lp.dot_VAV(m,A,m)
 94    
 95    delta = lAm**2 - (lAl-1.)*mAm
 96    pos_bad = np.logical_or(pos_bad,delta<=0)
 97    delta[pos_bad] = 1.
 98    
 99    mu = (lAm + np.sqrt(delta))/mAm
100    
101    # Check the positivity
102#    v = dot_AV(A,mu*m-L)
103    rm_ad = np.array
104    v = lp.dot_AV(rm_ad(A),rm_ad(mu)*rm_ad(m)-rm_ad(L))
105    pos_bad = np.logical_or(pos_bad,np.any(v<0,axis=0))
106    
107    result = mu
108    result[pos_bad] = -np.inf
109    
110    # Solve the lower dimensional sub-problems
111    # We could restrict to the bad positions, and avoid repeating computations
112    for i in range(dim):             
113        axes = np.full((dim),True); axes[i]=False
114        res = ConstrainedMaximize(Q[axes][:,axes],l[axes],m[axes])
115        result = np.maximum(result,res)
116    return result
117
118def SchemeUniform(u,SB,f,bc):
119    # Compute the finite differences along the superbase directions
120    d2u = bc.Diff2(u,SB) 
121    d2u[...,bc.not_interior] = 0. # Placeholder value to silent NaN warnings
122    
123    # Generate the parameters for the low dimensional optimization problem
124    Q = 0.5*np.array([[0,1,1],[1,0,1],[1,1,0]])
125    l = -d2u
126    m = lp.dot_VV(SB,SB)
127    
128    # Evaluate the numerical scheme
129    m = bc.as_field(m)
130    from agd.FiniteDifferences import as_field
131    Q = as_field(Q,m.shape[1:])
132    
133    dim = 2
134    alpha = dim * f**(1/dim)
135    mask= (alpha==0)
136
137    Q = Q* np.where(mask,1.,alpha**2)
138    residue = ConstrainedMaximize(Q,l,m).max(axis=0)
139    residue[mask] = np.max(l/m,axis=0).max(axis=0)[mask]
140    
141    # Boundary conditions
142    return np.where(bc.interior,residue,u-bc.grid_values)
143
144def SchemeUniform_OptInner(u,SB,f,bc,oracle=None):
145    # Use the oracle, if available, to select the active superbases only
146    if not(oracle is None):
147        SB = np.take_along_axis(SB,np.broadcast_to(oracle,SB.shape[:2]+(1,)+oracle.shape),axis=2)
148
149    d2u = bc.Diff2(u,SB) 
150    d2u[...,bc.not_interior] = 0. # Placeholder value to silent NaN warnings
151    
152    # Generate the parameters for the low dimensional optimization problem
153    Q = 0.5*np.array([[0,1,1],[1,0,1],[1,1,0]])
154    dim = 2
155    l = -d2u
156    m = lp.dot_VV(SB,SB)
157    
158    m = bc.as_field(m)
159    from agd.FiniteDifferences import as_field
160    Q = as_field(Q,m.shape[1:])
161    
162    dim = 2
163    alpha = dim * f**(1/dim)
164    mask= (alpha==0)
165
166    Q = Q* np.where(mask,1.,alpha**2)
167    # Evaluate the non-linear functional using dense-sparse composition
168    residue = ad.apply(ConstrainedMaximize,Q,l,m,shape_bound=u.shape).copy()
169    residue[:,mask] = np.max(l/m,axis=0)[:,mask]
170    
171    return ad.max_argmax(residue,axis=0)
172
173def SchemeUniform_Opt(u,SB,f,bc):
174    
175    # Evaluate the maximum over the superbases using the envelope theorem
176    residue,_ = ad.apply(SchemeUniform_OptInner, u,bc.as_field(SB),f,bc, envelope=True)
177    
178    return np.where(bc.interior,residue,u-bc.grid_values)
179
180def Hessian_ad(u,x):
181    x_ad = ad.Dense2.identity(constant=x,shape_free=(2,))
182    return u(x_ad).hessian()
183def MongeAmpere_ad(u,x):
184    return lp.det(Hessian_ad(u,x))
def newton_root( f, x0, fargs=(), stop='Default', relax=None, damping=None, ad='Sparse', solver=None, in_place=False):
151def newton_root(f,x0,fargs=tuple(),stop="Default",
152	relax=None,damping=None,ad="Sparse",solver=None,in_place=False):
153	"""
154	Newton's method, for finding a root of a given function.
155	f : function to be solved
156	x0 : initial guess for the root
157	stop : stopping criterion, presented as either
158	   - keyword "Default" for using the stop_default class
159	   - a dict for using the stop_class with specified initialization arguments
160	   - a callable,  
161	relax : added to the jacobian before inversion
162	damping : criterion for step reduction
163	ad : is either 
164	   - keyword "Sparse" for using Sparse AD (Default)
165	   - keyword "Dense" for using Dense AD
166	   - a shape_bound given as a tuple, for Dense AD with few independent variables
167	"""
168
169	if stop == "Default": 	stop = stop_default()
170	elif isinstance(stop,dict): stop = stop_default(**stop)
171	if ad == "Dense":		ad = tuple() # Tuple represents shape_bound
172
173	# Create a variable featuring AD information
174	def ad_var(x0):
175		if not in_place: x0=np.copy(x0)
176
177		if ad == "Sparse":		   return Sparse.identity(constant=x0)
178		elif isinstance(ad,tuple): return Dense.identity( constant=x0,shape_bound=ad)						
179		assert False
180
181	# Find Newton's descent direction
182	def descent_direction(residue):
183		if relax is not None: residue+=relax # Relax is usually a multiple of identity
184
185		if solver is not None:     return solver(residue)
186		elif ad == "Sparse":       return residue.solve() # Sparse ad
187		elif isinstance(ad,tuple): return residue.solve(shape_bound=ad) # Dense ad
188
189	x=ad_var(x0)
190	for niter in itertools.count():
191		residue = f(x,*fargs)
192		if stop(residue,niter): return remove_ad(x)
193
194		direction = descent_direction(residue)
195
196		step = 1. if damping is None else damping(remove_ad(x),direction,*fargs)
197		x += step*direction

Newton's method, for finding a root of a given function. f : function to be solved x0 : initial guess for the root stop : stopping criterion, presented as either

  • keyword "Default" for using the stop_default class
  • a dict for using the stop_class with specified initialization arguments
  • a callable,
    relax : added to the jacobian before inversion damping : criterion for step reduction ad : is either
  • keyword "Sparse" for using Sparse AD (Default)
  • keyword "Dense" for using Dense AD
  • a shape_bound given as a tuple, for Dense AD with few independent variables
def norm(arr, ord=2, axis=None, keepdims=False, averaged=False):
18def norm(arr,ord=2,axis=None,keepdims=False,averaged=False):
19	"""
20	Returns L^p norm of array, seen as a vector, w.r.t. weights.
21	Defined as : (sum_i x[i]^p)^(1/p)
22
23	Remark : not a matrix operator norm
24	
25	Inputs:
26	 - ord : exponent p
27	 - axis : int or None, axis along which to compute the norm. 
28	 - keepdims : wether to keep singleton dimensions.
29	 - averaged : wether to introduce a normalization factor, so that norm(ones(...))=1
30
31	Compatible with automatic differentiation.
32	"""
33	arr = ad_generic.array(arr)
34	if ord==np.inf or ord%2!=0:
35		try: arr = np.abs(arr)
36		except TypeError: arr = np.vectorize(np.abs)(arr)
37	if ord==np.inf: return np.max(arr,axis=axis,keepdims=keepdims)
38	sum_pow = np.sum(arr**ord,axis=axis,keepdims=keepdims)
39	
40	if averaged:
41		size = arr.size if axis is None else arr.shape[axis]
42		sum_pow/=size
43
44	return sum_pow**(1./ord)

Returns L^p norm of array, seen as a vector, w.r.t. weights. Defined as : (sum_i x[i]^p)^(1/p)

Remark : not a matrix operator norm

Inputs:

  • ord : exponent p
  • axis : int or None, axis along which to compute the norm.
  • keepdims : wether to keep singleton dimensions.
  • averaged : wether to introduce a normalization factor, so that norm(ones(...))=1

Compatible with automatic differentiation.

def SchemeNonMonotone(u, f, bc):
18def SchemeNonMonotone(u,f,bc):
19    # Compute the hessian matrix of u
20    uxx = bc.Diff2(u,(1,0))
21    uyy = bc.Diff2(u,(0,1))
22    uxy = 0.25*(bc.Diff2(u,(1,1)) - bc.Diff2(u,(1,-1)))
23    
24    # Numerical scheme
25    det = uxx*uyy-uxy**2
26    residue = f - det
27    
28    # Boundary conditions
29    return np.where(bc.interior,residue,u-bc.grid_values)
def MALBR_H(d2u):
31def MALBR_H(d2u):
32    a,b,c = np.sort(np.maximum(0.,d2u), axis=0)
33
34    # General formula, handling infinite values separately
35    A,B,C = (np.where(e==np.inf,0.,e) for e in (a,b,c))
36    result = 0.5*(A*B+B*C+C*A)-0.25*(A**2+B**2+C**2)
37    
38    pos_inf = np.logical_or.reduce(d2u==np.inf)    
39    result[pos_inf]=np.inf
40    
41    pos_ineq = a+b<c
42    result[pos_ineq] = (A*B)[pos_ineq]
43        
44    return result
def SchemeMALBR(u, SB, f, bc):
46def SchemeMALBR(u,SB,f,bc):
47    # Compute the finite differences along the superbase directions
48    d2u = bc.Diff2(u,SB)
49    d2u[...,bc.not_interior] = 0. # Replace NaNs with arbitrary values to silence warnings
50    
51    # Numerical scheme
52    residue = f-MALBR_H(d2u).min(axis=0)
53    
54    # Boundary conditions
55    return np.where(bc.interior,residue,u-bc.grid_values)
def InvalidMALBR(u, SB, f, bc):
57def InvalidMALBR(u,SB,f,bc):
58    residue = SchemeMALBR(u,SB,f,bc)
59    return np.any(residue[bc.interior]>=f/2)
def SchemeMALBR_OptInner(u, SB, bc, oracle=None):
61def SchemeMALBR_OptInner(u,SB,bc,oracle=None):
62    # If the active superbases are known, then take only these
63    if not(oracle is None):
64        SB = np.take_along_axis(SB,np.broadcast_to(oracle,SB.shape[:2]+(1,)+oracle.shape),axis=2)
65                
66    d2u = bc.Diff2(u,SB)
67    d2u[...,bc.not_interior] = 0. # Placeholder value to silent NaN warnings
68    # Evaluate the complex non-linear function using dense - sparse composition
69    result = ad.apply(MALBR_H,d2u,shape_bound=u.shape)
70    
71    return ad.min_argmin(result,axis=0)
def SchemeMALBR_Opt(u, SB, f, bc):
73def SchemeMALBR_Opt(u,SB,f,bc):
74    
75    # Evaluate using the envelope theorem
76    result,_ = ad.apply(SchemeMALBR_OptInner, u,bc.as_field(SB),bc, envelope=True)
77        
78    # Boundary conditions
79    return np.where(bc.interior, f - result, u-bc.grid_values)
def ConstrainedMaximize(Q, l, m):
 81def ConstrainedMaximize(Q,l,m):
 82    dim = l.shape[0]
 83    if dim==1:
 84        return (l[0]+np.sqrt(Q[0,0]))/m[0]
 85    
 86    # Discard infinite values, handled afterwards
 87    pos_bad = l.min(axis=0)==-np.inf
 88    L = l.copy(); L[:,pos_bad]=0
 89    
 90    # Solve the quadratic equation
 91    A = lp.inverse(Q)    
 92    lAl = lp.dot_VAV(L,A,L)
 93    lAm = lp.dot_VAV(L,A,m)
 94    mAm = lp.dot_VAV(m,A,m)
 95    
 96    delta = lAm**2 - (lAl-1.)*mAm
 97    pos_bad = np.logical_or(pos_bad,delta<=0)
 98    delta[pos_bad] = 1.
 99    
100    mu = (lAm + np.sqrt(delta))/mAm
101    
102    # Check the positivity
103#    v = dot_AV(A,mu*m-L)
104    rm_ad = np.array
105    v = lp.dot_AV(rm_ad(A),rm_ad(mu)*rm_ad(m)-rm_ad(L))
106    pos_bad = np.logical_or(pos_bad,np.any(v<0,axis=0))
107    
108    result = mu
109    result[pos_bad] = -np.inf
110    
111    # Solve the lower dimensional sub-problems
112    # We could restrict to the bad positions, and avoid repeating computations
113    for i in range(dim):             
114        axes = np.full((dim),True); axes[i]=False
115        res = ConstrainedMaximize(Q[axes][:,axes],l[axes],m[axes])
116        result = np.maximum(result,res)
117    return result
def SchemeUniform(u, SB, f, bc):
119def SchemeUniform(u,SB,f,bc):
120    # Compute the finite differences along the superbase directions
121    d2u = bc.Diff2(u,SB) 
122    d2u[...,bc.not_interior] = 0. # Placeholder value to silent NaN warnings
123    
124    # Generate the parameters for the low dimensional optimization problem
125    Q = 0.5*np.array([[0,1,1],[1,0,1],[1,1,0]])
126    l = -d2u
127    m = lp.dot_VV(SB,SB)
128    
129    # Evaluate the numerical scheme
130    m = bc.as_field(m)
131    from agd.FiniteDifferences import as_field
132    Q = as_field(Q,m.shape[1:])
133    
134    dim = 2
135    alpha = dim * f**(1/dim)
136    mask= (alpha==0)
137
138    Q = Q* np.where(mask,1.,alpha**2)
139    residue = ConstrainedMaximize(Q,l,m).max(axis=0)
140    residue[mask] = np.max(l/m,axis=0).max(axis=0)[mask]
141    
142    # Boundary conditions
143    return np.where(bc.interior,residue,u-bc.grid_values)
def SchemeUniform_OptInner(u, SB, f, bc, oracle=None):
145def SchemeUniform_OptInner(u,SB,f,bc,oracle=None):
146    # Use the oracle, if available, to select the active superbases only
147    if not(oracle is None):
148        SB = np.take_along_axis(SB,np.broadcast_to(oracle,SB.shape[:2]+(1,)+oracle.shape),axis=2)
149
150    d2u = bc.Diff2(u,SB) 
151    d2u[...,bc.not_interior] = 0. # Placeholder value to silent NaN warnings
152    
153    # Generate the parameters for the low dimensional optimization problem
154    Q = 0.5*np.array([[0,1,1],[1,0,1],[1,1,0]])
155    dim = 2
156    l = -d2u
157    m = lp.dot_VV(SB,SB)
158    
159    m = bc.as_field(m)
160    from agd.FiniteDifferences import as_field
161    Q = as_field(Q,m.shape[1:])
162    
163    dim = 2
164    alpha = dim * f**(1/dim)
165    mask= (alpha==0)
166
167    Q = Q* np.where(mask,1.,alpha**2)
168    # Evaluate the non-linear functional using dense-sparse composition
169    residue = ad.apply(ConstrainedMaximize,Q,l,m,shape_bound=u.shape).copy()
170    residue[:,mask] = np.max(l/m,axis=0)[:,mask]
171    
172    return ad.max_argmax(residue,axis=0)
def SchemeUniform_Opt(u, SB, f, bc):
174def SchemeUniform_Opt(u,SB,f,bc):
175    
176    # Evaluate the maximum over the superbases using the envelope theorem
177    residue,_ = ad.apply(SchemeUniform_OptInner, u,bc.as_field(SB),f,bc, envelope=True)
178    
179    return np.where(bc.interior,residue,u-bc.grid_values)
def Hessian_ad(u, x):
181def Hessian_ad(u,x):
182    x_ad = ad.Dense2.identity(constant=x,shape_free=(2,))
183    return u(x_ad).hessian()
def MongeAmpere_ad(u, x):
184def MongeAmpere_ad(u,x):
185    return lp.det(Hessian_ad(u,x))