Also take a look at the node costs of the current node during RN reduction.
[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 %d.", 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         pbqp_node   *node       = bucket[bucket_len - 1];
628         pbqp_edge   *edge;
629         vector      *node_vec   = node->costs;
630         vector      *vec;
631         pbqp_matrix *mat;
632         unsigned     edge_index;
633         unsigned     edge_len   = ARR_LEN(node->edges);
634         unsigned     node_index;
635         unsigned     node_len   = node_vec->len;
636         unsigned     min_index  = 0;
637         num          min        = INF_COSTS;
638         int          is_src;
639
640         assert(pbqp);
641
642         if (pbqp->dump_file) {
643                 char     txt[100];
644                 sprintf(txt, "RN-Reduction of Node n%d", node->index);
645                 dump_section(pbqp->dump_file, 2, txt);
646                 pbqp_dump_graph(pbqp);
647         }
648
649         for (node_index = 0; node_index < node_len; ++node_index) {
650                 num value = node_vec->entries[node_index].data;
651
652                 for (edge_index = 0; edge_index < edge_len; ++edge_index) {
653                         edge   = node->edges[edge_index];
654                         mat    = edge->costs;
655                         is_src = edge->src == node;
656
657                         if (is_src) {
658                                 vec = vector_copy(pbqp, edge->tgt->costs);
659                                 vector_add_matrix_row(vec, mat, node_index);
660                         } else {
661                                 vec = vector_copy(pbqp, edge->src->costs);
662                                 vector_add_matrix_col(vec, mat, node_index);
663                         }
664
665                         value = pbqp_add(value, vector_get_min(vec));
666
667                         obstack_free(&pbqp->obstack, vec);
668                 }
669
670                 if (value < min) {
671                         min = value;
672                         min_index = node_index;
673                 }
674         }
675
676         if (pbqp->dump_file) {
677                 fprintf(pbqp->dump_file, "node n%d is set to %d<br><br>\n",
678                                         node->index, min_index);
679                 fprintf(pbqp->dump_file, "Minimal cost of RN reduction: %d<br>\n",
680                                                         min);
681         }
682
683         node->solution = min_index;
684
685         /* Now that we found the local minimum set all other costs to infinity. */
686         for (node_index = 0; node_index < node_len; ++node_index) {
687                 if (node_index != min_index) {
688                         node_vec->entries[node_index].data = INF_COSTS;
689                 }
690         }
691
692         /* Add all incident edges to edge bucket, since they are now independent. */
693         for (edge_index = 0; edge_index < edge_len; ++edge_index) {
694                 insert_into_edge_bucket(node->edges[edge_index]);
695         }
696 }
697
698 void back_propagate_RI(pbqp *pbqp, pbqp_node *node)
699 {
700         pbqp_edge   *edge;
701         pbqp_node   *other;
702         pbqp_matrix *mat;
703         vector      *vec;
704         int          is_src;
705
706         assert(pbqp);
707         assert(node);
708
709         edge = node->edges[0];
710         mat = edge->costs;
711         is_src = edge->src == node;
712         vec = node->costs;
713
714         if (is_src) {
715                 other = edge->tgt;
716                 assert(other);
717                 vector_add_matrix_col(vec, mat, other->solution);
718         } else {
719                 other = edge->src;
720                 assert(other);
721                 vector_add_matrix_row(vec, mat, other->solution);
722         }
723
724         node->solution = vector_get_min_index(vec);
725         if (pbqp->dump_file) {
726                 fprintf(pbqp->dump_file, "node n%d is set to %d<br>\n", node->index, node->solution);
727         }
728 }
729
730 void back_propagate_RII(pbqp *pbqp, pbqp_node *node)
731 {
732         pbqp_edge   *src_edge   = node->edges[0];
733         pbqp_edge   *tgt_edge   = node->edges[1];
734         int          src_is_src = src_edge->src == node;
735         int          tgt_is_src = tgt_edge->src == node;
736         pbqp_matrix *src_mat;
737         pbqp_matrix *tgt_mat;
738         pbqp_node   *src_node;
739         pbqp_node   *tgt_node;
740         vector      *vec;
741         vector      *node_vec;
742         unsigned     col_index;
743         unsigned     row_index;
744
745         assert(pbqp);
746
747         if (src_is_src) {
748                 src_node = src_edge->tgt;
749         } else {
750                 src_node = src_edge->src;
751         }
752
753         if (tgt_is_src) {
754                 tgt_node = tgt_edge->tgt;
755         } else {
756                 tgt_node = tgt_edge->src;
757         }
758
759         /* Swap nodes if necessary. */
760         if (tgt_node->index < src_node->index) {
761                 pbqp_node *tmp_node;
762                 pbqp_edge *tmp_edge;
763
764                 tmp_node = src_node;
765                 src_node = tgt_node;
766                 tgt_node = tmp_node;
767
768                 tmp_edge = src_edge;
769                 src_edge = tgt_edge;
770                 tgt_edge = tmp_edge;
771
772                 src_is_src = src_edge->src == node;
773                 tgt_is_src = tgt_edge->src == node;
774         }
775
776         src_mat = src_edge->costs;
777         tgt_mat = tgt_edge->costs;
778
779         node_vec = node->costs;
780
781         row_index = src_node->solution;
782         col_index = tgt_node->solution;
783
784         vec = vector_copy(pbqp, node_vec);
785
786         if (src_is_src) {
787                 vector_add_matrix_col(vec, src_mat, row_index);
788         } else {
789                 vector_add_matrix_row(vec, src_mat, row_index);
790         }
791
792         if (tgt_is_src) {
793                 vector_add_matrix_col(vec, tgt_mat, col_index);
794         } else {
795                 vector_add_matrix_row(vec, tgt_mat, col_index);
796         }
797
798         node->solution = vector_get_min_index(vec);
799         if (pbqp->dump_file) {
800                 fprintf(pbqp->dump_file, "node n%d is set to %d<br>\n", node->index, node->solution);
801         }
802
803         obstack_free(&pbqp->obstack, vec);
804 }
805
806 int node_is_reduced(pbqp_node *node)
807 {
808         if (!reduced_bucket) return 0;
809
810         assert(node);
811         if (ARR_LEN(node->edges) == 0) return 1;
812
813         unsigned bucket_length = ARR_LEN(reduced_bucket);
814         unsigned bucket_index  = node->bucket_index;
815
816         return bucket_index < bucket_length && reduced_bucket[bucket_index] == node;
817 }