1+ from PEPit import PEP
2+ from PEPit .functions import BlockConvexConcaveFunction
3+ from PEPit .point import Point
4+ from PEPit .expression import Expression
5+
6+
7+ def wc_proximal_point (gamma , n , wrapper = "cvxpy" , solver = None , verbose = 1 ):
8+ """
9+ Consider the minimization problem
10+
11+ .. math:: f_\\ star \\ triangleq \\ min_x f(x),
12+
13+ where :math:`f` is closed, proper, and convex (and potentially non-smooth).
14+
15+ This code computes a worst-case guarantee for the **proximal point method** with step-size :math:`\\ gamma`.
16+ That is, it computes the smallest possible :math:`\\ tau(n,\\ gamma)` such that the guarantee
17+
18+ .. math:: f(x_n) - f_\\ star \\ leqslant \\ tau(n, \\ gamma) \\ |x_0 - x_\\ star\\ |^2
19+
20+ is valid, where :math:`x_n` is the output of the proximal point method, and where :math:`x_\\ star` is a
21+ minimizer of :math:`f`.
22+
23+ In short, for given values of :math:`n` and :math:`\\ gamma`,
24+ :math:`\\ tau(n,\\ gamma)` is computed as the worst-case value of :math:`f(x_n)-f_\\ star`
25+ when :math:`\\ |x_0 - x_\\ star\\ |^2 \\ leqslant 1`.
26+
27+ **Algorithm**:
28+
29+ The proximal point method is described by
30+
31+ .. math:: x_{t+1} = \\ arg\\ min_x \\ left\\ {f(x)+\\ frac{1}{2\gamma}\\ |x-x_t\\ |^2 \\ right\\ },
32+
33+ where :math:`\\ gamma` is a step-size.
34+
35+ **Theoretical guarantee**:
36+
37+ The **tight** theoretical guarantee can be found in [1, Theorem 4.1]:
38+
39+ .. math:: f(x_n)-f_\\ star \\ leqslant \\ frac{\\ |x_0-x_\\ star\\ |^2}{4\\ gamma n},
40+
41+ where tightness is obtained on, e.g., one-dimensional linear problems on the positive orthant.
42+
43+ **References**:
44+
45+ `[1] A. Taylor, J. Hendrickx, F. Glineur (2017).
46+ Exact worst-case performance of first-order methods for composite convex optimization.
47+ SIAM Journal on Optimization, 27(3):1283–1313.
48+ <https://arxiv.org/pdf/1512.07516.pdf>`_
49+
50+ Args:
51+ gamma (float): step-size.
52+ n (int): number of iterations.
53+ wrapper (str): the name of the wrapper to be used.
54+ solver (str): the name of the solver the wrapper should use.
55+ verbose (int): level of information details to print.
56+
57+ - -1: No verbose at all.
58+ - 0: This example's output.
59+ - 1: This example's output + PEPit information.
60+ - 2: This example's output + PEPit information + solver details.
61+
62+ Returns:
63+ pepit_tau (float): worst-case value
64+ theoretical_tau (float): theoretical value
65+
66+ Example:
67+ >>> pepit_tau, theoretical_tau = wc_proximal_point(gamma=3, n=4, wrapper="cvxpy", solver=None, verbose=1)
68+ (PEPit) Setting up the problem: size of the Gram matrix: 6x6
69+ (PEPit) Setting up the problem: performance measure is the minimum of 1 element(s)
70+ (PEPit) Setting up the problem: Adding initial conditions and general constraints ...
71+ (PEPit) Setting up the problem: initial conditions and general constraints (1 constraint(s) added)
72+ (PEPit) Setting up the problem: interpolation conditions for 1 function(s)
73+ Function 1 : Adding 20 scalar constraint(s) ...
74+ Function 1 : 20 scalar constraint(s) added
75+ (PEPit) Setting up the problem: additional constraints for 0 function(s)
76+ (PEPit) Compiling SDP
77+ (PEPit) Calling SDP solver
78+ (PEPit) Solver status: optimal (wrapper:cvxpy, solver: MOSEK); optimal value: 0.020833335685730252
79+ (PEPit) Primal feasibility check:
80+ The solver found a Gram matrix that is positive semi-definite up to an error of 3.626659005644299e-09
81+ All the primal scalar constraints are verified up to an error of 1.1386158081487519e-08
82+ (PEPit) Dual feasibility check:
83+ The solver found a residual matrix that is positive semi-definite
84+ All the dual scalar values associated with inequality constraints are nonnegative
85+ (PEPit) The worst-case guarantee proof is perfectly reconstituted up to an error of 3.0464297827437203e-08
86+ (PEPit) Final upper bound (dual): 0.020833337068527292 and lower bound (primal example): 0.020833335685730252
87+ (PEPit) Duality gap: absolute: 1.382797040067052e-09 and relative: 6.637425042856655e-08
88+ *** Example file: worst-case performance of proximal point method ***
89+ PEPit guarantee: f(x_n)-f_* <= 0.0208333 ||x_0 - x_*||^2
90+ Theoretical guarantee: f(x_n)-f_* <= 0.0208333 ||x_0 - x_*||^2
91+
92+ """
93+
94+ # Instantiate PEP
95+ problem = PEP ()
96+
97+ # Declare a convex function
98+
99+ block_partition = problem .declare_block_partition (d = 2 )
100+ func = problem .declare_function (function_class = BlockConvexConcaveFunction , partition = block_partition )
101+ # Start by defining its unique optimal point xs = x_* and corresponding function value fs = f_*
102+ zs = func .stationary_point ()
103+ fs = func (zs )
104+
105+ # Then define the starting point x0 of the algorithm
106+ z0 = problem .set_initial_point ()
107+
108+ # Set the initial constraint that is the distance between x0 and x^*
109+ problem .set_initial_condition ((z0 - zs ) ** 2 <= 1 )
110+
111+ # Run n steps of the proximal point method
112+ z = z0
113+ z_avg = z
114+ count = 1
115+ for _ in range (n ):
116+
117+
118+ # Compute x from the docstring equation.
119+ gz = Point ()
120+ fz = Expression ()
121+ z = z - gamma * block_partition .get_block (gz , 0 )
122+ z = z + gamma * block_partition .get_block (gz , 1 )
123+ z_avg += z
124+ count += 1
125+ # Add point to Function f.
126+ func .add_point ((z , gz , fz ))
127+
128+ z_avg /= count
129+ #func.add_point((z_avg, func.gradient(z_avg), func(z_avg)))
130+ # Set the performance metric to the final distance to optimum in function values
131+ problem .set_performance_metric (func (z_avg )- fs )
132+
133+ # Solve the PEP
134+ pepit_verbose = max (verbose , 0 )
135+ pepit_tau = problem .solve (wrapper = wrapper , solver = solver , verbose = pepit_verbose )
136+
137+ # Compute theoretical guarantee (for comparison)
138+ theoretical_tau = 1 / (gamma * n )
139+
140+ # Print conclusion if required
141+ if verbose != - 1 :
142+ print ('*** Example file: worst-case performance of proximal point method ***' )
143+ print ('\t PEPit guarantee:\t f(z_avg)-f_* <= {:.6} ||z_0 - z_*||^2' .format (pepit_tau ))
144+ print ('\t Theoretical guarantee:\t f(z_avg)-f_* <= {:.6} ||z_0 - z_*||^2' .format (theoretical_tau ))
145+
146+ # Return the worst-case guarantee of the evaluated method (and the reference theoretical value)
147+ return pepit_tau , theoretical_tau
148+
149+
150+ if __name__ == "__main__" :
151+ pepit_tau , theoretical_tau = wc_proximal_point (gamma = 0.2 , n = 10 , wrapper = "cvxpy" , solver = None , verbose = 1 )
0 commit comments