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