SDDS ToolKit Programs and Libraries for C and Python
All Classes Files Functions Variables Macros Pages
non_dominated_sort.c File Reference

Detailed Description

Definitions and implementations for non-dominated sorting and crowding distance routines used in multi-objective optimization.

This file provides the core functions for performing non-dominated sorting on a population of individuals, calculating crowding distances, and sorting the population based on dominance and crowding. It includes utility functions for managing linked lists used in the sorting process.

License
This file is distributed under the terms of the Software License Agreement found in the file LICENSE included with this distribution.
Author
H. Shang, R. Soliday

Definition in file non_dominated_sort.c.

#include "mdb.h"
#include "non_dominated_sort.h"

Go to the source code of this file.

Functions

void insert_node (list *node, long x)
 
list * del_node (list *node)
 
long check_dominance (individual *a, individual *b, long nobj)
 
void assign_crowding_distance_list (population *pop, list *lst, long front_size, long start_i, int64_t *sorted_index)
 
void assign_crowding_distance_indices (population *pop, long c1, long c2, long nobj)
 
void quicksort_front_obj (population *pop, long objcount, long obj_array[], long obj_array_size)
 
void q_sort_front_obj (population *pop, long objcount, long obj_array[], long left, long right)
 
void quicksort_dist (population *pop, long *dist, long front_size)
 
void q_sort_dist (population *pop, long *dist, long left, long right)
 
void assign_crowding_distance (population *pop, long *dist, long **obj_array, long front_size, long nobj)
 
int64_t * non_dominated_sort (population *new_pop)
 Performs non-dominated sorting on a population and assigns ranks and crowding distances.
 
void fill_population (population *pop, long rows, long columns, double **columnValue, long *maximize, double *const_violation)
 Initializes and fills the population structure with individuals and their objective values.
 
void free_pop_mem (population *pop)
 Frees all dynamically allocated memory associated with a population.
 

Function Documentation

◆ assign_crowding_distance()

void assign_crowding_distance ( population * pop,
long * dist,
long ** obj_array,
long front_size,
long nobj )

Definition at line 238 of file non_dominated_sort.c.

238 {
239 long i, j;
240 for (i = 0; i < nobj; i++) {
241 for (j = 0; j < front_size; j++)
242 obj_array[i][j] = dist[j];
243 quicksort_front_obj(pop, i, obj_array[i], front_size);
244 }
245 for (j = 0; j < front_size; j++)
246 pop->ind[dist[j]].crowd_dist = 0.0;
247 for (i = 0; i < nobj; i++)
248 pop->ind[obj_array[i][0]].crowd_dist = INF;
249 for (i = 0; i < nobj; i++) {
250 for (j = 1; j < front_size - 1; j++) {
251 if (pop->ind[obj_array[i][j]].crowd_dist != INF) {
252 if (pop->ind[obj_array[i][front_size - 1]].obj[i] == pop->ind[obj_array[i][0]].obj[i])
253 pop->ind[obj_array[i][j]].crowd_dist += 0.0;
254 else
255 pop->ind[obj_array[i][j]].crowd_dist += (pop->ind[obj_array[i][j + 1]].obj[i] - pop->ind[obj_array[i][j - 1]].obj[i]) / (pop->ind[obj_array[i][front_size - 1]].obj[i] - pop->ind[obj_array[i][0]].obj[i]);
256 }
257 }
258 }
259 for (j = 0; j < front_size; j++) {
260 if (pop->ind[dist[j]].crowd_dist != INF)
261 pop->ind[dist[j]].crowd_dist = (pop->ind[dist[j]].crowd_dist) / nobj;
262 }
263 return;
264}

◆ assign_crowding_distance_indices()

void assign_crowding_distance_indices ( population * pop,
long c1,
long c2,
long nobj )

Definition at line 133 of file non_dominated_sort.c.

133 {
134 long **obj_array;
135 long *dist;
136 long i, j;
137 long front_size;
138 front_size = c2 - c1 + 1;
139 if (front_size == 1) {
140 pop->ind[c1].crowd_dist = INF;
141 return;
142 } else if (front_size == 2) {
143 pop->ind[c1].crowd_dist = INF;
144 pop->ind[c2].crowd_dist = INF;
145 return;
146 }
147 obj_array = (long **)malloc(nobj * sizeof(long));
148 dist = (long *)malloc(front_size * sizeof(long));
149 for (i = 0; i < nobj; i++)
150 obj_array[i] = (long *)malloc(front_size * sizeof(long));
151 for (j = 0; j < front_size; j++)
152 dist[j] = c1++;
153 assign_crowding_distance(pop, dist, obj_array, front_size, nobj);
154 free(dist);
155 for (i = 0; i < nobj; i++)
156 free(obj_array[i]);
157 free(obj_array);
158 return;
159}

