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 throw std::invalid_argument("edge to disconnect not found");
77 gr_flowgraph::validate()
79 d_blocks = calc_used_blocks();
81 for (gr_basic_block_viter_t p = d_blocks.begin(); p != d_blocks.end(); p++) {
82 std::vector<int> used_ports;
83 int ninputs, noutputs;
85 if (GR_FLOWGRAPH_DEBUG)
86 std::cout << "Validating block: " << (*p) << std::endl;
88 used_ports = calc_used_ports(*p, true); // inputs
89 ninputs = used_ports.size();
90 check_contiguity(*p, used_ports, true); // inputs
92 used_ports = calc_used_ports(*p, false); // outputs
93 noutputs = used_ports.size();
94 check_contiguity(*p, used_ports, false); // outputs
96 if (!((*p)->check_topology(ninputs, noutputs)))
97 throw std::runtime_error("check topology failed");
102 gr_flowgraph::clear()
104 // Boost shared pointers will deallocate as needed
110 gr_flowgraph::check_valid_port(gr_io_signature_sptr sig, int port)
113 throw std::invalid_argument("negative port number");
114 if (sig->max_streams() >= 0 && port >= sig->max_streams())
115 throw std::invalid_argument("port number exceeds max");
119 gr_flowgraph::check_dst_not_used(const gr_endpoint &dst)
121 // A destination is in use if it is already on the edge list
122 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
124 throw std::invalid_argument("dst already in use");
128 gr_flowgraph::check_type_match(const gr_endpoint &src, const gr_endpoint &dst)
130 int src_size = src.block()->output_signature()->sizeof_stream_item(src.port());
131 int dst_size = dst.block()->input_signature()->sizeof_stream_item(dst.port());
133 if (src_size != dst_size)
134 throw std::invalid_argument("itemsize mismatch between src and dst");
137 gr_basic_block_vector_t
138 gr_flowgraph::calc_used_blocks()
140 gr_basic_block_vector_t tmp, result;
141 std::insert_iterator<gr_basic_block_vector_t> inserter(result, result.begin());
143 // Collect all blocks in the edge list
144 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
145 tmp.push_back(p->src().block());
146 tmp.push_back(p->dst().block());
149 // Return vector of unique blocks
150 sort(tmp.begin(), tmp.end());
151 unique_copy(tmp.begin(), tmp.end(), inserter);
156 gr_flowgraph::calc_used_ports(gr_basic_block_sptr block, bool check_inputs)
158 std::vector<int> tmp, result;
159 std::insert_iterator<std::vector<int> > inserter(result, result.begin());
161 // Collect all seen ports
162 gr_edge_vector_t edges = calc_connections(block, check_inputs);
163 for (gr_edge_viter_t p = edges.begin(); p != edges.end(); p++) {
164 if (check_inputs == true)
165 tmp.push_back(p->dst().port());
167 tmp.push_back(p->src().port());
170 // Return vector of unique values
171 std::sort(tmp.begin(), tmp.end());
172 std::unique_copy(tmp.begin(), tmp.end(), inserter);
177 gr_flowgraph::calc_connections(gr_basic_block_sptr block, bool check_inputs)
179 gr_edge_vector_t result;
181 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
183 if (p->dst().block() == block)
184 result.push_back(*p);
187 if (p->src().block() == block)
188 result.push_back(*p);
192 return result; // assumes no duplicates
196 gr_flowgraph::check_contiguity(gr_basic_block_sptr block,
197 const std::vector<int> &used_ports,
200 gr_io_signature_sptr sig =
201 check_inputs ? block->input_signature() : block->output_signature();
203 int nports = used_ports.size();
204 int min_ports = sig->min_streams();
210 throw std::runtime_error("insufficient ports");
213 if (used_ports[nports-1]+1 != nports) {
214 for (int i = 0; i < nports; i++)
215 if (used_ports[i] != i)
216 throw std::runtime_error("missing input assignment");
220 gr_basic_block_vector_t
221 gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block, int port)
223 gr_basic_block_vector_t tmp, result;
224 std::insert_iterator<gr_basic_block_vector_t> inserter(result, result.begin());
226 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
227 if (p->src() == gr_endpoint(block, port))
228 tmp.push_back(p->dst().block());
231 sort(tmp.begin(), tmp.end());
232 unique_copy(tmp.begin(), tmp.end(), inserter);
236 gr_basic_block_vector_t
237 gr_flowgraph::calc_downstream_blocks(gr_basic_block_sptr block)
239 gr_basic_block_vector_t tmp, result;
240 std::insert_iterator<gr_basic_block_vector_t> inserter(result, result.begin());
242 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
243 if (p->src().block() == block)
244 tmp.push_back(p->dst().block());
247 sort(tmp.begin(), tmp.end());
248 unique_copy(tmp.begin(), tmp.end(), inserter);
253 gr_flowgraph::calc_upstream_edges(gr_basic_block_sptr block)
255 gr_edge_vector_t result;
257 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++)
258 if (p->dst().block() == block)
259 result.push_back(*p);
261 return result; // Assume no duplicates
265 gr_flowgraph::has_block_p(gr_basic_block_sptr block)
267 gr_basic_block_viter_t result;
268 result = std::find(d_blocks.begin(), d_blocks.end(), block);
269 return (result != d_blocks.end());
273 gr_flowgraph::calc_upstream_edge(gr_basic_block_sptr block, int port)
277 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
278 if (p->dst() == gr_endpoint(block, port)) {
287 std::vector<gr_basic_block_vector_t>
288 gr_flowgraph::partition()
290 std::vector<gr_basic_block_vector_t> result;
291 gr_basic_block_vector_t blocks = calc_used_blocks();
292 gr_basic_block_vector_t graph;
294 while (blocks.size() > 0) {
295 graph = calc_reachable_blocks(blocks[0], blocks);
296 assert(graph.size());
297 result.push_back(topological_sort(graph));
299 for (gr_basic_block_viter_t p = graph.begin(); p != graph.end(); p++)
300 blocks.erase(find(blocks.begin(), blocks.end(), *p));
306 gr_basic_block_vector_t
307 gr_flowgraph::calc_reachable_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
309 gr_basic_block_vector_t result;
311 // Mark all blocks as unvisited
312 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++)
313 (*p)->set_color(gr_basic_block::WHITE);
315 // Recursively mark all reachable blocks
316 reachable_dfs_visit(block, blocks);
318 // Collect all the blocks that have been visited
319 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++)
320 if ((*p)->color() == gr_basic_block::BLACK)
321 result.push_back(*p);
326 // Recursively mark all reachable blocks from given block and block list
328 gr_flowgraph::reachable_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
330 // Mark the current one as visited
331 block->set_color(gr_basic_block::BLACK);
333 // Recurse into adjacent vertices
334 gr_basic_block_vector_t adjacent = calc_adjacent_blocks(block, blocks);
336 for (gr_basic_block_viter_t p = adjacent.begin(); p != adjacent.end(); p++)
337 if ((*p)->color() == gr_basic_block::WHITE)
338 reachable_dfs_visit(*p, blocks);
341 // Return a list of block adjacent to a given block along any edge
342 gr_basic_block_vector_t
343 gr_flowgraph::calc_adjacent_blocks(gr_basic_block_sptr block, gr_basic_block_vector_t &blocks)
345 gr_basic_block_vector_t tmp, result;
346 std::insert_iterator<gr_basic_block_vector_t> inserter(result, result.begin());
348 // Find any blocks that are inputs or outputs
349 for (gr_edge_viter_t p = d_edges.begin(); p != d_edges.end(); p++) {
351 if (p->src().block() == block)
352 tmp.push_back(p->dst().block());
353 if (p->dst().block() == block)
354 tmp.push_back(p->src().block());
358 sort(tmp.begin(), tmp.end());
359 unique_copy(tmp.begin(), tmp.end(), inserter);
363 gr_basic_block_vector_t
364 gr_flowgraph::topological_sort(gr_basic_block_vector_t &blocks)
366 gr_basic_block_vector_t tmp;
367 gr_basic_block_vector_t result;
368 tmp = sort_sources_first(blocks);
370 // Start 'em all white
371 for (gr_basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++)
372 (*p)->set_color(gr_basic_block::WHITE);
374 for (gr_basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++) {
375 if ((*p)->color() == gr_basic_block::WHITE)
376 topological_dfs_visit(*p, result);
379 reverse(result.begin(), result.end());
383 gr_basic_block_vector_t
384 gr_flowgraph::sort_sources_first(gr_basic_block_vector_t &blocks)
386 gr_basic_block_vector_t sources, nonsources, result;
388 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
390 sources.push_back(*p);
392 nonsources.push_back(*p);
395 for (gr_basic_block_viter_t p = sources.begin(); p != sources.end(); p++)
396 result.push_back(*p);
398 for (gr_basic_block_viter_t p = nonsources.begin(); p != nonsources.end(); p++)
399 result.push_back(*p);
405 gr_flowgraph::source_p(gr_basic_block_sptr block)
407 return (calc_upstream_edges(block).size() == 0);
411 gr_flowgraph::topological_dfs_visit(gr_basic_block_sptr block, gr_basic_block_vector_t &output)
413 block->set_color(gr_basic_block::GREY);
414 gr_basic_block_vector_t blocks(calc_downstream_blocks(block));
416 for (gr_basic_block_viter_t p = blocks.begin(); p != blocks.end(); p++) {
417 switch ((*p)->color()) {
418 case gr_basic_block::WHITE:
419 topological_dfs_visit(*p, output);
422 case gr_basic_block::GREY:
423 throw std::runtime_error("flow graph has loops!");
425 case gr_basic_block::BLACK:
429 throw std::runtime_error("invalid color on block!");
433 block->set_color(gr_basic_block::BLACK);
434 output.push_back(block);