1 /*  _________________________________________________________________________
2  *
3  *  Acro: A Common Repository for Optimizers
4  *  Copyright (c) 2008 Sandia Corporation.
5  *  This software is distributed under the BSD License.
6  *  Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
7  *  the U.S. Government retains certain rights in this software.
8  *  For more information, see the README.txt file in the top Acro directory.
9  *  _________________________________________________________________________
10  */
11 //
12 // Coliny_DockingPS.cpp
13 //
14 
15 #include <acro_config.h>
16 
17 #include <scolib/DockingPS.h>
18 
19 #include <colin/SolverMngr.h>
20 
21 #define M_TWO_PI   (2.0*M_PI)
22 #define TESTING
23 
24 using namespace std;
25 using namespace utilib;
26 
27 namespace scolib {
28 
29 namespace {
30 
renormalize(double & nx,double & ny,double & nz)31 void renormalize(double& nx, double& ny, double& nz)
32 {
33 double norm = sqrt(nx*nx + ny*ny + nz*nz);
34 nx /= norm;
35 ny /= norm;
36 nz /= norm;
37 }
38 
compute_z(double x,double y)39 double compute_z(double x, double y)
40 {
41 double tmp = 1.0 - x*x - y*y;
42 if (tmp < -1e-7)
43    EXCEPTION_MNGR(runtime_error, "Bad x/y values!");
44 return sqrt(max(0.0,tmp));
45 }
46 
47 
48 }
49 
DockingPS()50 DockingPS::DockingPS()
51 {
52 num_rotations=4;
53 last_x=last_y=last_theta=0.0;
54 properties.privilegedGet("expansion_factor").set_readonly();
55 initial_angle=80.0;
56 
57 reset_signal.connect(boost::bind(&DockingPS::reset_DockingPS, this));
58 }
59 
60 
61 
reset_DockingPS()62 void DockingPS::reset_DockingPS()
63 {
64 if ( problem.empty() ) return;
65 
66 if (Delta_init > 1.0)
67    Delta_init = 1.0;
68 
69 basis_str = "coordinate";
70 
71 ntrials = ncore_trials = 2*problem->num_real_vars.as<size_t>()
72    - 4 + num_rotations;
73 ex_factor=1.0;
74 }
75 
76 
77 
generate_trial(int id,const DoubleVector & x_,DoubleVector & trial,double scale,bool & feasible,const DoubleVector & _bias)78 void DockingPS::generate_trial(int id, const DoubleVector& x_,
79 				   DoubleVector& trial, double scale,
80 				   bool& feasible, const DoubleVector& _bias)
81 {
82 //
83 // Initial setup
84 //
85 feasible=true;
86 if (debug > 4) {
87    ucout << "Current Point:   " << x_ << endl;
88    }
89 //
90 // The index of the dimension that is being changed
91 //     0    - 2		positive steps in the positional parameters
92 //     3    - n-4	positive steps in the torsion angles
93 //     n-3  - n-1	negative steps in the positional parameters
94 //     n    - 2n-7	negative steps in the torsion angles
95 //     2n-6 - 2n-5      positive/negative steps in rotations
96 //     2n-4 - 2n-4+k    k rotations of quarternion angles
97 //
98 size_type curr = ndx[id];
99 size_type n    = x_.size();
100 trial << x_;
101 
102 if (curr <= 2) {
103    trial[curr] += scale*Sigma[curr];
104    if (trial[curr] > upper_bc[curr])
105       feasible=false;
106    }
107 
108 else if (curr <= (n-4)) {
109    trial[curr+3] += scale*Sigma[curr+3];
110    if (trial[curr+3] > (2*upper_bc[curr+3]-lower_bc[curr+3]))
111       feasible=false;
112    }
113 
114 else if (curr <= (n-1)) {
115    trial[curr-n+3] -= scale*Sigma[curr-n+3];
116    if (trial[curr-n+3] < lower_bc[curr-n+3])
117       feasible=false;
118    }
119 
120 else if (curr <= (2*n-7)) {
121    trial[curr-n+6] -= scale*Sigma[curr-n+6];
122    if (trial[curr-n+6] < (2*lower_bc[curr-n+6]-upper_bc[curr-n+6]))
123       feasible=false;
124    if (trial[curr-n+6] < 0.0)
125       trial[curr-n+6] += M_TWO_PI;
126    }
127 
128 else if (curr == (2*n-6)) {
129    trial[5] -= scale*Sigma[5];
130    if (trial[5] < (2*lower_bc[5]-upper_bc[5]))
131       feasible=false;
132    if (trial[5] < 0.0)
133       trial[5] += M_TWO_PI;
134    }
135 
136 else if (curr == (2*n-5)) {
137    trial[5] += scale*Sigma[5];
138    if (trial[5] > (2*upper_bc[5]-lower_bc[5]))
139       feasible=false;
140    if (trial[5] > M_TWO_PI)
141       trial[5] -= M_TWO_PI;
142    }
143 
144 else {
145    if (scale != Delta_min)
146       EXCEPTION_MNGR(runtime_error, "BUG HERE");
147 
148       rotation_trial(min(scale,Delta_min), curr-(2*n-4), x_[3], x_[4], x_[5],
149 					trial[3], trial[4], trial[5]);
150 #if 0
151    else {
152       int tmp = curr-(2*n-4);
153       if (tmp == 0) {
154          trial[3] += scale*Sigma[3];
155          if (trial[3] > (2*upper_bc[3]-lower_bc[3]))
156             feasible=false;
157          }
158       else if (tmp == 1) {
159          trial[4] += scale*Sigma[4];
160          if (trial[4] > (2*upper_bc[4]-lower_bc[4]))
161             feasible=false;
162          }
163       else if (tmp == 2) {
164          trial[3] -= scale*Sigma[3];
165          if (trial[3] > lower_bc[3])
166             feasible=false;
167          }
168       else {
169          trial[4] -= scale*Sigma[4];
170          if (trial[4] > lower_bc[4])
171             feasible=false;
172          }
173       if (feasible && ((trial[3]*trial[3]+trial[4]*trial[4]) > 1.0)) {
174 	 double z=0.0;
175 	 renormalize(trial[3],trial[4],z);
176 	 }
177       }
178 #endif
179    }
180 
181 if (debug > 4) {
182    ucout << "Trial Point:   " << trial << endl;
183    ucout << "Feasible: " << feasible << endl;
184    }
185 if (debug > 4) {
186    double val=0.0;
187    for (unsigned int i=0; i<x_.size(); i++)
188      val += (trial[i]-x_[i])*(trial[i]-x_[i]);
189    ucout << "Trial Point Length: " << sqrt(val) << endl;
190    }
191 
192 //
193 // Collect running stats
194 //
195 if (output_level == 3) {
196    ntrial_points++;
197    if (feasible == true) nfeasible_points++;
198    }
199 }
200 
201 
202 
update_pattern(DoubleVector & prev_iter,DoubleVector & curr_iter,bool flag)203 void DockingPS::update_pattern(DoubleVector& prev_iter,
204                       		DoubleVector& curr_iter,
205                       		bool flag)
206 {
207 if (update_flag == 3)
208    EXCEPTION_MNGR(runtime_error, "DockingPS::update_pattern - bad step length update!");
209 
210 bool quaternion_diff = ((curr_iter[3] != prev_iter[3]) ||
211  			(curr_iter[4] != prev_iter[4]));
212 DEBUGPR(1000, ucout << "DockingPS::update_pattern - update=" << update_flag <<
213         " succ=" << flag <<
214         " quaternion-diff=" << quaternion_diff << endl);
215 //
216 // Don't update the pattern if the quaternion info hasn't changed.
217 // Update the angle if it is negative.
218 //
219 if (curr_iter[5] > M_TWO_PI)
220    EXCEPTION_MNGR(runtime_error, "BUG HERE 3");
221 if ((curr_iter[5] < 0.0) && !flag)
222    EXCEPTION_MNGR(runtime_error, "BUG HERE 4");
223 //
224 // After an unsuccessful iteration, we don't change the pattern orientation,
225 // but we _do_ reduce it in size
226 //
227 if (!flag) {
228    if (update_flag == 2) {
229       last_x = (prev_iter[3] + last_x)/2.0;
230       last_y = (prev_iter[4] + last_y)/2.0;
231       last_z = (last_z + compute_z(prev_iter[3],prev_iter[4]))/2.0;
232       renormalize(last_x,last_y,last_z);
233       last_theta = prev_iter[5];
234       }
235    else
236       EXCEPTION_MNGR(runtime_error, "BUG HERE 2");
237    return;
238    }
239 //
240 // This is a successful iteration, but don't change the quaternion unless
241 // it was the reason for the new iterate.
242 //
243 if (!quaternion_diff) {
244    return;
245    }
246 //
247 // After a successful iteration, last_x,y,z is the previous iteration.
248 //
249 last_x = prev_iter[3];
250 last_y = prev_iter[4];
251 last_z = compute_z(prev_iter[3],prev_iter[4]);
252 if (curr_iter[5] < 0.0) {
253    curr_iter[5] += M_TWO_PI;
254    last_x *= -1;
255    last_y *= -1;
256    last_z *= -1;
257    }
258 renormalize(last_x,last_y,last_z);
259 #if 0
260 WEH - do we need to rotate the pattern?????
261 
262 //
263 // After a successful iteration, we need to rotate the pattern.
264 // We may also need to rescale it larger, but we won't be doing that for now
265 //
266 //
267 // Compute a vector 'n' which is perpendicular to the previous iteration
268 // and current iteration
269 //
270 double nx,ny,nz;
271 double cz = sqrt(1-curr_iter[3]*curr_iter[3]-curr_iter[4]*curr_iter[4]);
272 if (flag && quaternion_diff) {
273   double pz = sqrt(1-prev_iter[3]*prev_iter[3]-prev_iter[4]*prev_iter[4]);
274 
275   nx = curr_iter[4]*pz           - cz*prev_iter[4];
276   ny = cz*prev_iter[3]           - curr_iter[3]*pz;
277   nz = curr_iter[3]*prev_iter[4] - curr_iter[4]*prev_iter[3];
278   }
279 else {
280   nx = curr_iter[4]*last_z - cz*last_y;
281   ny = cz*last_x           - curr_iter[3]*last_z;
282   nz = curr_iter[3]*last_y - curr_iter[4]*last_x;
283   }
284 renormalize(nx,ny,nz);
285 //
286 //
287 //
288 #if defined(TESTING)
289 double tmp = nx*prev_iter[3] +
290 	     ny*prev_iter[4] +
291 	     nz*sqrt(1-prev_iter[3]*prev_iter[3]-prev_iter[4]*prev_iter[4]);
292 if (fabs(tmp) > 1e-5)
293    EXCEPTION_MNGR(runtime_error, "The normal vector doesn't seem normal enough.");
294 if (fabs(min(nx,min(ny,nz))) < 1e-5)
295    EXCEPTION_MNGR(runtime_error, "The normal vector doesn't seem long enough.");
296 #endif
297 //
298 // Rotate the initial point M_PI/2 times Delta about n.
299 // This gives a reference value
300 // for a 'last point'.  Rotations of this 'last point' will define subsequent
301 // steps.
302 //
303 rotate(angle, curr_iter[3],  curr_iter[4], cz,
304                        nx,     ny,     nz,
305                        last_x, last_y, last_z);
306 #endif
307 //
308 // Misc testing
309 //
310 #if 0
311 #if defined(TESTING)
312 double angle = min(1.0, Sigma[3]*Delta)*M_PI/2.0;
313 if (curr_iter[5] < 0.0)
314    EXCEPTION_MNGR(runtime_error, "THIS CANNOT HAPPEN, RIGHT?");
315 double est_dist = 2*sin(angle/2.0);
316 double dist = sqrt((curr_iter[3]-last_x)*(curr_iter[3]-last_x) +
317                    (curr_iter[4]-last_y)*(curr_iter[4]-last_y) +
318                    (cz-last_z)*(cz-last_z));
319 DEBUGPR(1000, ucout << "UpdatePattern Test: angle=" << angle <<
320         " est=" << est_dist << " dist=" << dist << endl << Flush);
321 DEBUGPR(1000, ucout << "x=" << curr_iter[3] << " y=" << curr_iter[4]
322         << " z=" << cz << "  rx=" << last_x << " ry=" << last_y
323         << " rz=" << last_z << endl << Flush);
324 if (fabs(est_dist - dist) > Delta*1e-5)
325    EXCEPTION_MNGR(runtime_error,"The last_* data appears to be incorrect.");
326 #endif
327 #endif
328 }
329 
330 
331 
rotation_trial(double scale,int id,double x,double y,double theta,double & new_x,double & new_y,double & new_theta)332 void DockingPS::rotation_trial(double scale, int id,
333 			double x, double y, double theta,
334 			double& new_x, double& new_y, double& new_theta)
335 {
336 double z = compute_z(x,y);
337 double new_z;
338 if (id==0) {
339    new_x = last_x;
340    new_y = last_y;
341    new_z = last_z;
342    //new_z = sqrt(1-new_x*new_x-new_y*new_y);
343    }
344 else {
345    rotate( id*2*M_PI/num_rotations,
346 	last_x, last_y, last_z,
347 	x, y, z,
348 	new_x, new_y, new_z);
349    }
350 
351 
352 //
353 // Misc testing
354 //
355 #if defined(TESTING)
356 double angle = id*2*M_PI/num_rotations;
357 double est_dist = 2*sin(angle/2.0);
358 double dist = sqrt((x-new_x)*(x-new_x) +
359                    (y-new_y)*(y-new_y) +
360                    (z-new_z)*(z-new_z));
361 double dist2 = sqrt((last_x-new_x)*(last_x-new_x) +
362                    (last_y-new_y)*(last_y-new_y) +
363                    (last_z-new_z)*(last_z-new_z));
364 DEBUGPR(1000, ucout << "RotationTest: angle=" << angle << " est=" <<
365         est_dist << " dist=" << dist << " DIST=" << dist2 << endl << Flush);
366 DEBUGPR(1000, ucout << "x=" << x << " y=" << y << " z=" << z <<
367         "  rx=" << new_x << " ry=" << new_y << " rz=" << new_z <<
368         endl << Flush);
369 DEBUGPR(1000, ucout << "lx=" << last_x << " ly=" << last_y << " lz="
370         << last_z << "  rx=" << new_x << " ry=" << new_y << " rz="
371         << new_z << endl << Flush);
372 #if 0
373 if (fabs(est_dist - dist) > scale*1e-5)
374    EXCEPTION_MNGR(runtime_error,"The new_* data appears to be incorrect.");
375 #endif
376 #endif
377 
378 if (new_z < 0.0) {
379    new_x *= -1.0;
380    new_y *= -1.0;
381    new_z *= -1.0;
382    new_theta *= -1.0;
383    }
384 }
385 
386 
387 
388 //
389 // Rotate the 'p' vector about 'n', returning the value in 'r'
390 //
rotate(double angle,double px,double py,double pz,double nx,double ny,double nz,double & rx,double & ry,double & rz)391 void DockingPS::rotate(double angle,
392 			double  px, double  py, double  pz,
393 			double  nx, double  ny, double  nz,
394 			double& rx, double& ry, double& rz)
395 {
396 if (angle == 0.0) {
397    rx = px;
398    ry = py;
399    rz = pz;
400    return;
401    }
402 
403 double c = cos(angle);
404 double s = sin(angle);
405 double t = 1-c;
406 
407 #if 0
408 double p11 = t*nx*nx + c;
409 double p12 = t*nx*ny + s*nz;
410 double p13 = t*nx*nz + s*ny;
411 double p21 = t*nx*ny - s*nz;
412 double p22 = t*ny*ny + c;
413 double p23 = t*ny*nz + s*nz;
414 double p31 = t*nx*nz + s*ny;
415 double p32 = t*ny*nz - s*nx;
416 double p33 = t*nz*nz + c;
417 #endif
418 
419 double p11 = t*nx*nx + c;
420 double p21 = t*ny*nx + s*nz;
421 double p31 = t*nz*nx - s*ny;
422 double p12 = t*nx*ny - s*nz;
423 double p22 = t*ny*ny + c;
424 double p32 = t*nz*ny + s*nx;
425 double p13 = t*nx*nz + s*ny;
426 double p23 = t*ny*nz - s*nx;
427 double p33 = t*nz*nz + c;
428 
429 #if 0
430 rx = px*p11 + py*p12 + pz*p13;
431 ry = px*p21 + py*p22 + pz*p23;
432 rz = px*p31 + py*p32 + pz*p33;
433 #endif
434 rx = px*p11 + py*p21 + pz*p31;
435 ry = px*p12 + py*p22 + pz*p32;
436 rz = px*p13 + py*p23 + pz*p33;
437 }
438 
439 
440 
set_initial_point(const utilib::AnyRef & point)441 void DockingPS::set_initial_point(const utilib::AnyRef& point)
442 {
443 initial_point_flag=true;
444 utilib::TypeManager()->lexical_cast(point, initial_point);
445 //
446 // Compute an (x,y,z) vector that we assume is co-planar with 'initial_point'
447 //
448 double x=0.0;
449 double y=0.0;
450 double z=0.0;
451 double pz = compute_z(initial_point[3],initial_point[4]);
452 if (initial_point[3] < initial_point[4]) {
453    if (initial_point[3] < pz)
454       x = 1.0;
455    else
456       z = 1.0;
457    }
458 else {
459    if (initial_point[4] > pz)
460       z = 1.0;
461    else
462       y = 1.0;
463    }
464 //
465 // Compute a vector 'n' which is perpendicular to the initial point and
466 // a vector (x,y,z) defined along the largest dimension of the initial point
467 //
468 double nx = y*pz    - z*initial_point[4];
469 double ny = z*initial_point[3] - x*pz;
470 double nz = x*initial_point[4] - y*initial_point[3];
471 renormalize(nx,ny,nz);
472 //
473 // Rotate the initial point M_PI/2 times Delta.  This gives a reference value
474 // for a 'last point'.  Rotations of this 'last point' will define subsequent
475 // steps.
476 //
477 rotate((min(75.0,initial_angle)/90)*M_PI/2.0, initial_point[3],  initial_point[4],  pz,
478 		       nx,     ny,     nz,
479 		       last_x, last_y, last_z);
480 //
481 // Misc testing
482 //
483 #if defined(TESTING)
484 #if 0
485 double est_dist = 2*sin(angle/2.0);
486 double dist = sqrt((initial_point[3]-last_x)*(initial_point[3]-last_x) +
487                    (initial_point[4]-last_y)*(initial_point[4]-last_y) +
488                    (pz-last_z)*(pz-last_z));
489 if (fabs(est_dist - dist) > 1e-5)
490    EXCEPTION_MNGR(runtime_error,"The last_* data appears to be incorrect.");
491 #endif
492 #endif
493 }
494 
495 
496 // Defines scolib::StaticInitializers::DockingPS_bool
497 REGISTER_COLIN_SOLVER_WITH_ALIAS(DockingPS,"sco:DockingPS","sco:dockingps", "The SCO DockingPS optimizer")
498 
499 } // namespace scolib
500