◆ assign_crowding_distance_list()

void assign_crowding_distance_list ( population * pop,
list * lst,
long front_size,
long start_i,
int64_t * sorted_index )

Definition at line 96 of file non_dominated_sort.c.

96 {
97 long **obj_array;
98 long *dist;
99 long i, j, nobj;
100 list *temp;
101
102 temp = lst;
103 nobj = pop->nobj;
104 if (front_size == 1) {
105 pop->ind[lst->index].crowd_dist = INF;
106 return;
107 } else if (front_size == 2) {
108 pop->ind[lst->index].crowd_dist = INF;
109 pop->ind[lst->child->index].crowd_dist = INF;
110 return;
111 }
112 obj_array = (long **)malloc(nobj * sizeof(long));
113 dist = (long *)malloc(front_size * sizeof(long));
114 for (i = 0; i < nobj; i++)
115 obj_array[i] = (long *)malloc(front_size * sizeof(long));
116
117 for (j = 0; j < front_size; j++) {
118 dist[j] = temp->index;
119 temp = temp->child;
120 }
121 assign_crowding_distance(pop, dist, obj_array, front_size, nobj);
122 q_sort_dist(pop, dist, 0, front_size - 1);
123 for (j = 0; j < front_size; j++)
124 sorted_index[start_i + j] = dist[j];
125 free(dist);
126 for (i = 0; i < nobj; i++)
127 free(obj_array[i]);
128 free(obj_array);
129 return;
130}

◆ check_dominance()

long check_dominance ( individual * a,
individual * b,
long nobj )

Definition at line 60 of file non_dominated_sort.c.

60 {
61 long i;
62 long flag1;
63 long flag2;
64 flag1 = 0;
65 flag2 = 0;
66 if (a->constr_violation < 0 && b->constr_violation < 0) {
67 if (a->constr_violation > b->constr_violation)
68 return (1);
69 else if (a->constr_violation < b->constr_violation)
70 return (-1);
71 else
72 return (0);
73 } else {
74 if (a->constr_violation < 0 && b->constr_violation >= 0)
75 return (-1);
76 else if (a->constr_violation >= 0 && b->constr_violation < 0)
77 return (1);
78 else {
79 for (i = 0; i < nobj; i++) {
80 if (a->obj[i] < b->obj[i])
81 flag1 = 1;
82 else if (a->obj[i] > b->obj[i])
83 flag2 = 1;
84 }
85 if (flag1 == 1 && flag2 == 0)
86 return (1);
87 else if (flag1 == 0 && flag2 == 1)
88 return (-1);
89 else
90 return (0);
91 }
92 }
93}

◆ del_node()

list * del_node ( list * node)

Definition at line 41 of file non_dominated_sort.c.

41 {
42 list *temp;
43 if (node == NULL) {
44 printf("\n Error!! asked to delete a NULL pointer, hence exiting \n");
45 exit(1);
46 }
47 temp = node->parent;
48 temp->child = node->child;
49 if (temp->child != NULL)
50 temp->child->parent = temp;
51 free(node);
52 return (temp);
53}

◆ fill_population()

void fill_population ( population * pop,
long rows,
long columns,
double ** columnValue,
long * maximize,
double * const_violation )

Initializes and fills the population structure with individuals and their objective values.

This function allocates memory for the population's individuals, sets up their objective values based on the provided column values, and initializes constraint violations if provided.

Parameters
popPointer to the population structure to be filled.
rowsThe number of individuals in the population.
columnsThe number of objective functions.
columnValue2D array containing the objective values for each individual and objective.
maximizeArray indicating which objectives should be maximized (non-zero value) or minimized.
const_violationArray containing constraint violation values for each individual, or NULL if no constraints.

Definition at line 370 of file non_dominated_sort.c.

