Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/optimize/_lsq/bvls.py: 5%
117 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-12 06:31 +0000
1"""Bounded-variable least-squares algorithm."""
2import numpy as np
3from numpy.linalg import norm, lstsq
4from scipy.optimize import OptimizeResult
6from .common import print_header_linear, print_iteration_linear
9def compute_kkt_optimality(g, on_bound):
10 """Compute the maximum violation of KKT conditions."""
11 g_kkt = g * on_bound
12 free_set = on_bound == 0
13 g_kkt[free_set] = np.abs(g[free_set])
14 return np.max(g_kkt)
17def bvls(A, b, x_lsq, lb, ub, tol, max_iter, verbose, rcond=None):
18 m, n = A.shape
20 x = x_lsq.copy()
21 on_bound = np.zeros(n)
23 mask = x <= lb
24 x[mask] = lb[mask]
25 on_bound[mask] = -1
27 mask = x >= ub
28 x[mask] = ub[mask]
29 on_bound[mask] = 1
31 free_set = on_bound == 0
32 active_set = ~free_set
33 free_set, = np.nonzero(free_set)
35 r = A.dot(x) - b
36 cost = 0.5 * np.dot(r, r)
37 initial_cost = cost
38 g = A.T.dot(r)
40 cost_change = None
41 step_norm = None
42 iteration = 0
44 if verbose == 2:
45 print_header_linear()
47 # This is the initialization loop. The requirement is that the
48 # least-squares solution on free variables is feasible before BVLS starts.
49 # One possible initialization is to set all variables to lower or upper
50 # bounds, but many iterations may be required from this state later on.
51 # The implemented ad-hoc procedure which intuitively should give a better
52 # initial state: find the least-squares solution on current free variables,
53 # if its feasible then stop, otherwise, set violating variables to
54 # corresponding bounds and continue on the reduced set of free variables.
56 while free_set.size > 0:
57 if verbose == 2:
58 optimality = compute_kkt_optimality(g, on_bound)
59 print_iteration_linear(iteration, cost, cost_change, step_norm,
60 optimality)
62 iteration += 1
63 x_free_old = x[free_set].copy()
65 A_free = A[:, free_set]
66 b_free = b - A.dot(x * active_set)
67 z = lstsq(A_free, b_free, rcond=rcond)[0]
69 lbv = z < lb[free_set]
70 ubv = z > ub[free_set]
71 v = lbv | ubv
73 if np.any(lbv):
74 ind = free_set[lbv]
75 x[ind] = lb[ind]
76 active_set[ind] = True
77 on_bound[ind] = -1
79 if np.any(ubv):
80 ind = free_set[ubv]
81 x[ind] = ub[ind]
82 active_set[ind] = True
83 on_bound[ind] = 1
85 ind = free_set[~v]
86 x[ind] = z[~v]
88 r = A.dot(x) - b
89 cost_new = 0.5 * np.dot(r, r)
90 cost_change = cost - cost_new
91 cost = cost_new
92 g = A.T.dot(r)
93 step_norm = norm(x[free_set] - x_free_old)
95 if np.any(v):
96 free_set = free_set[~v]
97 else:
98 break
100 if max_iter is None:
101 max_iter = n
102 max_iter += iteration
104 termination_status = None
106 # Main BVLS loop.
108 optimality = compute_kkt_optimality(g, on_bound)
109 for iteration in range(iteration, max_iter): # BVLS Loop A
110 if verbose == 2:
111 print_iteration_linear(iteration, cost, cost_change,
112 step_norm, optimality)
114 if optimality < tol:
115 termination_status = 1
117 if termination_status is not None:
118 break
120 move_to_free = np.argmax(g * on_bound)
121 on_bound[move_to_free] = 0
123 while True: # BVLS Loop B
125 free_set = on_bound == 0
126 active_set = ~free_set
127 free_set, = np.nonzero(free_set)
129 x_free = x[free_set]
130 x_free_old = x_free.copy()
131 lb_free = lb[free_set]
132 ub_free = ub[free_set]
134 A_free = A[:, free_set]
135 b_free = b - A.dot(x * active_set)
136 z = lstsq(A_free, b_free, rcond=rcond)[0]
138 lbv, = np.nonzero(z < lb_free)
139 ubv, = np.nonzero(z > ub_free)
140 v = np.hstack((lbv, ubv))
142 if v.size > 0:
143 alphas = np.hstack((
144 lb_free[lbv] - x_free[lbv],
145 ub_free[ubv] - x_free[ubv])) / (z[v] - x_free[v])
147 i = np.argmin(alphas)
148 i_free = v[i]
149 alpha = alphas[i]
151 x_free *= 1 - alpha
152 x_free += alpha * z
153 x[free_set] = x_free
155 if i < lbv.size:
156 on_bound[free_set[i_free]] = -1
157 else:
158 on_bound[free_set[i_free]] = 1
159 else:
160 x_free = z
161 x[free_set] = x_free
162 break
164 step_norm = norm(x_free - x_free_old)
166 r = A.dot(x) - b
167 cost_new = 0.5 * np.dot(r, r)
168 cost_change = cost - cost_new
170 if cost_change < tol * cost:
171 termination_status = 2
172 cost = cost_new
174 g = A.T.dot(r)
175 optimality = compute_kkt_optimality(g, on_bound)
177 if termination_status is None:
178 termination_status = 0
180 return OptimizeResult(
181 x=x, fun=r, cost=cost, optimality=optimality, active_mask=on_bound,
182 nit=iteration + 1, status=termination_status,
183 initial_cost=initial_cost)