20#include <unordered_map>
21#include <unordered_set>
34 std::ostringstream oss;
35 oss <<
"Cycle detected: ";
36 for (
size_t i = 0; i <
components.size(); ++i) {
78 void AddEdge(
const std::string &producer,
const std::string &consumer,
79 const std::string &signal_name =
"") {
81 if (adjacency_.find(producer) == adjacency_.end()) {
82 adjacency_[producer] = {};
83 in_degree_[producer] = 0;
85 if (adjacency_.find(consumer) == adjacency_.end()) {
86 adjacency_[consumer] = {};
87 in_degree_[consumer] = 0;
91 auto &edges = adjacency_[producer];
92 bool already_exists =
false;
93 for (
const auto &e : edges) {
94 if (e.target == consumer) {
95 already_exists =
true;
100 if (!already_exists) {
101 edges.push_back({consumer, signal_name});
102 in_degree_[consumer]++;
114 if (adjacency_.find(component) == adjacency_.end()) {
115 adjacency_[component] = {};
116 in_degree_[component] = 0;
123 [[nodiscard]] std::vector<std::string>
GetNodes()
const {
124 std::vector<std::string> nodes;
125 nodes.reserve(adjacency_.size());
126 for (
const auto &kv : adjacency_) {
127 nodes.push_back(kv.first);
141 std::unordered_map<std::string, int> in_deg = in_degree_;
144 std::queue<std::string> ready;
145 for (
const auto &kv : in_deg) {
146 if (kv.second == 0) {
147 ready.push(kv.first);
152 while (!ready.empty()) {
153 std::string node = ready.front();
158 auto it = adjacency_.find(node);
159 if (it != adjacency_.end()) {
160 for (
const auto &edge : it->second) {
161 in_deg[edge.target]--;
162 if (in_deg[edge.target] == 0) {
163 ready.push(edge.target);
184 std::vector<CycleInfo> cycles;
187 std::unordered_map<std::string, int> state;
188 for (
const auto &kv : adjacency_) {
192 std::vector<std::string> path;
193 std::vector<std::string> path_signals;
195 for (
const auto &kv : adjacency_) {
196 if (state[kv.first] == 0) {
197 DetectCyclesDFS(kv.first, state, path, path_signals, cycles);
207 [[nodiscard]]
size_t Size()
const {
return adjacency_.size(); }
212 [[nodiscard]]
bool Empty()
const {
return adjacency_.empty(); }
217 [[nodiscard]] std::vector<std::string>
GetDependents(
const std::string &component)
const {
218 std::vector<std::string> deps;
219 auto it = adjacency_.find(component);
220 if (it != adjacency_.end()) {
221 for (
const auto &edge : it->second) {
222 deps.push_back(edge.target);
231 [[nodiscard]] std::vector<std::string>
GetDependencies(
const std::string &component)
const {
232 std::vector<std::string> deps;
233 for (
const auto &kv : adjacency_) {
234 for (
const auto &edge : kv.second) {
235 if (edge.target == component) {
236 deps.push_back(kv.first);
250 std::unordered_map<std::string, std::vector<Edge>> adjacency_;
251 std::unordered_map<std::string, int> in_degree_;
253 void DetectCyclesDFS(
const std::string &node, std::unordered_map<std::string, int> &state,
254 std::vector<std::string> &path, std::vector<std::string> &path_signals,
255 std::vector<CycleInfo> &cycles)
const {
257 path.push_back(node);
259 auto it = adjacency_.find(node);
260 if (it != adjacency_.end()) {
261 for (
const auto &edge : it->second) {
262 if (state[edge.target] == 1) {
265 bool in_cycle =
false;
266 for (
size_t i = 0; i < path.size(); ++i) {
267 if (path[i] == edge.target) {
271 cycle.components.push_back(path[i]);
272 if (i < path_signals.size()) {
273 cycle.signals.push_back(path_signals[i]);
277 cycle.signals.push_back(edge.signal);
278 cycles.push_back(cycle);
279 }
else if (state[edge.target] == 0) {
280 path_signals.push_back(edge.signal);
281 DetectCyclesDFS(edge.target, state, path, path_signals, cycles);
282 path_signals.pop_back();
311 std::vector<std::string> sorted_names;
313 std::string name = comp.FullPath();
314 sorted_names.push_back(name);
320 std::sort(sorted_names.begin(), sorted_names.end(),
321 [](
const std::string &a,
const std::string &b) {
322 if (a.size() != b.size()) {
323 return a.size() > b.size();
329 const auto &route_list = routes.empty() ? config.
routes : routes;
333 auto find_component = [&](
const std::string &signal_path) -> std::string {
335 for (
const auto &comp_name : sorted_names) {
336 if (signal_path.size() > comp_name.size() &&
337 signal_path.compare(0, comp_name.size(), comp_name) == 0 &&
338 signal_path[comp_name.size()] ==
'.') {
343 size_t dot = signal_path.find(
'.');
344 return (dot != std::string::npos) ? signal_path.substr(0, dot) : signal_path;
348 for (
const auto &route : route_list) {
349 std::string producer = find_component(route.output_path);
350 std::string consumer = find_component(route.input_path);
352 if (!producer.empty() && !consumer.empty() && producer != consumer) {
353 graph.
AddEdge(producer, consumer, route.output_path);
375 switch (cycle_handling) {
377 std::ostringstream oss;
378 oss <<
"Cyclic dependencies detected in component graph:\n";
379 for (
const auto &cycle : result.
cycles) {
380 oss <<
" " << cycle.ToString() <<
"\n";
391 std::vector<std::string> all_nodes = graph.
GetNodes();
394 std::sort(all_nodes.begin(), all_nodes.end());
397 for (
const auto &node : all_nodes) {
398 if (included.find(node) == included.end()) {
422 double rate_hz = 400.0) {
424 group.
name = group_name;
430 group.
members.emplace_back(component, priority++);
447 std::unordered_map<std::string, int> priority_map;
453 std::set<std::string> scheduled;
456 for (
auto &group : config.
groups) {
457 for (
auto &member : group.members) {
458 auto it = priority_map.find(member.component);
459 if (it != priority_map.end()) {
460 member.priority = it->second;
461 scheduled.insert(member.component);
467 group.members.begin(), group.members.end(),
472 std::vector<std::string> unscheduled;
474 if (scheduled.find(comp) == scheduled.end()) {
475 unscheduled.push_back(comp);
479 if (!unscheduled.empty()) {
481 unscheduled_group.
name =
"unscheduled";
483 unscheduled_group.
priority =
static_cast<int>(config.
groups.size());
485 for (
const auto &comp : unscheduled) {
486 auto it = priority_map.find(comp);
487 int prio = (it != priority_map.end()) ? it->second : 0;
488 unscheduled_group.
members.emplace_back(comp, prio);
491 config.
groups.push_back(unscheduled_group);
Consolidated error handling for Icarus.
Centralized signal routing configuration.
Simulator and subsystem configuration structs.
Configuration/parsing errors with optional file context.
Definition Error.hpp:185
Dependency graph for component execution ordering.
Definition TopologyAnalyzer.hpp:69
TopologyResult TopologicalSort() const
Perform topological sort using Kahn's algorithm.
Definition TopologyAnalyzer.hpp:137
void AddNode(const std::string &component)
Add a node without any edges.
Definition TopologyAnalyzer.hpp:113
std::vector< std::string > GetDependents(const std::string &component) const
Get dependents of a component (what runs after it).
Definition TopologyAnalyzer.hpp:217
std::vector< CycleInfo > DetectCycles() const
Detect cycles using DFS.
Definition TopologyAnalyzer.hpp:183
void AddEdge(const std::string &producer, const std::string &consumer, const std::string &signal_name="")
Add a dependency edge: producer must execute before consumer.
Definition TopologyAnalyzer.hpp:78
bool Empty() const
Check if graph is empty.
Definition TopologyAnalyzer.hpp:212
std::vector< std::string > GetNodes() const
Get all nodes in the graph.
Definition TopologyAnalyzer.hpp:123
std::vector< std::string > GetDependencies(const std::string &component) const
Get dependencies of a component (what runs before it).
Definition TopologyAnalyzer.hpp:231
size_t Size() const
Get number of nodes.
Definition TopologyAnalyzer.hpp:207
Utility class for building and analyzing component dependency graphs.
Definition TopologyAnalyzer.hpp:295
static SchedulerGroupConfig GenerateSchedulerGroup(const TopologyResult &result, const std::string &group_name="auto", double rate_hz=400.0)
Generate scheduler groups from topological order.
Definition TopologyAnalyzer.hpp:421
static TopologyResult ComputeExecutionOrder(const SimulatorConfig &config, TopologyConfig::CycleHandling cycle_handling=TopologyConfig::CycleHandling::Error)
Compute execution order from configuration.
Definition TopologyAnalyzer.hpp:368
static void ApplyTopologyOrder(SchedulerConfig &config, const TopologyResult &result)
Merge topology order into existing scheduler config.
Definition TopologyAnalyzer.hpp:445
static DependencyGraph BuildGraph(const SimulatorConfig &config, const std::vector< signal::SignalRoute > &routes={})
Build dependency graph from configuration.
Definition TopologyAnalyzer.hpp:307
Definition SignalRouter.hpp:22
Definition AggregationTypes.hpp:13
Information about a detected cycle in the dependency graph.
Definition TopologyAnalyzer.hpp:29
std::vector< std::string > components
Components forming the cycle.
Definition TopologyAnalyzer.hpp:30
std::string ToString() const
Definition TopologyAnalyzer.hpp:33
std::vector< std::string > signals
Signals involved in the cycle.
Definition TopologyAnalyzer.hpp:31
A component member within a scheduler group.
Definition SimulatorConfig.hpp:299
Scheduler configuration with rate groups.
Definition SimulatorConfig.hpp:346
std::vector< SchedulerGroupConfig > groups
Definition SimulatorConfig.hpp:347
A scheduler group with its own rate and priority.
Definition SimulatorConfig.hpp:310
std::vector< GroupMember > members
Definition SimulatorConfig.hpp:314
std::string name
Definition SimulatorConfig.hpp:311
double rate_hz
Must be integer divisor of simulation rate.
Definition SimulatorConfig.hpp:312
int priority
Execution order relative to other groups.
Definition SimulatorConfig.hpp:313
Complete simulation configuration.
Definition SimulatorConfig.hpp:673
std::vector< signal::SignalRoute > routes
Definition SimulatorConfig.hpp:701
std::vector< ComponentConfig > components
Definition SimulatorConfig.hpp:696
CycleHandling
Definition SimulatorConfig.hpp:335
@ BreakAtDelay
Definition SimulatorConfig.hpp:335
@ Warn
Definition SimulatorConfig.hpp:335
@ Error
Definition SimulatorConfig.hpp:335
Result of topological analysis.
Definition TopologyAnalyzer.hpp:52
std::vector< std::string > execution_order
Sorted component names.
Definition TopologyAnalyzer.hpp:53
bool IsValid() const
Definition TopologyAnalyzer.hpp:57
std::vector< CycleInfo > cycles
Detected cycles (if any).
Definition TopologyAnalyzer.hpp:54
bool has_cycles
Quick check for cycles.
Definition TopologyAnalyzer.hpp:55