mirror of
				https://github.com/cookiengineer/audacity
				synced 2025-10-31 14:13:50 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			188 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  *  hillclimb.cpp
 | |
|  *  scorealign
 | |
|  *
 | |
|  *  Created by Roger Dannenberg on 10/20/07.
 | |
|  *  Copyright 2007 __MyCompanyName__. All rights reserved.
 | |
|  *
 | |
|  * Hillclimb is an abstract class for optimization. It models problems where
 | |
|  * you have a vector of parameters (stored as an array), a corresponding set
 | |
|  * of step sizes, and a non-linear function. The function is a virtual
 | |
|  * member function that subclasses must implement.
 | |
|  *
 | |
|  * The optimization algorithm is as follows:
 | |
|  * An initial set of parameters and step sizes is given.
 | |
|  *
 | |
|  * Estimate the partial derivatives with respect to each parameter value
 | |
|  * by taking a step along that dimension (use step sizes to determine
 | |
|  * how far to go) and calling the evaluate virtual function.
 | |
|  * Find the parameter that causes the maximum absolute change. If the
 | |
|  * change is positive for that parameter, take the step along that
 | |
|  * dimension. If the change is negative, take a negative step along that
 | |
|  * dimension. 
 | |
|  *
 | |
|  * Repeat the previous paragraph as long as the result of evaluate is
 | |
|  * increasing. When it stops, you are at the top of a hill, a local
 | |
|  * maximum.
 | |
|  */
 | |
| 
 | |
| 
 | |
| #include "stdio.h"
 | |
| #include <stdlib.h>
 | |
| #include "sautils.h"
 | |
| #include "hillclimb.h"
 | |
| 
 | |
| #define HC_VERBOSE 0
 | |
| #define V if (HC_VERBOSE)
 | |
| 
 | |
| Hillclimb::~Hillclimb()
 | |
