1 /* ****************************************************************************
2 * FILE: annealing.c
3 * MODULE: v.labels.sa
4 * AUTHOR(S): Wolf Bergenheim
5 * PURPOSE: This file contains functions which have to do with the
6 annealing part of the algorithm.
7 * COPYRIGHT: (C) 2007 by the GRASS Development Team
8 *
9 * This program is free software under the GNU General Public
10 * License (>=v2). Read the file COPYING that comes with GRASS
11 * for details.
12 *
13 *****************************************************************************/
14
15 #include "labels.h"
16 #ifndef M_E
17
18 /**
19 * This is simply Euler's number
20 */
21 # define M_E 2.7182818284590452353602874713526625L
22 #endif
23
24 /**
25 * How many times to decrease the Temperature T.
26 */
27 #define TEMP_DECS 50
28
29 static double calc_label_overlap(label_t * label, int cc, int nc);
30 static void do_label_overlap(label_t * label, int cc, int nc);
31
32 /**
33 * This is just a variable used to show statistics in the end.
34 */
35 static unsigned int overlaps_created = 0;
36
37 /**
38 * This is just a variable used to show statistics in the end.
39 */
40 static unsigned int overlaps_removed = 0;
41
42 /**
43 * This funxtion does the actual sumulated annealing process. Each round 30 x
44 * n (the number of labels) a label is picked at random, and placed in a random
45 * new position. Then the dE is calculated, and if dE is > 0 the new position is
46 * reversed with the probablility 1 - e^(-dE / T).
47 @param labels The array of all labels.
48 @param n_labels The size of the labels array.
49 @params The commandline parameters.
50 */
simulate_annealing(label_t * labels,int n_labels,struct params * p)51 void simulate_annealing(label_t * labels, int n_labels, struct params *p)
52 {
53 /* The temperature of the system */
54 double T;
55
56 /* The change in energy */
57 double dE;
58
59 T = -1.0 / log(1.0 / 3.0);
60 unsigned int t, tot_better = 0, tot_worse = 0, tot_ign = 0;
61
62 fprintf(stderr, "Optimizing label positions: ...");
63 for (t = 0; t < TEMP_DECS; t++) {
64 int i;
65 unsigned int successes = 0, consec_successes = 0;
66
67 for (i = 0; i < (n_labels * 30); i++) {
68 int l, c, cc, r;
69 label_t *lp;
70
71 /* pick a random label */
72 r = rand();
73 l = (int)((double)(n_labels) * (r / (RAND_MAX + 1.0)));
74 lp = &labels[l];
75 /* skip labels without sufficient number of candidates */
76 if (lp->n_candidates < 2)
77 continue;
78
79 cc = lp->current_candidate;
80 /*and a random new candidate place */
81 c = (int)((double)(lp->n_candidates) *
82 (rand() / (RAND_MAX + 1.0)));
83 if (c == cc) {
84 if (c == 0)
85 c++;
86 else
87 c--;
88 }
89 /* calc dE */
90 dE = lp->candidates[c].score - lp->candidates[cc].score;
91 dE += calc_label_overlap(lp, cc, c);
92
93 /* if dE < 0 accept */
94 if (dE < 0.0) {
95 lp->current_score = lp->candidates[c].score;
96 do_label_overlap(lp, cc, c);
97 lp->current_candidate = c;
98 successes++;
99 consec_successes++;
100 tot_better++;
101 }
102 /* else apply with probability p=e^(-dE/T) */
103 else {
104 double dp, dr;
105
106 dp = pow(M_E, -dE / T);
107 dr = (double)rand() / RAND_MAX;
108 if (dr <= dp) {
109 do_label_overlap(lp, cc, c);
110 lp->current_score += lp->candidates[c].score;
111 lp->current_candidate = c;
112 successes++;
113 consec_successes++;
114 tot_worse++;
115 }
116 else {
117 tot_ign++;
118 consec_successes = 0;
119 }
120 }
121 /* decrease immediately */
122 if (consec_successes > (5 * n_labels)) {
123 consec_successes = 0;
124 break;
125 }
126 }
127 G_percent(t, TEMP_DECS, 1);
128 /* we have found an optimal solution */
129 if (successes == 0) {
130 break;
131 }
132 T -= 0.1 * T;
133 }
134 G_percent(TEMP_DECS, TEMP_DECS, 1);
135 }
136
137 /**
138 * This function calculates the change in E (dE) if the given label would
139 * be moved to the new place. Contrary to the original algorithm this function
140 * requires twice as much energy to overlap two labels then is released by
141 * resolving an overlap. I don't have and scientific fact but it seems to
142 * improve the result.
143 * @param label The label in question
144 * @param cc The current candidate
145 * @param nc The new potential candidate location
146 * @return The dE value.
147 */
calc_label_overlap(label_t * label,int cc,int nc)148 static double calc_label_overlap(label_t * label, int cc, int nc)
149 {
150 int i;
151 double dE = 0.0;
152
153 /* calculate the overlaps removed */
154 for (i = 0; i < label->candidates[cc].n_intersections; i++) {
155 label_t *ol;
156 int oc;
157
158 ol = label->candidates[cc].intersections[i].label;
159 oc = label->candidates[cc].intersections[i].candidate;
160 if (ol->current_candidate == oc) {
161 dE -= LABEL_OVERLAP_WEIGHT;
162 }
163 }
164
165 /* calculate the overlaps created */
166 for (i = 0; i < label->candidates[nc].n_intersections; i++) {
167 label_t *ol;
168 int oc;
169
170 ol = label->candidates[nc].intersections[i].label;
171 oc = label->candidates[nc].intersections[i].candidate;
172 if (ol->current_candidate == oc) {
173 dE += LABEL_OVERLAP_WEIGHT;
174 }
175 }
176
177 return dE;
178 }
179
180 /**
181 * This function commits the label change to the new location.
182 * @param label The label to move
183 * @param cc The current candidate
184 * @param nc The new potential candidate location
185 */
do_label_overlap(label_t * label,int cc,int nc)186 static void do_label_overlap(label_t * label, int cc, int nc)
187 {
188 int i;
189
190 /* remove the current label overlaps */
191 for (i = 0; i < label->candidates[cc].n_intersections; i++) {
192 label_t *ol;
193 int oc;
194
195 ol = label->candidates[cc].intersections[i].label;
196 oc = label->candidates[cc].intersections[i].candidate;
197 if (ol->current_candidate == oc) {
198 ol->current_score -= LABEL_OVERLAP_WEIGHT;
199 label->current_score -= LABEL_OVERLAP_WEIGHT;
200 /* ol->candidates[oc].score -= LABEL_OVERLAP_WEIGHT; */
201 overlaps_removed++;
202 }
203 }
204
205 /* create new overlaps */
206 for (i = 0; i < label->candidates[nc].n_intersections; i++) {
207 label_t *ol;
208 int oc;
209
210 ol = label->candidates[nc].intersections[i].label;
211 oc = label->candidates[nc].intersections[i].candidate;
212 if (ol->current_candidate == oc) {
213 ol->current_score += LABEL_OVERLAP_WEIGHT;
214 label->current_score += LABEL_OVERLAP_WEIGHT;
215 /* ol->candidates[oc]->score += 40; */
216 overlaps_created++;
217 }
218 }
219 }
220