370 {
371 long i, j;
372 if (!pop) {
373 fprintf(stderr, "Null pointer passed to fill_population.\n");
374 exit(1);
375 }
376 pop->ind = (individual *)malloc(rows * sizeof(individual));
377 pop->popsize = rows;
378 pop->nbin = pop->ncons = pop->nreal = 0;
379 pop->nbits = NULL;
380 pop->nobj = columns;
381 for (i = 0; i < rows; i++) {
382 pop->ind[i].xreal = NULL;
383 pop->ind[i].xbin = NULL;
384 pop->ind[i].gene = NULL;
385 pop->ind[i].obj = (double *)malloc(sizeof(double) * pop->nobj);
386 if (const_violation)
387 pop->ind[i].constr_violation = const_violation[i];
388 else
389 pop->ind[i].constr_violation = 0;
390 pop->ind[i].constr = NULL;
391 pop->ind[i].rank = 0;
392 pop->ind[i].crowd_dist = 0;
393 for (j = 0; j < columns; j++) {
394 if (maximize[j])
395 pop->ind[i].obj[j] = -1.0 * columnValue[j][i];
396 else
397 pop->ind[i].obj[j] = columnValue[j][i];
398 }
399 }
400 return;
401}

◆ free_pop_mem()

void free_pop_mem ( population * pop)

Frees all dynamically allocated memory associated with a population.

This function deallocates memory for all individuals in the population, including their objective values, binary variables, constraints, genes, and other dynamically allocated fields.

Parameters
popPointer to the population structure whose memory is to be freed.

Definition at line 411 of file non_dominated_sort.c.

411 {
412 long i, j;
413 for (i = 0; i < pop->popsize; i++) {
414 if (pop->ind[i].xreal)
415 free(pop->ind[i].xreal);
416 if (pop->ind[i].obj)
417 free(pop->ind[i].obj);
418 if (pop->ind[i].xbin)
419 free(pop->ind[i].xbin);
420 if (pop->ind[i].constr)
421 free(pop->ind[i].constr);
422 for (j = 0; j < pop->nbin; j++)
423 if (pop->ind[i].gene && pop->ind[i].gene[j])
424 free(pop->ind[i].gene[j]);
425 if (pop->ind[i].gene)
426 free(pop->ind[i].gene);
427 }
428 if (pop->ind)
429 free(pop->ind);
430 if (pop->nbits)
431 free(pop->nbits);
432}

◆ insert_node()

void insert_node ( list * node,
long x )

Definition at line 24 of file non_dominated_sort.c.

24 {
25 list *temp;
26 if (node == NULL) {
27 printf("\n Error!! asked to enter after a NULL pointer, hence exiting \n");
28 exit(1);
29 }
30 temp = (list *)malloc(sizeof(list));
31 temp->index = x;
32 temp->child = node->child;
33 temp->parent = node;
34 if (node->child != NULL)
35 node->child->parent = temp;
36 node->child = temp;
37 return;
38}

◆ non_dominated_sort()

int64_t * non_dominated_sort ( population * new_pop)

Performs non-dominated sorting on a population and assigns ranks and crowding distances.

This function sorts the population into different fronts based on non-domination, assigns a rank to each individual indicating its front, and computes the crowding distance to maintain diversity within each front.

Parameters
new_popPointer to the population structure to be sorted.
Returns
Array of sorted indices representing the population ordered by rank and crowding distance.

Definition at line 275 of file non_dominated_sort.c.