| {
 | |
|     if (parameters) FREE(parameters);
 | |
|     if (step_size)  FREE(step_size);
 | |
|     if (min_param)  FREE(min_param);
 | |
|     if (max_param)  FREE(max_param);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Hillclimb::setup(int n_) {
 | |
|     n = n_;
 | |
|     parameters = ALLOC(double, n);
 | |
|     step_size = ALLOC(double, n);
 | |
|     min_param = ALLOC(double, n);
 | |
|     max_param = ALLOC(double, n);
 | |
| }
 | |
| 
 | |
| 
 | |
| void Hillclimb::set_parameters(double *p, double *ss, 
 | |
|                                double *min_, double *max_, int plen)
 | |
| {
 | |
|     parameters = p;
 | |
|     step_size = ss;
 | |
|     min_param = min_;
 | |
|     max_param = max_;
 | |
|     n = plen;
 | |
| }
 | |
| 
 | |
| /* this optimize assumes that the surface is smooth enought that if the
 | |
|  * function decreases when parameter[i] increases, then the function will
 | |
|  * increase when parameter[i] decreases. The alternative version does more
 | |
|  * evaluation, but checks in both directions to find the best overall move.
 | |
| 
 | |
| double Hillclimb::optimize()
 | |
| {
 | |
|     double best = evaluate();
 | |
|     while (true) {
 | |
|         printf("best %g ", best);
 | |
|         // eval partial derivatives
 | |
|         int i;
 | |
|         // variables to search for max partial derivative
 | |
|         double max = 0; // max of |dy| so far
 | |
|         int max_i; // index where max was found
 | |
|         int max_sign = 1; // sign of dy
 | |
|         double max_y; // value of evaluate() at max_i
 | |
|         // now search over all parameters for max change
 | |
|         for (i = 0; i < n; i++) {
 | |
|             int sign = 1; // sign of derivative in the +step direction
 | |
|             int step_direction = 1; // how to undo parameter variation
 | |
|             parameters[i] += step_size[i];
 | |
|             if (parameters[i] > max_param[i]) {
 | |
|                 // try stepping in the other direction
 | |
|                 parameters[i] -= step_size[i] * 2;
 | |
|                 sign = -1;
 | |
|                 step_direction = -1;
 | |
|             }
 | |
|             
 | |
|             double y = evaluate();
 | |
|             // restore parameter i
 | |
|             parameters[i] -= step_size[i] * step_direction;
 | |
|             
 | |
|             double dy = y - best;
 | |
|             if (dy < 0) {
 | |
|                 dy = -dy;
 | |
|                 sign = -sign;
 | |
|             }
 | |
|             // is this the best yet and legal move?
 | |
|             double proposal = parameters[i] + step_size[i] * sign;
 | |
|             if (dy > max && proposal <= max_param[i] && 
 | |
|                 proposal >= min_param[i]) {
 | |
|                 max = dy;
 | |
|                 max_i = i;
 | |
|                 max_y = y;
 | |
|                 max_sign = sign;
 | |
|             }
 | |
|         }
 | |
|         // best move is parameter max_i in max_sign direction
 | |
|         parameters[max_i] += step_size[max_i] * max_sign;
 | |
|         printf("moved %d to %g", max_i, parameters[max_i]);
 | |
|         // what's the value now? put it in max_y
 | |
|         if (max_sign == -1) max_y = evaluate();
 | |
|         printf(" to get %g (vs. best %g)\n", max_y, best);
 | |
|         // otherwise, max_y already has the new value
 | |
|         if (max_y <= best) { // no improvement, we're done
 | |
|             parameters[max_i] -= step_size[max_i] * max_sign;
 | |
|             printf("\nCompleted hillclimbing, best %g\n", best);
 | |
|             return best;
 | |
|         }
 | |
|         // improvement because max_y higher than best:
 | |
|         best = max_y;
 | |
|     }
 | |
| }
 | |
| */
 | |
| 
 | |
| double Hillclimb::optimize(Report_fn_ptr report, void *cookie)
 | |
| {
 | |
|     double best = evaluate();
 | |
|     int iterations = 0;
 | |
|     while (true) {
 | |
|         (*report)(cookie, iterations, best);
 | |
|         V printf("best %g ", best);
 | |
|         // eval partial derivatives
 | |
|         int i;
 | |
|         // variables to search for max partial derivative
 | |
|         double max_y = best; // max of evaluate() so far
 | |
|         int max_i = 0; // index where best max was found
 | |
|         // the good parameter value for max_i:
 | |
|         double max_parameter = parameters[0]; 
 | |
|         // now search over all parameters for best improvement
 | |
|         for (i = 0; i < n; i++) {
 | |
|             V printf("optimize at %d param %g ", i, parameters[i]);
 | |
|             double save_param = parameters[i];
 | |
|             parameters[i] = save_param + step_size[i];
 | |
|             if (parameters[i] <= max_param[i]) {
 | |
|                 double y = evaluate();
 | |
|                 V printf("up->%g ", y);
 | |
|                 if (y > max_y) {
 | |
|                     V printf("NEW MAX! ");
 | |
|                     max_y = y;
 | |
|                     max_i = i;
 | |
|                     max_parameter = parameters[i];
 | |
|                 }
 | |
|             }
 | |
|             parameters[i] = save_param - step_size[i];
 | |
|             if (parameters[i] >= min_param[i]) {
 | |
|                 double y = evaluate();
 | |
|                 V printf("dn->%g ", y);
 | |
|                 if (y > max_y) {
 | |
|                     V printf("NEW MAX! ");
 | |
|                     max_y = y;
 | |
|                     max_i = i;
 | |
|                     max_parameter = parameters[i];
 | |
|                 }
 | |
|             }
 | |
|             parameters[i] = save_param;
 | |
|             V printf("\n");
 | |
|         }
 | |
|         iterations++; // for debugging, reporting
 | |
|         if (max_y <= best) { // no improvement, we're done
 | |
|             V printf("\nCompleted hillclimbing, best %g\n", best);
 | |
|             (*report)(cookie, iterations, best);
 | |
|             return best;
 | |
|         }
 | |
|         // improvement because max_y higher than best:
 | |
|         parameters[max_i] = max_parameter;
 | |
|         best = max_y;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 |