1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 """Back-and-forth coordinate descent (CD) optimization.
25
26 This file is part of the U{minfx optimisation library<https://sourceforge.net/projects/minfx>}.
27 """
28
29
30 from numpy import dot, float64, identity
31
32
33 from minfx.base_classes import Line_search, Min
34
35
36 -def coordinate_descent(func=None, dfunc=None, args=(), x0=None, min_options=None, func_tol=1e-25, grad_tol=None, maxiter=1e6, a0=1.0, mu=0.0001, eta=0.1, full_output=0, print_flag=0, print_prefix=""):
37 """Back-and-forth coordinate descent minimisation."""
38
39 if print_flag:
40 if print_flag >= 2:
41 print(print_prefix)
42 print(print_prefix)
43 print(print_prefix + "Back-and-forth coordinate descent minimisation")
44 print(print_prefix + "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
45 min = Coordinate_descent(func, dfunc, args, x0, min_options, func_tol, grad_tol, maxiter, a0, mu, eta, full_output, print_flag, print_prefix)
46 if min.init_failure:
47 print(print_prefix + "Initialisation of minimisation has failed.")
48 return None
49 results = min.minimise()
50 return results
51
52
54 - def __init__(self, func, dfunc, args, x0, min_options, func_tol, grad_tol, maxiter, a0, mu, eta, full_output, print_flag, print_prefix):
55 """Class for back-and-forth coordinate descent minimisation specific functions.
56
57 Unless you know what you are doing, you should call the function 'coordinate_descent' rather than using this class.
58 """
59
60
61 self.func = func
62 self.dfunc = dfunc
63 self.args = args
64 self.xk = x0
65 self.func_tol = func_tol
66 self.grad_tol = grad_tol
67 self.maxiter = maxiter
68 self.full_output = full_output
69 self.print_flag = print_flag
70 self.print_prefix = print_prefix
71
72
73 self.a0 = a0
74
75
76 self.mu = mu
77 self.eta = eta
78
79
80 self.init_failure = 0
81
82
83 self.line_search_options(min_options)
84 self.setup_line_search()
85
86
87 self.f_count = 0
88 self.g_count = 0
89 self.h_count = 0
90
91
92 self.warning = None
93
94
95 self.setup_conv_tests()
96
97
98 self.fk, self.f_count = self.func(*(self.xk,)+self.args), self.f_count + 1
99 self.dfk, self.g_count = self.dfunc(*(self.xk,)+self.args), self.g_count + 1
100
101
102 self.cd_dir = identity(len(self.xk), float64)
103 self.n = 0
104 self.back = 0
105
106
108 """The new parameter function.
109
110 Find the search direction, do a line search, and get xk+1 and fk+1.
111 """
112
113
114 if dot(self.dfk, self.cd_dir[self.n]) > 0.0:
115 self.pk = -self.cd_dir[self.n]
116 else:
117 self.pk = self.cd_dir[self.n]
118
119
120 self.line_search()
121
122
123 self.xk_new = self.xk + self.alpha * self.pk
124 self.fk_new, self.f_count = self.func(*(self.xk_new,)+self.args), self.f_count + 1
125 self.dfk_new, self.g_count = self.dfunc(*(self.xk_new,)+self.args), self.g_count + 1
126
127
128 self.cd_dir[self.n] = self.alpha * self.pk
129
130
132 """Function to update the function value, gradient vector, and Hessian matrix."""
133
134
135 if not self.back:
136 if self.n < len(self.xk) - 1:
137 self.n = self.n + 1
138 else:
139 self.back = 1
140 self.n = self.n - 1
141 else:
142 if self.n > 0:
143 self.n = self.n - 1
144 else:
145 self.back = 0
146 self.n = self.n + 1
147 if self.print_flag >= 2:
148 print(self.print_prefix + "back_flag: " + repr(self.back))
149 print(self.print_prefix + "n: " + repr(self.n))
150
151
152 self.fk_last = self.fk
153 self.dfk_last = self.dfk * 1.0
154
155
156 self.xk = self.xk_new * 1.0
157 self.fk = self.fk_new
158 self.dfk = self.dfk_new * 1.0
159