cbc61e0d0be31f1e865f36c2b49a295c69937e23
[libfirm] / heuristical.c
1 #include "adt/array.h"
2 #include "assert.h"
3 #include "error.h"
4
5 #include "heuristical.h"
6 #include "html_dumper.h"
7 #include "kaps.h"
8 #include "matrix.h"
9 #include "pbqp_edge.h"
10 #include "pbqp_edge_t.h"
11 #include "pbqp_node.h"
12 #include "pbqp_node_t.h"
13 #include "vector.h"
14
15 static pbqp_edge **edge_bucket;
16 static pbqp_node **node_buckets[4];
17 static pbqp_node **reduced_bucket = NULL;
18 static int         buckets_filled = 0;
19
20 static void insert_into_edge_bucket(pbqp_edge *edge)
21 {
22         unsigned bucket_len = ARR_LEN(edge_bucket);
23
24         if (edge->bucket_index < bucket_len && edge_bucket[edge->bucket_index]
25                         == edge)
26                 /* Edge is already inserted. */
27                 return;
28
29         edge->bucket_index = bucket_len;
30         ARR_APP1(pbqp_edge *, edge_bucket, edge);
31 }
32
33 static void init_buckets(void)
34 {
35         int i;
36
37         edge_bucket = NEW_ARR_F(pbqp_edge *, 0);
38         reduced_bucket = NEW_ARR_F(pbqp_node *, 0);
39
40         for (i = 0; i < 4; ++i) {
41                 node_buckets[i] = NEW_ARR_F(pbqp_node *, 0);
42         }
43 }
44
45 static void free_buckets(void)
46 {
47         int i;
48
49         for (i = 0; i < 4; ++i) {
50                 DEL_ARR_F(node_buckets[i]);
51                 node_buckets[i] = NULL;
52         }
53
54         DEL_ARR_F(edge_bucket);
55         edge_bucket = NULL;
56
57         DEL_ARR_F(reduced_bucket);
58         reduced_bucket = NULL;
59
60         buckets_filled = 0;
61 }
62
63 static void fill_node_buckets(pbqp *pbqp)
64 {
65         unsigned node_index;
66         unsigned node_len;
67
68         assert(pbqp);
69         node_len = pbqp->num_nodes;
70
71         for (node_index = 0; node_index < node_len; ++node_index) {
72                 unsigned   arity;
73                 pbqp_node *node = get_node(pbqp, node_index);
74
75                 if (!node) continue;
76
77                 arity = ARR_LEN(node->edges);
78
79                 /* We have only one bucket for nodes with arity >= 3. */
80                 if (arity > 3) {
81                         arity = 3;
82                 }
83
84                 node->bucket_index = ARR_LEN(node_buckets[arity]);
85
86                 ARR_APP1(pbqp_node *, node_buckets[arity], node);
87         }
88
89         buckets_filled = 1;
90 }
91
92 static void normalize_towards_source(pbqp *pbqp, pbqp_edge *edge)
93 {
94         pbqp_matrix    *mat;
95         pbqp_node      *src_node;
96         pbqp_node      *tgt_node;
97         vector         *src_vec;
98         vector         *tgt_vec;
99         int             src_len;
100         int             tgt_len;
101         int             src_index;
102
103         assert(pbqp);
104         assert(edge);
105
106         src_node = edge->src;
107         tgt_node = edge->tgt;
108         assert(src_node);
109         assert(tgt_node);
110
111         src_vec = src_node->costs;
112         tgt_vec = tgt_node->costs;
113         assert(src_vec);
114         assert(tgt_vec);
115
116         src_len = src_vec->len;
117         tgt_len = tgt_vec->len;
118         assert(src_len > 0);
119         assert(tgt_len > 0);
120
121         mat = edge->costs;
122         assert(mat);
123
124         /* Normalize towards source node. */
125         for (src_index = 0; src_index < src_len; ++src_index) {
126                 num min = pbqp_matrix_get_row_min(mat, src_index, tgt_vec);
127
128                 if (min != 0) {
129                         if (src_vec->entries[src_index].data == INF_COSTS) {
130                                 pbqp_matrix_set_row_value(mat, src_index, 0);
131                         } else {
132                                 pbqp_matrix_sub_row_value(mat, src_index, tgt_vec, min);
133                         }
134                         src_vec->entries[src_index].data = pbqp_add(
135                                         src_vec->entries[src_index].data, min);
136
137                         if (min == INF_COSTS) {
138                                 unsigned edge_index;
139                                 unsigned edge_len = ARR_LEN(src_node->edges);
140
141                                 for (edge_index = 0; edge_index < edge_len; ++edge_index) {
142                                         pbqp_edge *edge_candidate = src_node->edges[edge_index];
143                                         if (edge_candidate != edge) {
144                                                 insert_into_edge_bucket(edge_candidate);
145                                         }
146                                 }
147                         }
148                 }
149         }
150 }
151
152 static void normalize_towards_target(pbqp *pbqp, pbqp_edge *edge)
153 {
154         pbqp_matrix    *mat;
155         pbqp_node      *src_node;
156         pbqp_node      *tgt_node;
157         vector         *src_vec;
158         vector         *tgt_vec;
159         int             src_len;
160         int             tgt_len;
161         int             tgt_index;
162
163         assert(pbqp);
164         assert(edge);
165
166         src_node = edge->src;
167         tgt_node = edge->tgt;
168         assert(src_node);
169         assert(tgt_node);
170
171         src_vec = src_node->costs;
172         tgt_vec = tgt_node->costs;
173         assert(src_vec);
174         assert(tgt_vec);
175
176         src_len = src_vec->len;
177         tgt_len = tgt_vec->len;
178         assert(src_len > 0);
179         assert(tgt_len > 0);
180
181         mat = edge->costs;
182         assert(mat);
183
184         for (tgt_index = 0; tgt_index < tgt_len; ++tgt_index) {
185                 num min = pbqp_matrix_get_col_min(mat, tgt_index, src_vec);
186
187                 if (min != 0) {
188                         if (tgt_vec->entries[tgt_index].data == INF_COSTS) {
189                                 pbqp_matrix_set_col_value(mat, tgt_index, 0);
190                         } else {
191                                 pbqp_matrix_sub_col_value(mat, tgt_index, src_vec, min);
192                         }
193                         tgt_vec->entries[tgt_index].data = pbqp_add(
194                                         tgt_vec->entries[tgt_index].data, min);
195
196                         if (min == INF_COSTS) {
197                                 unsigned edge_index;
198                                 unsigned edge_len = ARR_LEN(tgt_node->edges);
199
200                                 for (edge_index = 0; edge_index < edge_len; ++edge_index) {
201                                         pbqp_edge *edge_candidate = tgt_node->edges[edge_index];
202                                         if (edge_candidate != edge) {
203                                                 insert_into_edge_bucket(edge_candidate);
204                                         }
205                                 }
206                         }
207                 }
208         }
209 }
210
211 static void reorder_node(pbqp_node *node)
212 {
213         unsigned    arity;
214         unsigned    old_arity;
215         unsigned    old_bucket_len;
216         unsigned    old_bucket_index;
217         pbqp_node **old_bucket;
218         pbqp_node  *other;
219
220         if (!buckets_filled) return;
221
222         assert(node);
223
224         arity = ARR_LEN(node->edges);
225
226         /* Same bucket as before */
227         if (arity > 2) return;
228
229         /* Assume node lost one incident edge. */
230         old_arity        = arity + 1;
231         old_bucket       = node_buckets[old_arity];
232         old_bucket_len   = ARR_LEN(old_bucket);
233         old_bucket_index = node->bucket_index;
234
235         if (old_bucket_len <= old_bucket_index || old_bucket[old_bucket_index]
236                         != node) {
237                 unsigned bucket_len = ARR_LEN(node_buckets[arity]);
238
239                 /* Old arity is new arity, so we have nothing to do. */
240                 assert(old_bucket_index < bucket_len);
241                 assert(node_buckets[arity][old_bucket_index] == node);
242                 return;
243         }
244
245         assert(old_bucket[old_bucket_index] == node);
246
247         /* Delete node from old bucket... */
248         other                        = old_bucket[old_bucket_len - 1];
249         other->bucket_index          = old_bucket_index;
250         old_bucket[old_bucket_index] = other;
251         ARR_SHRINKLEN(node_buckets[old_arity], old_bucket_len - 1);
252
253         /* ..and add to new one. */
254         node->bucket_index = ARR_LEN(node_buckets[arity]);
255         ARR_APP1(pbqp_node*, node_buckets[arity], node);
256 }
257
258 static void simplify_edge(pbqp *pbqp, pbqp_edge *edge)
259 {
260         pbqp_matrix    *mat;
261         pbqp_node      *src_node;
262         pbqp_node      *tgt_node;
263         vector         *src_vec;
264         vector         *tgt_vec;
265         int             src_len;
266         int             tgt_len;
267
268         assert(pbqp);
269         assert(edge);
270
271         src_node = edge->src;
272         tgt_node = edge->tgt;
273         assert(src_node);
274         assert(tgt_node);
275
276         /* If edge are already deleted, we have nothing to do. */
277         if (!is_connected(src_node, edge) || !is_connected(tgt_node, edge))
278                 return;
279
280         if (pbqp->dump_file) {
281                 char txt[100];
282                 sprintf(txt, "Simplification of Edge n%d-n%d", src_node->index, tgt_node->index);
283                 dump_section(pbqp->dump_file, 3, txt);
284         }
285
286         src_vec = src_node->costs;
287         tgt_vec = tgt_node->costs;
288         assert(src_vec);
289         assert(tgt_vec);
290
291         src_len = src_vec->len;
292         tgt_len = tgt_vec->len;
293         assert(src_len > 0);
294         assert(tgt_len > 0);
295
296         mat = edge->costs;
297         assert(mat);
298
299         if (pbqp->dump_file) {
300                 fputs("Input:<br>\n", pbqp->dump_file);
301                 dump_simplifyedge(pbqp, edge);
302         }
303
304         normalize_towards_source(pbqp, edge);
305         normalize_towards_target(pbqp, edge);
306
307         if (pbqp->dump_file) {
308                 fputs("<br>\nOutput:<br>\n", pbqp->dump_file);
309                 dump_simplifyedge(pbqp, edge);
310         }
311
312         if (pbqp_matrix_is_zero(mat, src_vec, tgt_vec)) {
313                 if (pbqp->dump_file) {
314                         fputs("edge has been eliminated<br>\n", pbqp->dump_file);
315                 }
316
317                 delete_edge(edge);
318                 reorder_node(src_node);
319                 reorder_node(tgt_node);
320         }
321 }
322
323 void solve_pbqp_heuristical(pbqp *pbqp)
324 {
325         unsigned node_index;
326         unsigned node_len;
327
328         assert(pbqp);
329
330         if (pbqp->dump_file) {
331                 pbqp_dump_input(pbqp);
332                 dump_section(pbqp->dump_file, 1, "2. Simplification of Cost Matrices");
333         }
334
335         node_len = pbqp->num_nodes;
336
337         init_buckets();
338
339         /* First simplify all edges. */
340         for (node_index = 0; node_index < node_len; ++node_index) {
341                 unsigned    edge_index;
342                 pbqp_node  *node = get_node(pbqp, node_index);
343                 pbqp_edge **edges;
344                 unsigned    edge_len;
345
346                 if (!node) continue;
347
348                 edges = node->edges;
349                 edge_len = ARR_LEN(edges);
350
351                 for (edge_index = 0; edge_index < edge_len; ++edge_index) {
352                         pbqp_edge *edge = edges[edge_index];
353
354                         /* Simplify only once per edge. */
355                         if (node != edge->src) continue;
356
357                         simplify_edge(pbqp, edge);
358                 }
359         }
360
361         /* Put node into bucket representing their arity. */
362         fill_node_buckets(pbqp);
363
364         for (;;) {
365                 if (ARR_LEN(edge_bucket) > 0) {
366                         apply_edge(pbqp);
367                 } else if (ARR_LEN(node_buckets[1]) > 0) {
368                         apply_RI(pbqp);
369                 } else if (ARR_LEN(node_buckets[2]) > 0) {
370                         apply_RII(pbqp);
371                 } else if (ARR_LEN(node_buckets[3]) > 0) {
372                         apply_RN(pbqp);
373                 } else {
374                         break;
375                 }
376         }
377
378         if (pbqp->dump_file) {
379                 dump_section(pbqp->dump_file, 1, "4. Determine Solution/Minimum");
380                 dump_section(pbqp->dump_file, 2, "4.1. Trivial Solution");
381         }
382
383         /* Solve trivial nodes and calculate solution. */
384         node_len = ARR_LEN(node_buckets[0]);
385         for (node_index = 0; node_index < node_len; ++node_index) {
386                 pbqp_node *node = node_buckets[0][node_index];
387                 assert(node);
388
389                 node->solution = vector_get_min_index(node->costs);
390                 pbqp->solution = pbqp_add(pbqp->solution,
391                                 node->costs->entries[node->solution].data);
392                 if (pbqp->dump_file) {
393                         fprintf(pbqp->dump_file, "node n%d is set to %d<br>\n", node->index, node->solution);
394                         dump_node(pbqp, node);
395                 }
396         }
397
398         if (pbqp->dump_file) {
399                 dump_section(pbqp->dump_file, 2, "Minimum");
400                 fprintf(pbqp->dump_file, "Minimum is equal to %lld.", pbqp->solution);
401                 dump_section(pbqp->dump_file, 2, "Back Propagation");
402         }
403
404         /* Solve reduced nodes. */
405         node_len = ARR_LEN(reduced_bucket);
406         for (node_index = node_len; node_index > 0; --node_index) {
407                 pbqp_node *node = reduced_bucket[node_index - 1];
408                 assert(node);
409
410                 switch (ARR_LEN(node->edges)) {
411                         case 1:
412                                 back_propagate_RI(pbqp, node);
413                                 break;
414                         case 2:
415                                 back_propagate_RII(pbqp, node);
416                                 break;
417                         default:
418                                 panic("Only nodes with degree one or two should be in this bucket");
419                                 break;
420                 }
421         }
422
423         free_buckets();
424 }
425
426 void apply_edge(pbqp *pbqp)
427 {
428         unsigned   bucket_len = ARR_LEN(edge_bucket);
429         pbqp_edge *edge       = edge_bucket[bucket_len - 1];
430
431         ARR_SHRINKLEN(edge_bucket, (int)bucket_len - 1);
432
433         simplify_edge(pbqp, edge);
434 }
435
436 void apply_RI(pbqp *pbqp)
437 {
438         pbqp_node  **bucket     = node_buckets[1];
439         unsigned     bucket_len = ARR_LEN(bucket);
440         pbqp_node   *node       = bucket[bucket_len - 1];
441         pbqp_edge   *edge       = node->edges[0];
442         pbqp_matrix *mat        = edge->costs;
443         int          is_src     = edge->src == node;
444         pbqp_node   *other_node;
445
446         if (is_src) {
447                 other_node = edge->tgt;
448         } else {
449                 other_node = edge->src;
450         }
451
452         if (pbqp->dump_file) {
453                 char     txt[100];
454                 sprintf(txt, "RI-Reduction of Node n%d", node->index);
455                 dump_section(pbqp->dump_file, 2, txt);
456                 pbqp_dump_graph(pbqp);
457                 fputs("<br>\nBefore reduction:<br>\n", pbqp->dump_file);
458                 dump_node(pbqp, node);
459                 dump_node(pbqp, other_node);
460                 dump_edge(pbqp, edge);
461         }
462
463         if (is_src) {
464                 pbqp_matrix_add_to_all_cols(mat, node->costs);
465                 normalize_towards_target(pbqp, edge);
466         } else {
467                 pbqp_matrix_add_to_all_rows(mat, node->costs);
468                 normalize_towards_source(pbqp, edge);
469         }
470         disconnect_edge(other_node, edge);
471
472         if (pbqp->dump_file) {
473                 fputs("<br>\nAfter reduction:<br>\n", pbqp->dump_file);
474                 dump_node(pbqp, other_node);
475         }
476
477         /* Remove node from bucket... */
478         ARR_SHRINKLEN(bucket, (int)bucket_len - 1);
479         reorder_node(other_node);
480
481         /* ...and add it to back propagation list. */
482         node->bucket_index = ARR_LEN(reduced_bucket);
483         ARR_APP1(pbqp_node *, reduced_bucket, node);
484 }
485
486 void apply_RII(pbqp *pbqp)
487 {
488         pbqp_node  **bucket     = node_buckets[2];
489         unsigned     bucket_len = ARR_LEN(bucket);
490         pbqp_node   *node       = bucket[bucket_len - 1];
491         pbqp_edge   *src_edge   = node->edges[0];
492         pbqp_edge   *tgt_edge   = node->edges[1];
493         int          src_is_src = src_edge->src == node;
494         int          tgt_is_src = tgt_edge->src == node;
495         pbqp_matrix *src_mat;
496         pbqp_matrix *tgt_mat;
497         pbqp_node   *src_node;
498         pbqp_node   *tgt_node;
499         pbqp_matrix *mat;
500         vector      *vec;
501         vector      *node_vec;
502         vector      *src_vec;
503         vector      *tgt_vec;
504         unsigned     col_index;
505         unsigned     col_len;
506         unsigned     row_index;
507         unsigned     row_len;
508         unsigned     node_len;
509
510         assert(pbqp);
511
512         if (src_is_src) {
513                 src_node = src_edge->tgt;
514         } else {
515                 src_node = src_edge->src;
516         }
517
518         if (tgt_is_src) {
519                 tgt_node = tgt_edge->tgt;
520         } else {
521                 tgt_node = tgt_edge->src;
522         }
523
524         /* Swap nodes if necessary. */
525         if (tgt_node->index < src_node->index) {
526                 pbqp_node *tmp_node;
527                 pbqp_edge *tmp_edge;
528
529                 tmp_node = src_node;
530                 src_node = tgt_node;
531                 tgt_node = tmp_node;
532
533                 tmp_edge = src_edge;
534                 src_edge = tgt_edge;
535                 tgt_edge = tmp_edge;
536
537                 src_is_src = src_edge->src == node;
538                 tgt_is_src = tgt_edge->src == node;
539         }
540
541         if (pbqp->dump_file) {
542                 char     txt[100];
543                 sprintf(txt, "RII-Reduction of Node n%d", node->index);
544                 dump_section(pbqp->dump_file, 2, txt);
545                 pbqp_dump_graph(pbqp);
546                 fputs("<br>\nBefore reduction:<br>\n", pbqp->dump_file);
547                 dump_node(pbqp, src_node);
548                 dump_edge(pbqp, src_edge);
549                 dump_node(pbqp, node);
550                 dump_edge(pbqp, tgt_edge);
551                 dump_node(pbqp, tgt_node);
552         }
553
554         src_mat = src_edge->costs;
555         tgt_mat = tgt_edge->costs;
556
557         src_vec  = src_node->costs;
558         tgt_vec  = tgt_node->costs;
559         node_vec = node->costs;
560
561         row_len  = src_vec->len;
562         col_len  = tgt_vec->len;
563         node_len = node_vec->len;
564
565         mat = pbqp_matrix_alloc(pbqp, row_len, col_len);
566
567         for (row_index = 0; row_index < row_len; ++row_index) {
568                 for (col_index = 0; col_index < col_len; ++col_index) {
569                         vec = vector_copy(pbqp, node_vec);
570
571                         if (src_is_src) {
572                                 vector_add_matrix_col(vec, src_mat, row_index);
573                         } else {
574                                 vector_add_matrix_row(vec, src_mat, row_index);
575                         }
576
577                         if (tgt_is_src) {
578                                 vector_add_matrix_col(vec, tgt_mat, col_index);
579                         } else {
580                                 vector_add_matrix_row(vec, tgt_mat, col_index);
581                         }
582
583                         mat->entries[row_index * col_len + col_index] = vector_get_min(vec);
584
585                         obstack_free(&pbqp->obstack, vec);
586                 }
587         }
588
589         pbqp_edge *edge = get_edge(pbqp, src_node->index, tgt_node->index);
590
591         /* Disconnect node. */
592         disconnect_edge(src_node, src_edge);
593         disconnect_edge(tgt_node, tgt_edge);
594
595         /* Remove node from bucket... */
596         ARR_SHRINKLEN(bucket, (int)bucket_len - 1);
597
598         /* ...and add it to back propagation list. */
599         node->bucket_index = ARR_LEN(reduced_bucket);
600         ARR_APP1(pbqp_node *, reduced_bucket, node);
601
602         if (edge == NULL) {
603                 edge = alloc_edge(pbqp, src_node->index, tgt_node->index, mat);
604         } else {
605                 pbqp_matrix_add(edge->costs, mat);
606
607                 /* Free local matrix. */
608                 obstack_free(&pbqp->obstack, mat);
609
610                 reorder_node(src_node);
611                 reorder_node(tgt_node);
612         }
613
614         if (pbqp->dump_file) {
615                 fputs("<br>\nAfter reduction:<br>\n", pbqp->dump_file);
616                 dump_edge(pbqp, edge);
617         }
618
619         /* Edge has changed so we simplify it. */
620         simplify_edge(pbqp, edge);
621 }
622
623 void apply_RN(pbqp *pbqp)
624 {
625         pbqp_node  **bucket       = node_buckets[3];
626         unsigned     bucket_len   = ARR_LEN(bucket);
627         unsigned     bucket_index;
628         pbqp_node   *node         = NULL;
629         pbqp_edge   *edge;
630         vector      *node_vec;
631         vector      *vec;
632         pbqp_matrix *mat;
633         unsigned     edge_index;
634         unsigned     max_degree   = 0;
635         unsigned     node_index;
636         unsigned     node_len;
637         unsigned     min_index    = 0;
638         num          min          = INF_COSTS;
639         int          is_src;
640
641         assert(pbqp);
642
643         /* Search for node with maximum degree. */
644         for (bucket_index = 0; bucket_index < bucket_len; ++bucket_index) {
645                 pbqp_node *candidate = bucket[bucket_index];
646                 unsigned   degree    = ARR_LEN(candidate->edges);
647
648                 if (degree > max_degree) {
649                         node = candidate;
650                         max_degree = degree;
651                 }
652         }
653         assert(node);
654         node_vec = node->costs;
655         node_len = node_vec->len;
656
657         if (pbqp->dump_file) {
658                 char     txt[100];
659                 sprintf(txt, "RN-Reduction of Node n%d", node->index);
660                 dump_section(pbqp->dump_file, 2, txt);
661                 pbqp_dump_graph(pbqp);
662         }
663
664         for (node_index = 0; node_index < node_len; ++node_index) {
665                 num value = node_vec->entries[node_index].data;
666
667                 for (edge_index = 0; edge_index < max_degree; ++edge_index) {
668                         edge   = node->edges[edge_index];
669                         mat    = edge->costs;
670                         is_src = edge->src == node;
671
672                         if (is_src) {
673                                 vec = vector_copy(pbqp, edge->tgt->costs);
674                                 vector_add_matrix_row(vec, mat, node_index);
675                         } else {
676                                 vec = vector_copy(pbqp, edge->src->costs);
677                                 vector_add_matrix_col(vec, mat, node_index);
678                         }
679
680                         value = pbqp_add(value, vector_get_min(vec));
681
682                         obstack_free(&pbqp->obstack, vec);
683                 }
684
685                 if (value < min) {
686                         min = value;
687                         min_index = node_index;
688                 }
689         }
690
691         if (pbqp->dump_file) {
692                 fprintf(pbqp->dump_file, "node n%d is set to %d<br><br>\n",
693                                         node->index, min_index);
694                 fprintf(pbqp->dump_file, "Minimal cost of RN reduction: %lld<br>\n",
695                                                         min);
696         }
697
698         node->solution = min_index;
699
700         /* Now that we found the local minimum set all other costs to infinity. */
701         for (node_index = 0; node_index < node_len; ++node_index) {
702                 if (node_index != min_index) {
703                         node_vec->entries[node_index].data = INF_COSTS;
704                 }
705         }
706
707         /* Add all incident edges to edge bucket, since they are now independent. */
708         for (edge_index = 0; edge_index < max_degree; ++edge_index) {
709                 insert_into_edge_bucket(node->edges[edge_index]);
710         }
711 }
712
713 void back_propagate_RI(pbqp *pbqp, pbqp_node *node)
714 {
715         pbqp_edge   *edge;
716         pbqp_node   *other;
717         pbqp_matrix *mat;
718         vector      *vec;
719         int          is_src;
720
721         assert(pbqp);
722         assert(node);
723
724         edge = node->edges[0];
725         mat = edge->costs;
726         is_src = edge->src == node;
727         vec = node->costs;
728
729         if (is_src) {
730                 other = edge->tgt;
731                 assert(other);
732                 vector_add_matrix_col(vec, mat, other->solution);
733         } else {
734                 other = edge->src;
735                 assert(other);
736                 vector_add_matrix_row(vec, mat, other->solution);
737         }
738
739         node->solution = vector_get_min_index(vec);
740         if (pbqp->dump_file) {
741                 fprintf(pbqp->dump_file, "node n%d is set to %d<br>\n", node->index, node->solution);
742         }
743 }
744
745 void back_propagate_RII(pbqp *pbqp, pbqp_node *node)
746 {
747         pbqp_edge   *src_edge   = node->edges[0];
748         pbqp_edge   *tgt_edge   = node->edges[1];
749         int          src_is_src = src_edge->src == node;
750         int          tgt_is_src = tgt_edge->src == node;
751         pbqp_matrix *src_mat;
752         pbqp_matrix *tgt_mat;
753         pbqp_node   *src_node;
754         pbqp_node   *tgt_node;
755         vector      *vec;
756         vector      *node_vec;
757         unsigned     col_index;
758         unsigned     row_index;
759
760         assert(pbqp);
761
762         if (src_is_src) {
763                 src_node = src_edge->tgt;
764         } else {
765                 src_node = src_edge->src;
766         }
767
768         if (tgt_is_src) {
769                 tgt_node = tgt_edge->tgt;
770         } else {
771                 tgt_node = tgt_edge->src;
772         }
773
774         /* Swap nodes if necessary. */
775         if (tgt_node->index < src_node->index) {
776                 pbqp_node *tmp_node;
777                 pbqp_edge *tmp_edge;
778
779                 tmp_node = src_node;
780                 src_node = tgt_node;
781                 tgt_node = tmp_node;
782
783                 tmp_edge = src_edge;
784                 src_edge = tgt_edge;
785                 tgt_edge = tmp_edge;
786
787                 src_is_src = src_edge->src == node;
788                 tgt_is_src = tgt_edge->src == node;
789         }
790
791         src_mat = src_edge->costs;
792         tgt_mat = tgt_edge->costs;
793
794         node_vec = node->costs;
795
796         row_index = src_node->solution;
797         col_index = tgt_node->solution;
798
799         vec = vector_copy(pbqp, node_vec);
800
801         if (src_is_src) {
802                 vector_add_matrix_col(vec, src_mat, row_index);
803         } else {
804                 vector_add_matrix_row(vec, src_mat, row_index);
805         }
806
807         if (tgt_is_src) {
808                 vector_add_matrix_col(vec, tgt_mat, col_index);
809         } else {
810                 vector_add_matrix_row(vec, tgt_mat, col_index);
811         }
812
813         node->solution = vector_get_min_index(vec);
814         if (pbqp->dump_file) {
815                 fprintf(pbqp->dump_file, "node n%d is set to %d<br>\n", node->index, node->solution);
816         }
817
818         obstack_free(&pbqp->obstack, vec);
819 }
820
821 int node_is_reduced(pbqp_node *node)
822 {
823         if (!reduced_bucket) return 0;
824
825         assert(node);
826         if (ARR_LEN(node->edges) == 0) return 1;
827
828         unsigned bucket_length = ARR_LEN(reduced_bucket);
829         unsigned bucket_index  = node->bucket_index;
830
831         return bucket_index < bucket_length && reduced_bucket[bucket_index] == node;
832 }