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