Moving Block Routing using Simulation#109
Conversation
* Add nix devshell * Add tools for pre-commit hooks and linting * Add example test for new simulation category * Add tool for graph visualization * Add empty class `SimulationInstance` * Add members and test for `SimulationInstance` * Determine simulation parameters * Add struct `RoutingSolutionTrain` * Switch integer type * Add general classes for routing * Add random solution generation * Move consistency check and restrict access qualifiers * Update dev packages * Add dev scripts * Add class `RoutingSolution` * Add class `EdgeTrajectory` * Add struct `SpeedTargets` * Add search through speed targets * Add edge trajectory simulation * Simplify edge simulation and add part of edge transition * Switch datatypes and structures * Switch to ninja build tool * Fix random solution generation * Add edge transition logic * Add `TrainTrajectory` * Formalize edge transition outcomes * Fix includes and tests * Add check for planned stop * Generate amount of timestep from schedule * Generate initial train state from schedule * Check overspeed outcome * Add part of trajectory construction * Keep reference to instance in trajectory objects * Add `limit_speed_after` * Use const iterator * Remove undefined constructor * Add test for `limit_speed_after` * Add braking logic helper functions * Save information about edge transitions * Fix range of `switch_directions` * Index timesteps from 0 * Add `distance_to_last_transition` and `is_feasible_braking_point` * Fix orientation format * Fix test * Convert member functions to const * Add helpers to SpeedTargets * Clarify trajectory object naming * Sort member functions * Consider carried over targets when limiting speeds * Rename internal function * Add const to `RoutingSolver` members * Add braking function * Fix const member initialization * Enable holding both until a fixed time and a minimum time * Add edge speed limiting logic * Clarify timestep indexing * Begin trajectory construction logic * Add `find_next_reversal` * Finish trajectory construction logic * Add test for trajectory construction * Refactor random solution generation * Use calculated amount of switch vars in test * Fix edge simulation exiting immediately * Fix timestep range check * Track visited stops * Rework edge transition logic We need compatability for passing multiple edges in one timestep * Narrow scope * Fix acceleration sign * Make sub trajectory selection more robust * Make checks for timestep range train specific * Fix range error while pruning trajectories * Include hold in returned braking period * Clarify error message * Improve struct member names * Determine last timestep during construction * Fix transition timestep increment * Recalculate entire trajectory when adjusting first target * Rename variable to avoid confusion with member * Terminate construction after simulation time ends * Establish invariant for `EdgeEntry` * Fix control flow for revisited planned stop * Fix accidental iterator decrement * Update tools * Fix incorrect use of map erase * Resolve name collision * Fix missing member initialization * Skip target restoration at end of simulation * Split functions for searching trajectories * When not reversing out of dead end wait until end of time * Change type * Replace `ulong` with `u_int64_t` * Edge index has type `size_t` * Remove redundant range checks * Test more solutions with random target amount * Prevent integer underflow * Remove redundant calculation * Disable optimizations in debug mode This should be covered by default behavior but isn't. * Fix braking distance calculation * Fix negative braking time * Remove remaining distance calculation * Fix incorrect `std::signbit` usage * Disallow default constructors * Allow const members * Add include guards * Add solution and trajectory sets * Explain map keys in solution and trajectory sets * Add test for trajectory set * Pass solution set by reference * Test size of solution and trajectory sets * Add trajectory set export to csv * Add trajectory plotting * Change test network * Add speed limit checks * Add indication for inactive trains * Add todo * Make a copy of traversal instead of referencing Last element of edge_trajs can change at any time. * Add todo * Introduce namespace `cda_rail::sim` * Add random search * Add train distance * Fix random solution test * Add collision objective * Simplify and document distance penalties * Fix missing iteration * Test random search * Add test for train distance * Fix distance calculation * Simplify random search abort criterium * Reduce testing time * Move test of trajectory export * Check for sufficient time resolution * Choose more appropriate exception types * Fix missing train in plot * Add critical TODOs * Disable unused and cpp warnings fortify warning cannot be turned off otherwise even with explicit `D_FORTIFY_SOURCE=0` * Add vertex shortest paths * Add undirected vertex shortest paths * Fix train distance calculation with using vertex shortest paths * Add helpers for testing train distance exactly * Fix entry position * Fix fall-through in switch statement * Add detailed test for train distance * Refactor collision detection * Add destination objective * Enable early train exit according to schedule * Add stop objective * Add TODO * Add plot for speed curves * Make amount of speed targets into a decision variable * Add random search * Add missing function * Make trajectories copyable using reference wrapper * Add `contains` helper to trajectory set * Fix uninitialized variable * Fix penalty for trains without stops * Fix premature abort of greedy search * Plot all tests * Add todo * Shuffle train placement order * Add `SolverResult` class * Add destination penalty for single trains * Add stop penalty for single trains * Pass reference * Inline stop handling * Simplify train iteration * Reduce duplicate calculations with solver result object * Add todo * Remove unused arguments * Add greedy search * Test incremental solution building * Add TODO * Switch to normal second-order euler * Record score history * Add unidirectional network template * Add score history csv export * Add `ScoreHistoryCollection` * Add app for collecting simulator statistics * Move dev scripts into subdirectory * Solve resource conflict in parallel search * Limit thread count * Improve thread task assignment * Use explicit time types * Expand search abort criteria * Add network conversion script * Fix initial edge selection for tests * Add second executable for simulator * Add converted overtake network * Remove unused import * Add local search * Add search method comparison executable * Remove outdated documentation * Add grasp search * Update dev scripts for launching apps * Introduce local search parameter testing * Improve parameter search and export * Measure on same network * Shorten grasp name * Update best local search parameters * Convert Stammstrecke16Trains to unidirectional * Add openGA submodule * Add random solution overload for GA supplied random generator * Remove outdated documentation * Add GA mutate * Add rest of GA helpers * Bind member function objects * Test genetic algorithm * Convert results of genetic search * Add GA to method comparison * Pass parameters to GA * Avoid copy when binding reference * Add GA parameter search * Fix GA testing parameters * Add mutation rate to GA param testing * Add GA population parameter testing * Add GA elite to parameter testing * Add GA with local improvment * Improve GA testing parameters * Improve search parameter passing * Automate measure for multiple networks * Formalize local search params * Prepare conversion to vss solution format * Finish solution conversion * Add TODO * Adjust optimal genetic params * Add simultaneous measurement * Save individual scores in history * Fix double division * Further automate testing * Add bidirectional networks for exporting simulator solutions * Indicate error attempting to export a train skipping tracks * Create example network without skipping for testing ExportVSS * Remove TODO * Normalize and test summing of objective scores * Automate parameter search measurements * Reenable optimization * Update parameters and fix export * Record sample number in of score collection * Adjust comparison parameters * Update launcher scripts * Fix seed for multiple calls a second * Update optimum parameters * Update parameters * Add multithreading test * Add requirements.txt * Remove openGA submodule * Add openGA as file We need to define `mtx_rand` a static. Instead of applying a patch we include the modified source directly. * Remove development artifacts * Revert compile flag changes
|
|
||
| // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) | ||
|
|
||
| int main(int argc, char** argv) { |
Check warning
Code scanning / CodeQL
Poorly documented large function Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To address the issue, we will add comments to document the purpose and functionality of the main function and its key sections. This will include:
- A high-level comment at the start of the
mainfunction explaining its overall purpose. - Inline comments for each major block of code to describe what it does.
- Comments for any non-obvious operations or parameters to clarify their intent.
These changes will improve the readability and maintainability of the function without altering its functionality.
| @@ -15,4 +15,8 @@ | ||
| int main(int argc, char** argv) { | ||
| auto args = gsl::span<char*>(argv, argc); | ||
| // Main function for running genetic parameter testing on a timetable simulation. | ||
| // This function imports the network and timetable, sets up simulation parameters, | ||
| // and performs multithreaded genetic algorithm optimization with varying crossover fractions. | ||
|
|
||
| // Parse command-line arguments for model and output paths. | ||
| auto args = gsl::span<char*>(argv, argc); | ||
| const std::string model_path = args[1]; | ||
| @@ -22,2 +26,3 @@ | ||
|
|
||
| // Import the network and timetable data. | ||
| cda_rail::Network network = | ||
| @@ -27,4 +32,6 @@ | ||
|
|
||
| // Initialize the simulation instance with the imported data. | ||
| cda_rail::sim::SimulationInstance instance{network, timetable, false}; | ||
|
|
||
| // Determine the number of available hardware threads for multithreading. | ||
| size_t processor_count = std::thread::hardware_concurrency(); | ||
| @@ -33,2 +40,3 @@ | ||
|
|
||
| // Set up default genetic algorithm parameters. | ||
| cda_rail::sim::GeneticParams ga_params{ | ||
| @@ -44,2 +52,3 @@ | ||
| { | ||
| // Test different crossover fractions for the genetic algorithm. | ||
| std::vector<double> xover_fractions = {0.01, 0.025, 0.1, 0.5, 0.7, 0.99}; | ||
| @@ -47,2 +56,3 @@ | ||
| for (double xover_fraction : xover_fractions) { | ||
| // Initialize a collection to store score histories and a mutex for thread safety. | ||
| cda_rail::sim::ScoreHistoryCollection score_coll; | ||
| @@ -50,4 +60,6 @@ | ||
|
|
||
| // Update the crossover fraction in the genetic algorithm parameters. | ||
| ga_params.xover_frac = xover_fraction; | ||
|
|
||
| // Launch worker threads to perform genetic algorithm optimization. | ||
| std::vector<std::thread> workers; | ||
| @@ -57,2 +69,3 @@ | ||
|
|
||
| // Perform multiple runs of the genetic algorithm and collect results. | ||
| for (size_t sample_runs = 0; sample_runs < 3; sample_runs++) { | ||
| @@ -68,2 +81,3 @@ | ||
|
|
||
| // Wait for all worker threads to finish. | ||
| while (workers.size() > 0) { | ||
| @@ -73,2 +87,3 @@ | ||
|
|
||
| // Save the score history to a CSV file for the current crossover fraction. | ||
| auto save_path = |
|
|
||
| // NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-array-to-pointer-decay,bugprone-exception-escape) | ||
|
|
||
| int main(int argc, char** argv) { |
Check warning
Code scanning / CodeQL
Poorly documented large function Warning
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To fix the issue, we will add comments to document the purpose of the main function and its key components. This includes:
- Adding a high-level comment at the start of the function to describe its overall purpose.
- Adding inline comments to explain the purpose of key variables, operations, and logic blocks.
- Ensuring that the comments are concise but informative, providing enough context for someone unfamiliar with the code to understand its intent.
No changes to the functionality of the code will be made.
| @@ -16,4 +16,10 @@ | ||
| int main(int argc, char** argv) { | ||
| // Entry point of the program. This function sets up the simulation environment, | ||
| // parses input arguments, initializes parameters, and runs simulations using | ||
| // different search methods. | ||
|
|
||
| // Parse command-line arguments | ||
| auto args = gsl::span<char*>(argv, argc); | ||
|
|
||
| // Paths for input model and output results | ||
| const std::string model_path = args[1]; | ||
| @@ -23,2 +29,3 @@ | ||
|
|
||
| // Import the network and timetable data | ||
| cda_rail::Network network = | ||
| @@ -28,24 +35,29 @@ | ||
|
|
||
| // Create a simulation instance with the imported data | ||
| cda_rail::sim::SimulationInstance instance{network, timetable, false}; | ||
|
|
||
| // Determine the number of available processors for multithreading | ||
| size_t processor_count = std::thread::hardware_concurrency(); | ||
| if (processor_count == 0) | ||
| processor_count = 1; | ||
| processor_count = 1; // Fallback to a single processor if detection fails | ||
|
|
||
| // Genetic algorithm parameters for the simulation | ||
| cda_rail::sim::GeneticParams ga_params{ | ||
| .is_multithread = false, | ||
| .population = 1000, | ||
| .gen_max = 100, | ||
| .stall_max = 10, | ||
| .n_elite = 10, | ||
| .xover_frac = 0.7, | ||
| .mut_rate = 0.1, | ||
| .is_multithread = false, // Disable multithreading for this example | ||
| .population = 1000, // Population size | ||
| .gen_max = 100, // Maximum number of generations | ||
| .stall_max = 10, // Maximum number of generations without improvement | ||
| .n_elite = 10, // Number of elite individuals to retain | ||
| .xover_frac = 0.7, // Crossover fraction | ||
| .mut_rate = 0.1, // Mutation rate | ||
| }; | ||
|
|
||
| // Local search parameters for the simulation | ||
| cda_rail::sim::LocalParams loc_params{ | ||
| .start_sampling_range_fraction = 0.4, | ||
| .abort_sampling_range_fraction = 0.001, | ||
| .contraction_coeff = 0.99, | ||
| .start_sampling_range_fraction = 0.4, // Initial sampling range | ||
| .abort_sampling_range_fraction = 0.001, // Termination sampling range | ||
| .contraction_coeff = 0.99, // Contraction coefficient | ||
| }; | ||
|
|
||
| // List of search methods to evaluate | ||
| std::vector<std::string> methods = {"random", "random+local", "greedy", | ||
| @@ -53,2 +65,3 @@ | ||
|
|
||
| // Iterate over each search method and run the simulation | ||
| for (std::string method : methods) { |
| cda_rail::sim::TrainState overshot_state) { | ||
| double edge_length = network.get_edge(overshot_state.edge).length; | ||
| bool exit_point = (overshot_state.position > 1); | ||
| size_t vertex; |
Check notice
Code scanning / CodeQL
Unused local variable Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To fix the issue, the unused variable vertex should be removed from the function cda_rail::sim::determine_exit. This involves:
- Deleting the declaration of
vertexon line 159. - Ensuring that no other parts of the function rely on
vertex(which is already the case here).
This change will not affect the functionality of the code, as vertex is not used anywhere in the function.
| @@ -158,3 +158,3 @@ | ||
| bool exit_point = (overshot_state.position > 1); | ||
| size_t vertex; | ||
|
|
||
| bool crossing_orientation; |
| double edge_length = network.get_edge(overshot_state.edge).length; | ||
| bool exit_point = (overshot_state.position > 1); | ||
| size_t vertex; | ||
| bool crossing_orientation; |
Check notice
Code scanning / CodeQL
Unused local variable Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To fix the issue, the unused variable crossing_orientation should be removed from the function determine_exit. This involves deleting its declaration on line 160. No other changes are necessary, as the variable is not used elsewhere in the function.
| @@ -159,3 +159,2 @@ | ||
| size_t vertex; | ||
| bool crossing_orientation; | ||
| double leftover_movement; |
| bool exit_point = (overshot_state.position > 1); | ||
| size_t vertex; | ||
| bool crossing_orientation; | ||
| double leftover_movement; |
Check notice
Code scanning / CodeQL
Unused local variable Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To fix the issue, the unused variable leftover_movement should be removed from the function cda_rail::sim::determine_exit. This involves deleting its declaration on line 161. No other changes are necessary, as the variable is not referenced elsewhere in the function.
| @@ -160,3 +160,2 @@ | ||
| bool crossing_orientation; | ||
| double leftover_movement; | ||
|
|
| // This is the definite integral under the braking curve | ||
| // As defined in EdgeTrajectory speed changes are done at maximum acceleration | ||
| // Zero-crossing and bidirectionality need to be considered | ||
| double braking_dist = |
Check notice
Code scanning / CodeQL
Unused local variable Note
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To fix the issue, the unused variable braking_dist should be removed from the function is_feasible_braking_point. This involves deleting its declaration and the associated computation on lines 183–187. Since the variable is not used elsewhere in the function, this change will not affect the program's behavior.
| @@ -182,7 +182,3 @@ | ||
| // Zero-crossing and bidirectionality need to be considered | ||
| double braking_dist = | ||
| starting_speed * required_braking_time + | ||
| (0.5 * std::copysign((required_braking_time - 1) * | ||
| (required_braking_time * accel), | ||
| speed_diff)); | ||
| // Removed unused variable braking_dist and its computation | ||
|
|
| Train train = | ||
| timetable.get_train_list().get_train(random_train_index(rng_engine)); | ||
|
|
||
| sim::SimulationInstance instance(network, timetable, true); |
Check notice
Code scanning / CodeQL
Declaration hides variable Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To fix the issue, we will rename the inner instance variable declared on line 115 to a more descriptive name, such as local_instance. This will eliminate the shadowing of the outer instance variable declared on line 107. The change will be limited to the EdgeTrajectory test function, and no other functionality will be affected.
| @@ -114,4 +114,4 @@ | ||
|
|
||
| sim::SimulationInstance instance(network, timetable, true); | ||
| sim::RoutingSolution solution(instance, train, rng_engine); | ||
| sim::SimulationInstance local_instance(network, timetable, true); | ||
| sim::RoutingSolution solution(local_instance, train, rng_engine); | ||
|
|
| size_t train_idx = random_train_index(rng_engine); | ||
|
|
||
| Train train = timetable.get_train_list().get_train(train_idx); | ||
| sim::SimulationInstance instance(network, timetable, true); |
Check notice
Code scanning / CodeQL
Declaration hides variable Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To fix the issue, we should rename the inner instance variable on line 158 to a distinct name that reflects its purpose. This will eliminate the shadowing and make the code more readable. For example, we could rename it to temp_instance or local_instance. This change will ensure that the outer instance variable remains accessible and unambiguous throughout the function.
| @@ -157,4 +157,4 @@ | ||
| Train train = timetable.get_train_list().get_train(train_idx); | ||
| sim::SimulationInstance instance(network, timetable, true); | ||
| sim::RoutingSolution solution(instance, train, rng_engine); | ||
| sim::SimulationInstance temp_instance(network, timetable, true); | ||
| sim::RoutingSolution solution(temp_instance, train, rng_engine); | ||
|
|
| sim::SimulationInstance instance(network, timetable, true); | ||
| sim::RoutingSolutionSet solution_set{instance}; | ||
| sim::TrainTrajectorySet traj{instance, solution_set}; | ||
| const TrainList& list = instance.timetable.get_train_list(); |
Check notice
Code scanning / CodeQL
Unused local variable Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To fix the issue, the unused variable list should be removed from the code. This involves deleting the line where list is declared. No additional changes are required, as the variable is not used elsewhere in the function.
| @@ -211,3 +211,3 @@ | ||
| sim::TrainTrajectorySet traj{instance, solution_set}; | ||
| const TrainList& list = instance.timetable.get_train_list(); | ||
|
|
||
|
|
| for (auto train_idx = train_idxs.begin(); train_idx != train_idxs.end(); | ||
| train_idx++) { | ||
| const auto train = train_list.get_train(*train_idx); | ||
| double previous_score = result.get_score_set().get_score(); |
Check notice
Code scanning / CodeQL
Unused local variable Note test
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 12 months ago
To fix the issue, we will remove the unused variable previous_score from line 275. This will eliminate the unnecessary declaration and improve code readability. No additional changes are required since the variable is not used elsewhere in the code.
| @@ -274,3 +274,2 @@ | ||
| const auto train = train_list.get_train(*train_idx); | ||
| double previous_score = result.get_score_set().get_score(); | ||
| sim::RoutingSolution round_sol{instance, train, rng_engine}; |
This reverts commit e10bef4.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #109 +/- ##
=======================================
- Coverage 97.3% 96.7% -0.6%
=======================================
Files 44 56 +12
Lines 13060 14366 +1306
Branches 1559 1745 +186
=======================================
+ Hits 12708 13902 +1194
- Misses 352 464 +112 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…l to 'clamp' on macos
…l to 'clamp' std::clamp(old_target.first + uniform_timestep_perturbation(), on macos
…tialization of 'sim::SpeedTargets' on macos
Cpp-Linter Report
|
This PR adds simulation methods and optimizers in the context of moving block routing