Source code for arsenal.maths.optimize
from scipy.optimize import minimize
[docs]class NProx:
"""
Numerically estimate the proximal operator `Prox_{s*f}(x)` and related
concepts such as the Moreau envelope.
This implementation is not efficient and should only be used for testing
purposes.
https://web.stanford.edu/~boyd/papers/pdf/prox_algs.pdf
"""
def __init__(self, f, s):
self.f = f
self.s = s
[docs] def solve(self, x, y0=None):
return minimize(lambda y: self.objective(x, y), y0 if y0 is not None else x)
[docs] def objective(self, x, y):
d = (x - y)
return self.f(y) + 0.5/self.s * d @ d
[docs] def op(self, x, **kw):
return self.solve(x, **kw).x
[docs] def func(self, x, **kw):
"Proximally smoothed `f` with smoothing parameter `s > 0`."
return self.solve(x, **kw).fun
[docs] def grad_like(self, x, **kw):
"Gradient of `prox_func` wrt `x`."
return (x - self.op(x, **kw)) / self.s
[docs]class NProj:
def __init__(self, C, type):
self.C = C
self.type = type
self.constraints = [{
'fun': self.C,
'type': {'>=': 'ineq', '==': 'eq'}[self.type],
}]
[docs] def solve(self, x, y0=None):
sol = minimize(
lambda y: 0.5 * (y - x) @ (y - x),
y0 if y0 is not None else x,
constraints = self.constraints,
)
if not sol.success: print(sol.message)
return sol
[docs] def op(self, x, **kw):
return self.solve(x, **kw).x
[docs] def func(self, x, **kw):
return self.solve(x, **kw).fun
[docs] def grad_like(self, x, **kw):
"Gradient of `prox_func` wrt `x`."
return (x - self.op(x, **kw))
[docs]def is_subgradient(f, g, x):
"Numerically check whether or not `g` is a subgradient of `f` at `x`."
# Check the condition:
# for all y, f(y) - f(x) >= g @ (y - x).
# iff
# for all y, f(y) - g @ y >= f(x) - g @ x
# min_y f(y) - g @ y >= f(x) - g @ x
sol = minimize(lambda y: f(y) - g @ y, x)
return sol.fun >= f(x) - g @ x