Implemented RN reduction, which was the last step to a working PBQP solver.
[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-Reduktion 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-Reduktion 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         for (node_index = 0; node_index < node_len; ++node_index) {
619                 num value = 0;
620
621                 for (edge_index = 0; edge_index < edge_len; ++edge_index) {
622                         edge   = node->edges[edge_index];
623                         mat    = edge->costs;
624                         is_src = edge->src == node;
625
626                         if (is_src) {
627                                 vec = vector_copy(pbqp, edge->tgt->costs);
628                                 vector_add_matrix_row(vec, mat, node_index);
629                         } else {
630                                 vec = vector_copy(pbqp, edge->src->costs);
631                                 vector_add_matrix_col(vec, mat, node_index);
632                         }
633
634                         value = pbqp_add(value, vector_get_min(vec));
635
636                         obstack_free(&pbqp->obstack, vec);
637                 }
638
639                 if (value < min) {
640                         min = value;
641                         min_index = node_index;
642                 }
643         }
644
645         node->solution = min_index;
646
647         /* Now that we found the local minimum set all other costs to infinity. */
648         for (node_index = 0; node_index < node_len; ++node_index) {
649                 if (node_index != min_index) {
650                         node_vec->entries[node_index].data = INF_COSTS;
651                 }
652         }
653
654         /* Add all incident edges to edge bucket, since they are now independent. */
655         for (edge_index = 0; edge_index < edge_len; ++edge_index) {
656                 insert_into_edge_bucket(node->edges[edge_index]);
657         }
658 }
659
660 void back_propagate_RI(pbqp *pbqp, pbqp_node *node)
661 {
662         pbqp_edge   *edge;
663         pbqp_node   *other;
664         pbqp_matrix *mat;
665         vector      *vec;
666         int          is_src;
667
668         assert(pbqp);
669         assert(node);
670
671         edge = node->edges[0];
672         mat = edge->costs;
673         is_src = edge->src == node;
674         vec = node->costs;
675
676         if (is_src) {
677                 other = edge->tgt;
678                 assert(other);
679                 vector_add_matrix_col(vec, mat, other->solution);
680         } else {
681                 other = edge->src;
682                 assert(other);
683                 vector_add_matrix_row(vec, mat, other->solution);
684         }
685
686         node->solution = vector_get_min_index(vec);
687         if (pbqp->dump_file) {
688                 fprintf(pbqp->dump_file, "node n%d is set to %d<br>\n", node->index, node->solution);
689         }
690 }
691
692 void back_propagate_RII(pbqp *pbqp, pbqp_node *node)
693 {
694         pbqp_edge   *src_edge   = node->edges[0];
695         pbqp_edge   *tgt_edge   = node->edges[1];
696         int          src_is_src = src_edge->src == node;
697         int          tgt_is_src = tgt_edge->src == node;
698         pbqp_matrix *src_mat;
699         pbqp_matrix *tgt_mat;
700         pbqp_node   *src_node;
701         pbqp_node   *tgt_node;
702         vector      *vec;
703         vector      *node_vec;
704         unsigned     col_index;
705         unsigned     row_index;
706
707         assert(pbqp);
708
709         if (src_is_src) {
710                 src_node = src_edge->tgt;
711         } else {
712                 src_node = src_edge->src;
713         }
714
715         if (tgt_is_src) {
716                 tgt_node = tgt_edge->tgt;
717         } else {
718                 tgt_node = tgt_edge->src;
719         }
720
721         /* Swap nodes if necessary. */
722         if (tgt_node->index < src_node->index) {
723                 pbqp_node *tmp_node;
724                 pbqp_edge *tmp_edge;
725
726                 tmp_node = src_node;
727                 src_node = tgt_node;
728                 tgt_node = tmp_node;
729
730                 tmp_edge = src_edge;
731                 src_edge = tgt_edge;
732                 tgt_edge = tmp_edge;
733
734                 src_is_src = src_edge->src == node;
735                 tgt_is_src = tgt_edge->src == node;
736         }
737
738         src_mat = src_edge->costs;
739         tgt_mat = tgt_edge->costs;
740
741         node_vec = node->costs;
742
743         row_index = src_node->solution;
744         col_index = tgt_node->solution;
745
746         vec = vector_copy(pbqp, node_vec);
747
748         if (src_is_src) {
749                 vector_add_matrix_col(vec, src_mat, row_index);
750         } else {
751                 vector_add_matrix_row(vec, src_mat, row_index);
752         }
753
754         if (tgt_is_src) {
755                 vector_add_matrix_col(vec, tgt_mat, col_index);
756         } else {
757                 vector_add_matrix_row(vec, tgt_mat, col_index);
758         }
759
760         node->solution = vector_get_min_index(vec);
761         if (pbqp->dump_file) {
762                 fprintf(pbqp->dump_file, "node n%d is set to %d<br>\n", node->index, node->solution);
763         }
764
765         obstack_free(&pbqp->obstack, vec);
766 }
767
768 int node_is_reduced(pbqp_node *node)
769 {
770         if (!reduced_bucket) return 0;
771
772         assert(node);
773         if (ARR_LEN(node->edges) == 0) return 1;
774
775         unsigned bucket_length = ARR_LEN(reduced_bucket);
776         unsigned bucket_index  = node->bucket_index;
777
778         return bucket_index < bucket_length && reduced_bucket[bucket_index] == node;
779 }