3 * Copyright 2007 Free Software Foundation, Inc.
5 * This file is part of GNU Radio
7 * GNU Radio is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3, or (at your option)
12 * GNU Radio is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with GNU Radio; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street,
20 * Boston, MA 02110-1301, USA.
27 #include <gr_flowgraph.h>
28 #include <gr_io_signature.h>
32 #define GR_FLOWGRAPH_DEBUG 0
38 gr_flowgraph_sptr gr_make_flowgraph()
40 return gr_flowgraph_sptr(new gr_flowgraph());
43 gr_flowgraph::gr_flowgraph()
47 gr_flowgraph::~gr_flowgraph()
52 gr_flowgraph::connect(const gr_endpoint &src, const gr_endpoint &dst)
54 check_valid_port(src.block()->output_signature(), src.port());
55 check_valid_port(dst.block()->input_signature(), dst.port());
56 check_dst_not_used(dst);
57 check_type_match(src, dst);
59 // All ist klar, Herr Kommisar
60 d_edges.push_back(gr_edge(src,dst));
64 gr_flowgraph::disconnect(const gr_endpoint &src, const gr_endpoint &dst)
66 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
67 if (src == p->src() && dst == p->dst()) {
73 std::stringstream msg;
74 msg << "cannot disconnect edge " << gr_edge(src, dst) << ", not found";
75 throw std::invalid_argument(msg.str());
79 gr_flowgraph::validate()
81 d_blocks = calc_used_blocks();
83 for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
84 std::vector<int> used_ports;
85 int ninputs, noutputs;
87 if (GR_FLOWGRAPH_DEBUG)
88 std::cout << "Validating block: " << (*p) << std::endl;
90 used_ports = calc_used_ports(*p, true); // inputs
91 ninputs = used_ports.size();
92 check_contiguity(*p, used_ports, true); // inputs
94 used_ports = calc_used_ports(*p, false); // outputs
95 noutputs = used_ports.size();
96 check_contiguity(*p, used_ports, false); // outputs
98 if (!((*p)->check_topology(ninputs, noutputs))) {
99 std::stringstream msg;
100 msg << "check topology failed on " << (*p)
101 << " using ninputs=" << ninputs
102 << ", noutputs=" << noutputs;
103 throw std::runtime_error(msg.str());
109 gr_flowgraph::clear()
111 // Boost shared pointers will deallocate as needed
117 gr_flowgraph::check_valid_port(gr_io_signature_sptr sig, int port)
119 std::stringstream msg;
122 msg << "negative port number " << port << " is invalid";
123 throw std::invalid_argument(msg.str());
126 int max = sig->max_streams();
127 if (max != gr_io_signature::IO_INFINITE && port >= max) {
128 msg << "port number " << port << " exceeds max of ";
133 throw std::invalid_argument(msg.str());
138 gr_flowgraph::check_dst_not_used(const gr_endpoint &dst)
140 // A destination is in use if it is already on the edge list
141 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
142 if (p->dst() == dst) {
143 std::stringstream msg;
144 msg << "destination already in use by edge " << (*p);
145 throw std::invalid_argument(msg.str());
150 gr_flowgraph::check_type_match(const gr_endpoint &src, const gr_endpoint &dst)
152 int src_size = src.block()->output_signature()->sizeof_stream_item(src.port());
153 int dst_size = dst.block()->input_signature()->sizeof_stream_item(dst.port());
155 if (src_size != dst_size) {
156 std::stringstream msg;
157 msg << "itemsize mismatch: " << src << " using " << src_size
158 << ", " << dst << " using " << dst_size;
159 throw std::invalid_argument(msg.str());
163 gr_basic_block_vector_t
164 gr_flowgraph::calc_used_blocks()
166 gr_basic_block_vector_t tmp, result;
167 std::insert_iterator<gr_basic_block_vector_t> inserter(result, result.begin());
169 // Collect all blocks in the edge list
170 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
171 tmp.push_back(p->src().block());
172 tmp.push_back(p->dst().block());
175 // Return vector of unique blocks
176 sort(tmp.begin(), tmp.end());
177 unique_copy(tmp.begin(), tmp.end(), inserter);
182 gr_flowgraph::calc_used_ports(gr_basic_block_sptr block, bool check_inputs)
184 std::vector<int> tmp, result;
185 std::insert_iterator<std::vector<int> > inserter(result, result.begin());
187 // Collect all seen ports
188 gr_edge_vector_t edges = calc_connections(block, check_inputs);
189 for (gr_edge_viter_t p = edges.begin(); p != edges.end(); p++) {
190 if (check_inputs == true)
191 tmp.push_back(p->dst().port());
193 tmp.push_back(p->src().port());
196 // Return vector of unique values
197 std::sort(tmp.begin(), tmp.end());
198 std::unique_copy(tmp.begin(), tmp.end(), inserter);
203 gr_flowgraph::calc_connections(gr_basic_block_sptr block, bool check_inputs)
205 gr_edge_vector_t result;
207 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
209 if (p->dst().block() == block)
210 result.push_back(*p);
213 if (p->src().block() == block)
214 result.push_back(*p);
218 return result; // assumes no duplicates
222 gr_flowgraph::check_contiguity(gr_basic_block_sptr block,
223 const std::vector<int> &used_ports,
226 std::stringstream msg;
228 gr_io_signature_sptr sig =
229 check_inputs ? block->input_signature() : block->output_signature();
231 int nports = used_ports.size();
232 int min_ports = sig->min_streams();
233 int max_ports = sig->max_streams();
235 if (nports == 0 && min_ports == 0)
238 if (nports < min_ports) {
239 msg << block << ": insufficient connected "
240 << (check_inputs ? "input ports " : "output ports ")
241 << "(" << min_ports << " needed, " << nports << " connected)";
242 throw std::runtime_error(msg.str());
245 if (nports > max_ports && max_ports != gr_io_signature::IO_INFINITE) {
246 msg << block << ": too many connected "
247 << (check_inputs ? "input ports " : "output ports ")
248 << "(" << max_ports << " allowed, " << nports << " connected)";
249 throw std::runtime_error(msg.str());
252 if (used_ports[nports-1]+1 != nports) {
253 for (int i = 0; i < nports; i++) {
254 if (used_ports[i] != i) {
255 msg << block << ": missing connection "
256 << (check_inputs ? "to input port " : "from output port ")
258 throw std::runtime_error(msg.str());
264 gr_basic_block_vector_t
265 gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block, int port)
267 gr_basic_block_vector_t tmp, result;
268 std::insert_iterator<gr_basic_block_vector_t> inserter(result, result.begin());
270 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
271 if (p->src() == gr_endpoint(block, port))
272 tmp.push_back(p->dst().block());
275 sort(tmp.begin(), tmp.end());
276 unique_copy(tmp.begin(), tmp.end(), inserter);
280 gr_basic_block_vector_t
281 gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block)
283 gr_basic_block_vector_t tmp, result;
284 std::insert_iterator<gr_basic_block_vector_t> inserter(result, result.begin());
286 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
287 if (p->src().block() == block)
288 tmp.push_back(p->dst().block());
291 sort(tmp.begin(), tmp.end());
292 unique_copy(tmp.begin(), tmp.end(), inserter);
297 gr_flowgraph::calc_upstream_edges(gr_basic_block_sptr block)
299 gr_edge_vector_t result;
301 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
302 if (p->dst().block() == block)
303 result.push_back(*p);
305 return result; // Assume no duplicates
309 gr_flowgraph::has_block_p(gr_basic_block_sptr block)
311 gr_basic_block_viter_t result;
312 result = std::find(d_blocks.begin(), d_blocks.end(), block);
313 return (result != d_blocks.end());
317 gr_flowgraph::calc_upstream_edge(gr_basic_block_sptr block, int port)
321 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
322 if (p->dst() == gr_endpoint(block, port)) {
331 std::vector<gr_basic_block_vector_t>
332 gr_flowgraph::partition()
334 std::vector<gr_basic_block_vector_t> result;
335 gr_basic_block_vector_t blocks = calc_used_blocks();
336 gr_basic_block_vector_t graph;
338 while (blocks.size() > 0) {
339 graph = calc_reachable_blocks(blocks[0], blocks);
340 assert(graph.size());
341 result.push_back(topological_sort(graph));
343 for (gr_basic_block_viter_t p = graph.begin(); p != graph.end(); p++)
344 blocks.erase(find(blocks.begin(), blocks.end(), *p));
350 gr_basic_block_vector_t
351 gr_flowgraph::calc_reachable_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
353 gr_basic_block_vector_t result;
355 // Mark all blocks as unvisited
356 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++)
357 (*p)->set_color(gr_basic_block::WHITE);
359 // Recursively mark all reachable blocks
360 reachable_dfs_visit(block, blocks);
362 // Collect all the blocks that have been visited
363 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++)
364 if ((*p)->color() == gr_basic_block::BLACK)
365 result.push_back(*p);
370 // Recursively mark all reachable blocks from given block and block list
372 gr_flowgraph::reachable_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
374 // Mark the current one as visited
375 block->set_color(gr_basic_block::BLACK);
377 // Recurse into adjacent vertices
378 gr_basic_block_vector_t adjacent = calc_adjacent_blocks(block, blocks);
380 for (gr_basic_block_viter_t p = adjacent.begin(); p != adjacent.end(); p++)
381 if ((*p)->color() == gr_basic_block::WHITE)
382 reachable_dfs_visit(*p, blocks);
385 // Return a list of block adjacent to a given block along any edge
386 gr_basic_block_vector_t
387 gr_flowgraph::calc_adjacent_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
389 gr_basic_block_vector_t tmp, result;
390 std::insert_iterator<gr_basic_block_vector_t> inserter(result, result.begin());
392 // Find any blocks that are inputs or outputs
393 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
395 if (p->src().block() == block)
396 tmp.push_back(p->dst().block());
397 if (p->dst().block() == block)
398 tmp.push_back(p->src().block());
402 sort(tmp.begin(), tmp.end());
403 unique_copy(tmp.begin(), tmp.end(), inserter);
407 gr_basic_block_vector_t
408 gr_flowgraph::topological_sort(gr_basic_block_vector_t &blocks)
410 gr_basic_block_vector_t tmp;
411 gr_basic_block_vector_t result;
412 tmp = sort_sources_first(blocks);
414 // Start 'em all white
415 for (gr_basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++)
416 (*p)->set_color(gr_basic_block::WHITE);
418 for (gr_basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++) {
419 if ((*p)->color() == gr_basic_block::WHITE)
420 topological_dfs_visit(*p, result);
423 reverse(result.begin(), result.end());
427 gr_basic_block_vector_t
428 gr_flowgraph::sort_sources_first(gr_basic_block_vector_t &blocks)
430 gr_basic_block_vector_t sources, nonsources, result;
432 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
434 sources.push_back(*p);
436 nonsources.push_back(*p);
439 for (gr_basic_block_viter_t p = sources.begin(); p != sources.end(); p++)
440 result.push_back(*p);
442 for (gr_basic_block_viter_t p = nonsources.begin(); p != nonsources.end(); p++)
443 result.push_back(*p);
449 gr_flowgraph::source_p(gr_basic_block_sptr block)
451 return (calc_upstream_edges(block).size() == 0);
455 gr_flowgraph::topological_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &output)
457 block->set_color(gr_basic_block::GREY);
458 gr_basic_block_vector_t blocks(calc_downstream_blocks(block));
460 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
461 switch ((*p)->color()) {
462 case gr_basic_block::WHITE:
463 topological_dfs_visit(*p, output);
466 case gr_basic_block::GREY:
467 throw std::runtime_error("flow graph has loops!");
469 case gr_basic_block::BLACK:
473 throw std::runtime_error("invalid color on block!");
477 block->set_color(gr_basic_block::BLACK);
478 output.push_back(block);