diff --git a/maxflow/graph.cpp b/maxflow/graph.cpp index 40e348c..17197dc 100644 --- a/maxflow/graph.cpp +++ b/maxflow/graph.cpp @@ -33,6 +33,7 @@ namespace maxflow { template Graph::Graph(int node_num_max, int edge_num_max, void (*err_function)(const char *)) : node_num(0), + computing(0), nodeptr_block(NULL), error_function(err_function) { @@ -70,6 +71,7 @@ template node_last = nodes; arc_last = arcs; node_num = 0; + computing = 0; if (nodeptr_block) { @@ -603,6 +605,189 @@ template /***********************************************************************/ +template + void Graph::maxflow_prepare(bool reuse_trees, Block* _changed_list) +{ + if (computing) + { + if (error_function) + (*error_function)("Do not call maxflow_prepare() while you did not finish with maxflow_compute()!"); + exit(1); + } + + compute_node = NULL; + compute_reuse_trees = reuse_trees; + compute_iteration = 0; + compute_prev_progress = 0; + compute_max = node_num; + compute_step = node_num / 10; + compute_step = compute_step > 1000 ? compute_step : 1000; + computing = 1; + + if (!nodeptr_block) + { + nodeptr_block = new DBlock(NODEPTR_BLOCK_SIZE, error_function); + } + + changed_list = _changed_list; + if (maxflow_iteration == 0 && reuse_trees) { if (error_function) (*error_function)("reuse_trees cannot be used in the first call to maxflow()!"); exit(1); } + if (changed_list && !reuse_trees) { if (error_function) (*error_function)("changed_list cannot be used without reuse_trees!"); exit(1); } + + if (reuse_trees) maxflow_reuse_trees_init(); + else maxflow_init(); +} + +template + bool Graph::maxflow_compute(float *progress) +{ + bool more_to_do = 1; + node *i, *j; + arc *a; + nodeptr *np, *np_next; + + if (! computing) + { + if (error_function) + (*error_function)("You did not call maxflow_prepare()!"); + exit(1); + } + + while ( 1 ) + { + // test_consistency(compute_node); + + if ((i=compute_node)) + { + i -> next = NULL; /* remove active flag */ + if (!i->parent) i = NULL; + } + if (!i) + { + if (!(i = next_active())) + { + more_to_do = 0; + break; + } + + compute_iteration++; + } + + /* growth */ + if (!i->is_sink) + { + /* grow source tree */ + for (a=i->first; a; a=a->next) + if (a->r_cap) + { + j = a -> head; + if (!j->parent) + { + j -> is_sink = 0; + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + if (! j->next) + compute_max++; + set_active(j); + add_to_changed_list(j); + } + else if (j->is_sink) break; + else if (j->TS <= i->TS && + j->DIST > i->DIST) + { + /* heuristic - trying to make the distance from j to the source shorter */ + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + } + } + } + else + { + /* grow sink tree */ + for (a=i->first; a; a=a->next) + if (a->sister->r_cap) + { + j = a -> head; + if (!j->parent) + { + j -> is_sink = 1; + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + if (! j->next) + compute_max++; + set_active(j); + add_to_changed_list(j); + } + else if (!j->is_sink) { a = a -> sister; break; } + else if (j->TS <= i->TS && + j->DIST > i->DIST) + { + /* heuristic - trying to make the distance from j to the sink shorter */ + j -> parent = a -> sister; + j -> TS = i -> TS; + j -> DIST = i -> DIST + 1; + } + } + } + + TIME ++; + + if (a) + { + i -> next = i; /* set active flag */ + compute_node = i; + + /* augmentation */ + augment(a); + /* augmentation end */ + + /* adoption */ + while ((np=orphan_first)) + { + np_next = np -> next; + np -> next = NULL; + + while ((np=orphan_first)) + { + orphan_first = np -> next; + i = np -> ptr; + nodeptr_block -> Delete(np); + if (!orphan_first) orphan_last = NULL; + if (i->is_sink) process_sink_orphan(i); + else process_source_orphan(i); + } + + orphan_first = np_next; + } + /* adoption end */ + } + else compute_node = NULL; + + if (compute_iteration > compute_prev_progress + compute_step) + { + compute_prev_progress = compute_iteration; + *progress = (float) compute_iteration / compute_max; + break; + } + } + // test_consistency(); + + if (! more_to_do && (!compute_reuse_trees || (maxflow_iteration % 64) == 0)) + { + delete nodeptr_block; + nodeptr_block = NULL; + + maxflow_iteration++; + + *progress = 1.0; + computing = 0; + } + + return more_to_do; +} + template flowtype Graph::maxflow(bool reuse_trees, Block* _changed_list) { @@ -610,6 +795,13 @@ template arc *a; nodeptr *np, *np_next; + if (computing) + { + if (error_function) + (*error_function)("Do not call maxflow() while iterating with maxflow_prepare() and maxflow_compute()!"); + exit(1); + } + if (!nodeptr_block) { nodeptr_block = new DBlock(NODEPTR_BLOCK_SIZE, error_function); diff --git a/maxflow/graph.h b/maxflow/graph.h index e723871..9c36d9a 100644 --- a/maxflow/graph.h +++ b/maxflow/graph.h @@ -130,6 +130,24 @@ template class Graph // FOR DESCRIPTION OF changed_list, SEE remove_from_changed_list(). flowtype maxflow(bool reuse_trees = false, Block* changed_list = NULL); + // Alternative to maxflow(). You must first call maxflow_prepare() + // with the same arguments you would have passed on maxflow(), then + // call maxflow_compute() in a loop, for as long as it doesn't + // return 0. This can be used when you wish to get approximate + // progression information, for instance to give GUI feedback. + // The returned progress value is a float between 0.0 and 1.0. + // Usage example: + // + // float progress; + // g->maxflow_prepare(); + // while (g->maxflow_compute(&progress)) + // report_progression(progress); + // + // IMPORTANT: do not mix maxflow() and maxflow_prepare/maxflow_compute(). + void maxflow_prepare(bool reuse_trees = false, Block* changed_list = NULL); + bool maxflow_compute(float *progress); + + // After the maxflow is computed, this function returns to which // segment the node 'i' belongs (Graph::SOURCE or Graph::SINK). // @@ -361,6 +379,14 @@ template class Graph void process_sink_orphan(node *i); void test_consistency(node* current_node=NULL); // debug function + + bool computing; + node *compute_node; + bool compute_reuse_trees; + int compute_max; + int compute_iteration; + int compute_prev_progress; + int compute_step; };