samer@0
|
1 /*
|
samer@0
|
2 * Copyright (c) 2000, Samer Abdallah, King's College London.
|
samer@0
|
3 * All rights reserved.
|
samer@0
|
4 *
|
samer@0
|
5 * This software is provided AS iS and WITHOUT ANY WARRANTY;
|
samer@0
|
6 * without even the implied warranty of MERCHANTABILITY or
|
samer@0
|
7 * FITNESS FOR A PARTICULAR PURPOSE.
|
samer@0
|
8 */
|
samer@0
|
9
|
samer@0
|
10 package samer.maths.opt;
|
samer@0
|
11 import samer.maths.*;
|
samer@0
|
12 import samer.core.*;
|
samer@0
|
13 import samer.core.types.*;
|
samer@0
|
14 import java.util.*;
|
samer@0
|
15
|
samer@0
|
16 /**
|
samer@0
|
17 Constraints and line minimisations for functions with gradient
|
samer@0
|
18 discontinuity at co-ordinate zeros (hyperplanes).
|
samer@0
|
19 The ZeroCrossingSparsity class handles this by checking for
|
samer@0
|
20 zero crossings during the line search. If a gradient
|
samer@0
|
21 discontinuity causes a change of sign in the gradient,
|
samer@0
|
22 that dimension is constrained and becomes inactive (drops
|
samer@0
|
23 out of minimisation). It can later be reactivated if
|
samer@0
|
24 the algorithm determines that the gradient no longer
|
samer@0
|
25 changes sign at that point.
|
samer@0
|
26 */
|
samer@0
|
27
|
samer@0
|
28 public class ZeroCrossingSparsity extends Constraints
|
samer@0
|
29 {
|
samer@0
|
30 public double alphas[];
|
samer@0
|
31 public int crossings[];
|
samer@0
|
32 public int numcrossings;
|
samer@0
|
33 public int clipped=0;
|
samer@0
|
34 private int released, releasable;
|
samer@0
|
35 private double XEPS=1e-6, GEPS=1e-4;
|
samer@0
|
36 private double GTHRESH=1;
|
samer@0
|
37
|
samer@0
|
38 public static Factory factory() {
|
samer@0
|
39 return new Factory() {
|
samer@0
|
40 public Constraints build(MinimiserBase minimiser) {
|
samer@0
|
41 return new ZeroCrossingSparsity(minimiser);
|
samer@0
|
42 }
|
samer@0
|
43 };
|
samer@0
|
44 }
|
samer@0
|
45
|
samer@0
|
46 public ZeroCrossingSparsity(MinimiserBase minimiser)
|
samer@0
|
47 {
|
samer@0
|
48 super(minimiser);
|
samer@0
|
49 crossings=new int[n];
|
samer@0
|
50 alphas=new double[n];
|
samer@0
|
51
|
samer@0
|
52 minimiser.add(new VParameter("XEPS", new DoubleModel() {
|
samer@0
|
53 public void set(double t) { XEPS=t; }
|
samer@0
|
54 public double get() { return XEPS; }
|
samer@0
|
55 } )
|
samer@0
|
56 );
|
samer@0
|
57
|
samer@0
|
58 minimiser.add(new VParameter("GEPS", new DoubleModel() {
|
samer@0
|
59 public void set(double t) { GEPS=t; }
|
samer@0
|
60 public double get() { return GEPS; }
|
samer@0
|
61 } )
|
samer@0
|
62 );
|
samer@0
|
63
|
samer@0
|
64 try {
|
samer@0
|
65 final Object jump=Shell.get("ZeroCrossingSparsity.jump");
|
samer@0
|
66 if (jump instanceof Double) {
|
samer@0
|
67 GTHRESH=((Double)jump).doubleValue();
|
samer@0
|
68 Shell.print("Gradient jump="+GTHRESH);
|
samer@0
|
69 } else if (jump instanceof VDouble) {
|
samer@0
|
70 GTHRESH=((VDouble)jump).value;
|
samer@0
|
71 ((VDouble)jump).addObserver( new Observer() {
|
samer@0
|
72 public void update(Observable o, Object arg) {
|
samer@0
|
73 GTHRESH=((VDouble)jump).value;
|
samer@0
|
74 }
|
samer@0
|
75 } );
|
samer@0
|
76 Shell.print("Gradient jump in VDouble="+GTHRESH);
|
samer@0
|
77 }
|
samer@0
|
78 } catch (Exception ex) {
|
samer@0
|
79 Shell.print("*** no jump object found! ***");
|
samer@0
|
80 Shell.print(ex.toString());
|
samer@0
|
81 ex.printStackTrace();
|
samer@0
|
82 }
|
samer@0
|
83 }
|
samer@0
|
84
|
samer@0
|
85 public void setGThreshold(double gt) { GTHRESH=gt; }
|
samer@0
|
86
|
samer@0
|
87 /** constrain all dimensions which are currently set
|
samer@0
|
88 to zero.
|
samer@0
|
89 */
|
samer@0
|
90
|
samer@0
|
91 public void initialise()
|
samer@0
|
92 {
|
samer@0
|
93 int p=0, q=0;
|
samer@0
|
94
|
samer@0
|
95 for (int i=0; i<n; i++) {
|
samer@0
|
96 if (S.P1.x[i]==0) inactive[p++]=i;
|
samer@0
|
97 else active[q++]=i;
|
samer@0
|
98 }
|
samer@0
|
99 m=q;
|
samer@0
|
100 }
|
samer@0
|
101
|
samer@0
|
102
|
samer@0
|
103 /** find all the zero crossings */
|
samer@0
|
104
|
samer@0
|
105 private boolean check()
|
samer@0
|
106 {
|
samer@0
|
107 // got to find sign swappers
|
samer@0
|
108 numcrossings=0;
|
samer@0
|
109 for (int j=0; j<m; j++) {
|
samer@0
|
110 int i=active[j];
|
samer@0
|
111 if ( (S.P1.x[i]>XEPS && S.P2.x[i]<XEPS)
|
samer@0
|
112 || (S.P2.x[i]>-XEPS && S.P1.x[i]<-XEPS) ) {
|
samer@0
|
113 crossings[numcrossings]=i;
|
samer@0
|
114 alphas[numcrossings]=-S.P1.x[i]/S.h[i];
|
samer@0
|
115 numcrossings++;
|
samer@0
|
116 }
|
samer@0
|
117 }
|
samer@0
|
118 return numcrossings>0;
|
samer@0
|
119 }
|
samer@0
|
120
|
samer@0
|
121 public void report()
|
samer@0
|
122 {
|
samer@0
|
123 if (numcrossings>0) {
|
samer@0
|
124 Shell.trace("crossings: "+numcrossings);
|
samer@0
|
125 for (int i=0; i<numcrossings; i++) {
|
samer@0
|
126 Shell.trace("dim: "+crossings[i]+" alpha: "+alphas[i]);
|
samer@0
|
127 }
|
samer@0
|
128 }
|
samer@0
|
129 super.report();
|
samer@0
|
130 }
|
samer@0
|
131
|
samer@0
|
132
|
samer@0
|
133 /**
|
samer@0
|
134 This finds the constrained dimension with the
|
samer@0
|
135 largest gradient, and if that absolute value of
|
samer@0
|
136 that gradient is bigger than 1, then releases
|
samer@0
|
137 that dimension so that it can take part in the
|
samer@0
|
138 optimization
|
samer@0
|
139 */
|
samer@0
|
140
|
samer@0
|
141 public boolean release(Datum P)
|
samer@0
|
142 {
|
samer@0
|
143 if (m==n) return false; // all active
|
samer@0
|
144
|
samer@0
|
145 int imax=-1;
|
samer@0
|
146 double grad=GTHRESH+GEPS;
|
samer@0
|
147
|
samer@0
|
148 releasable=0;
|
samer@0
|
149 for (int k=0; k<(n-m); k++) {
|
samer@0
|
150 int j=inactive[k];
|
samer@0
|
151 if (Math.abs(P.g[j])>grad) {
|
samer@0
|
152 releasable++;
|
samer@0
|
153 grad=Math.abs(P.g[j]);
|
samer@0
|
154 imax=j;
|
samer@0
|
155 }
|
samer@0
|
156 }
|
samer@0
|
157
|
samer@0
|
158 if (imax!=-1) {
|
samer@0
|
159 activate(imax); released=imax;
|
samer@0
|
160 return true;
|
samer@0
|
161 }
|
samer@0
|
162 else return false;
|
samer@0
|
163 }
|
samer@0
|
164
|
samer@0
|
165 public int getReleasedDimension() { return released; }
|
samer@0
|
166 public int getReleasableDimensions() { return releasable; }
|
samer@0
|
167
|
samer@0
|
168 public void lineSearch(Condition convergence, CubicLineSearch ls, double beta0)
|
samer@0
|
169 {
|
samer@0
|
170 boolean zcdone=false;
|
samer@0
|
171
|
samer@0
|
172 step(beta0);
|
samer@0
|
173 eval2();
|
samer@0
|
174
|
samer@0
|
175 while (!convergence.test()) {
|
samer@0
|
176
|
samer@0
|
177 // formulate any other direction change conditions
|
samer@0
|
178
|
samer@0
|
179 if (S.P2.f<S.P1.f && S.P2.s<0) {
|
samer@0
|
180 double beta=ls.extrapolate();
|
samer@0
|
181 S.move();
|
samer@0
|
182 step(beta);
|
samer@0
|
183 eval2();
|
samer@0
|
184 zcdone=false;
|
samer@0
|
185 } else {
|
samer@0
|
186 /* couple of options here:
|
samer@0
|
187 we could check all the zero crossings in one go.
|
samer@0
|
188 if we find one that needs constraining, then
|
samer@0
|
189 we're done.
|
samer@0
|
190 otherwise, we don't need to check zero crossings
|
samer@0
|
191 again unless we extrapolate
|
samer@0
|
192
|
samer@0
|
193 other option is to backtrack one zero crossing
|
samer@0
|
194 at a time
|
samer@0
|
195
|
samer@0
|
196 OR find zero crossing just after (or before)
|
samer@0
|
197 expected minimum
|
samer@0
|
198
|
samer@0
|
199 */
|
samer@0
|
200
|
samer@0
|
201 // get cubic step
|
samer@0
|
202 double interp = ls.interpolate();
|
samer@0
|
203
|
samer@0
|
204 if (!zcdone) { check(); zcdone=true; }
|
samer@0
|
205
|
samer@0
|
206 // try to pick a good step length
|
samer@0
|
207 // bearing in mind the zero crossings we have
|
samer@0
|
208 if (numcrossings>0) {
|
samer@0
|
209
|
samer@0
|
210 // we are going to look at the alphas, and
|
samer@0
|
211 // find the smallest one that is between
|
samer@0
|
212 // interp and current alpha
|
samer@0
|
213
|
samer@0
|
214 int best=-1;
|
samer@0
|
215 double bestAlpha=S.alpha;
|
samer@0
|
216
|
samer@0
|
217 for (int k=0; k<numcrossings; k++) {
|
samer@0
|
218 double beta=alphas[k];
|
samer@0
|
219 if (beta>=interp && beta<bestAlpha) {
|
samer@0
|
220 best=k; bestAlpha=beta;
|
samer@0
|
221 }
|
samer@0
|
222 }
|
samer@0
|
223
|
samer@0
|
224 if (best!=-1) {
|
samer@0
|
225 int j=crossings[best];
|
samer@0
|
226
|
samer@0
|
227 // kth zero crossing of jth dimension
|
samer@0
|
228 step(bestAlpha);
|
samer@0
|
229 S.P2.x[j]=0;
|
samer@0
|
230 eval2();
|
samer@0
|
231 if (Math.abs(S.P2.g[j])<=GTHRESH-GEPS) {
|
samer@0
|
232 inactivate(j);
|
samer@0
|
233 // Shell.trace("- inactivating: "+j);
|
samer@0
|
234 return;
|
samer@0
|
235
|
samer@0
|
236 // what if SEVERAL dimensions
|
samer@0
|
237 // end up close to zero:
|
samer@0
|
238 // should we constrain them all?
|
samer@0
|
239 } else {
|
samer@0
|
240 // Shell.trace("truncating");
|
samer@0
|
241 // what?
|
samer@0
|
242 // should we stick with this point, or
|
samer@0
|
243 // move to interp?
|
samer@0
|
244 continue;
|
samer@0
|
245 }
|
samer@0
|
246 } // else fall through to ordinary step
|
samer@0
|
247 }
|
samer@0
|
248
|
samer@0
|
249 // no crossings, take ordinary step
|
samer@0
|
250 step(interp);
|
samer@0
|
251 eval2();
|
samer@0
|
252 }
|
samer@0
|
253 }
|
samer@0
|
254 }
|
samer@0
|
255 }
|
samer@0
|
256
|