275 {
276 long flag, popsize, i, end, front_size, rank = 1, j;
277 int64_t *sorted_index = NULL;
278 list *orig;
279 list *cur;
280 list *temp1, *temp2;
281 orig = (list *)malloc(sizeof(list));
282 cur = (list *)malloc(sizeof(list));
283 front_size = 0;
284 orig->index = -1;
285 orig->parent = NULL;
286 orig->child = NULL;
287 cur->index = -1;
288 cur->parent = NULL;
289 cur->child = NULL;
290 temp1 = orig;
291 popsize = new_pop->popsize;
292 sorted_index = (int64_t *)malloc(sizeof(*sorted_index) * popsize);
293 for (i = 0; i < popsize; i++) {
294 sorted_index[i] = i;
295 insert_node(temp1, i);
296 temp1 = temp1->child;
297 }
298 i = j = 0;
299 do {
300 if (orig->child->child == NULL) {
301 new_pop->ind[orig->child->index].rank = rank;
302 new_pop->ind[orig->child->index].crowd_dist = INF;
303 sorted_index[i++] = orig->child->index;
304 free(orig->child);
305 break;
306 }
307 temp1 = orig->child;
308 insert_node(cur, temp1->index);
309 front_size = 1;
310 temp2 = cur->child;
311 temp1 = del_node(temp1);
312 temp1 = temp1->child;
313 do {
314 temp2 = cur->child;
315 do {
316 end = 0;
317 flag = check_dominance(&(new_pop->ind[temp1->index]),
318 &(new_pop->ind[temp2->index]), new_pop->nobj);
319 if (flag == 1) {
320 insert_node(orig, temp2->index);
321 temp2 = del_node(temp2);
322 front_size--;
323 temp2 = temp2->child;
324 }
325 if (flag == 0)
326 temp2 = temp2->child;
327 else if (flag == -1)
328 end = 1;
329 } while (end != 1 && temp2 != NULL);
330 if (flag == 0 || flag == 1) {
331 insert_node(cur, temp1->index);
332 front_size++;
333 temp1 = del_node(temp1);
334 }
335 temp1 = temp1->child;
336 } while (temp1 != NULL);
337 temp2 = cur->child;
338 do {
339 new_pop->ind[temp2->index].rank = rank;
340 sorted_index[i++] = temp2->index;
341 temp2 = temp2->child;
342 } while (temp2 != NULL);
343 assign_crowding_distance_list(new_pop, cur->child, front_size, j, sorted_index);
344 temp2 = cur->child;
345 do {
346 j++;
347 temp2 = del_node(temp2);
348 temp2 = temp2->child;
349 } while (cur->child != NULL);
350 rank += 1;
351 } while (orig->child != NULL);
352 free(orig);
353 free(cur);
354 return sorted_index;
355}

◆ q_sort_dist()

void q_sort_dist ( population * pop,
long * dist,
long left,
long right )

Definition at line 207 of file non_dominated_sort.c.

207 {
208 long index;
209 long temp;
210 long i, j;
211 double pivot;
212 if (left < right) {
213 index = (left + right) / 2;
214 temp = dist[right];
215 dist[right] = dist[index];
216 dist[index] = temp;
217 pivot = pop->ind[dist[right]].crowd_dist;
218 i = left - 1;
219 for (j = left; j < right; j++) {
220 if (pop->ind[dist[j]].crowd_dist > pivot) {
221 i += 1;
222 temp = dist[j];
223 dist[j] = dist[i];
224 dist[i] = temp;
225 }
226 }
227 index = i + 1;
228 temp = dist[index];
229 dist[index] = dist[right];
230 dist[right] = temp;
231 q_sort_dist(pop, dist, left, index - 1);
232 q_sort_dist(pop, dist, index + 1, right);
233 }
234 return;
235}

◆ q_sort_front_obj()

void q_sort_front_obj ( population * pop,
long objcount,
long obj_array[],
long left,
long right )

Definition at line 170 of file non_dominated_sort.c.

170 {
171 long index;
172 long temp;
173 long i, j;
174 double pivot;
175 if (left < right) {
176 index = (left + right) / 2;
177 temp = obj_array[right];
178 obj_array[right] = obj_array[index];
179 obj_array[index] = temp;
180 pivot = pop->ind[obj_array[right]].obj[objcount];
181 i = left - 1;
182 for (j = left; j < right; j++) {
183 if (pop->ind[obj_array[j]].obj[objcount] <= pivot) {
184 i += 1;
185 temp = obj_array[j];
186 obj_array[j] = obj_array[i];
187 obj_array[i] = temp;
188 }
189 }
190 index = i + 1;
191 temp = obj_array[index];
192 obj_array[index] = obj_array[right];
193 obj_array[right] = temp;
194 q_sort_front_obj(pop, objcount, obj_array, left, index - 1);
195 q_sort_front_obj(pop, objcount, obj_array, index + 1, right);
196 }
197 return;
198}

◆ quicksort_dist()

void quicksort_dist ( population * pop,
long * dist,
long front_size )

Definition at line 201 of file non_dominated_sort.c.

201 {
202 q_sort_dist(pop, dist, 0, front_size - 1);
203 return;
204}

◆ quicksort_front_obj()

void quicksort_front_obj ( population * pop,
long objcount,
long obj_array[],
long obj_array_size )

Definition at line 163 of file non_dominated_sort.c.

163 {
164 q_sort_front_obj(pop, objcount, obj_array, 0, obj_array_size - 1);
165 return;
166}