File: hillclimb.cpp

package info (click to toggle)
audacity 2.2.2-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 129,312 kB
  • sloc: ansic: 373,350; cpp: 276,880; sh: 56,060; python: 18,922; makefile: 10,309; lisp: 8,365; xml: 1,888; perl: 1,798; java: 1,551; asm: 545; pascal: 395; sed: 58; awk: 35
file content (187 lines) | stat: -rw-r--r-- 6,425 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 *  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;
    }
}