Skip to content

Commit

Permalink
Numerical Issues on Ubuntu (#104)
Browse files Browse the repository at this point in the history
* imporve feedback on error

* linter warnings

* reduce numerical instability, maybe

* change tests to run as debug for more output

* linter warning std::pair

* more numerical issues

* improve coverage

* linter warnings

* linter warning std::pair

* remove long running test

* linter warnings
  • Loading branch information
sengels-tum authored Feb 20, 2025
1 parent 16486ee commit 7af816e
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 77 deletions.
18 changes: 9 additions & 9 deletions .github/workflows/cpp-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ jobs:
wget https://packages.gurobi.com/${{ env.GUROBI_VERSION_SHORT }}/${{ env.GUROBI_FILE }}
tar -xvzf ${{ env.GUROBI_FILE }}
- name: Configure CMake
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON ${{ matrix.config.toolchain }}
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON ${{ matrix.config.toolchain }}
- name: Build
run: cmake --build build --config Release
run: cmake --build build --config Debug
- name: Test
run: ctest -C Release --output-on-failure --test-dir build --repeat until-pass:3 --timeout 500
run: ctest -C Debug --output-on-failure --test-dir build --repeat until-pass:3 --timeout 500
cpp-macos-latest:
name: cpp-macos-latest
runs-on: macos-latest
Expand All @@ -97,11 +97,11 @@ jobs:
wget https://packages.gurobi.com/${{ env.GUROBI_VERSION_SHORT }}/${{ env.GUROBI_FILE }}
sudo installer -pkg ${{ env.GUROBI_FILE }} -target /
- name: Configure CMake
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON ${{ matrix.config.toolchain }}
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON ${{ matrix.config.toolchain }}
- name: Build
run: cmake --build build --config Release
run: cmake --build build --config Debug
- name: Test
run: ctest -C Release --output-on-failure --test-dir build --repeat until-pass:3 --timeout 500
run: ctest -C Debug --output-on-failure --test-dir build --repeat until-pass:3 --timeout 500
cpp-windows-latest:
name: cpp-windows-latest
runs-on: windows-latest
Expand Down Expand Up @@ -146,11 +146,11 @@ jobs:
echo "GUROBI_HOME=$PWD\gurobi\${{ needs.read-parameters.outputs.gurobiFolder }}\win64" >> $env:GITHUB_ENV
echo "$PWD\gurobi\${{ needs.read-parameters.outputs.gurobiFolder }}\win64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Configure CMake
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=ON ${{ matrix.config.toolchain }}
run: cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON ${{ matrix.config.toolchain }}
- name: Build
run: cmake --build build --config Release
run: cmake --build build --config Debug
- name: Test
run: ctest -C Release --output-on-failure --test-dir build --repeat until-pass:3 --timeout 500
run: ctest -C Debug --output-on-failure --test-dir build --repeat until-pass:3 --timeout 500
coverage:
name: Coverage
runs-on: ubuntu-latest
Expand Down
137 changes: 85 additions & 52 deletions src/EOMHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
#include <algorithm>
#include <cassert>
#include <cmath>
#include <iostream>
#include <limits>
#include <string>
#include <utility>

double cda_rail::min_travel_time_from_start(double v_1, double v_2, double v_m,
double a, double d, double s,
Expand Down Expand Up @@ -57,8 +58,8 @@ double cda_rail::min_travel_time(double v_1, double v_2, double v_m, double a,

bool cda_rail::possible_by_eom(double v_1, double v_2, double a, double d,
double s) {
return v_1 <= v_2 ? (v_2 + v_1) * (v_2 - v_1) <= 2 * a * s
: (v_1 + v_2) * (v_1 - v_2) <= 2 * d * s;
return v_1 <= v_2 ? (v_2 + v_1) * (v_2 - v_1) <= 2 * a * s + GRB_EPS
: (v_1 + v_2) * (v_1 - v_2) <= 2 * d * s + GRB_EPS;
}

double cda_rail::max_travel_time_from_start_no_stopping(double v_1, double v_2,
Expand Down Expand Up @@ -128,8 +129,8 @@ double cda_rail::max_travel_time_from_start_stopping_allowed(

const double bd = v_1 * v_1 / (2 * d); // Distance to stop

if (bd <= s_1) {
if (x >= s_1) {
if (bd <= s_1 + EPS) {
if (x + EPS >= s_1) {
// Infinite, because train could have stopped
return std::numeric_limits<double>::infinity();
}
Expand Down Expand Up @@ -163,22 +164,32 @@ double cda_rail::max_travel_time(double v_1, double v_2, double v_m, double a,
void cda_rail::check_consistency_of_eom_input(double& v_1, double& v_2,
double& a, double& d, double& s,
double& x) {
if (std::abs(v_1) < GRB_EPS)
if (std::abs(v_1) < GRB_EPS) {
v_1 = 0;
if (std::abs(v_2) < GRB_EPS)
}
if (std::abs(v_2) < GRB_EPS) {
v_2 = 0;
if (std::abs(a) < GRB_EPS)
}
if (std::abs(a) < GRB_EPS) {
a = 0;
if (std::abs(d) < GRB_EPS)
}
if (std::abs(d) < GRB_EPS) {
d = 0;
if (std::abs(s) < GRB_EPS)
}
if (std::abs(s) < GRB_EPS) {
s = 0;
if (std::abs(x) < GRB_EPS)
}
if (std::abs(x) < GRB_EPS) {
x = 0;
}
if (v_1 < 0 || v_2 < 0 || a < 0 || d < 0 || s < 0 || x < 0) {
throw exceptions::ConsistencyException(
"All input values must be non-negative.");
}
if (a < GRB_EPS || d < GRB_EPS) {
throw exceptions::ConsistencyException(
"Both acceleration and deceleration must be strictly positive.");
}

if (x > s) {
throw exceptions::ConsistencyException(
Expand All @@ -187,7 +198,7 @@ void cda_rail::check_consistency_of_eom_input(double& v_1, double& v_2,

if (!possible_by_eom(v_1, v_2, a, d, s)) {
throw exceptions::ConsistencyException(
"Travel time not possible by equations of motion.");
"Entry and exit velocities not possible by equations of motion.");
}
}

Expand Down Expand Up @@ -232,19 +243,23 @@ double cda_rail::min_time_to_push_ma_forward(double v_0, double a, double d,
// How much time does a train need to move its moving authority forward by s
// given initial speed v_0 and acceleration a and deceleration d

if (std::abs(v_0) < GRB_EPS)
if (std::abs(v_0) < GRB_EPS) {
v_0 = 0;
if (std::abs(a) < GRB_EPS)
}
if (std::abs(a) < GRB_EPS) {
a = 0;
if (d < 0 && d > -GRB_EPS)
}
if (d < 0 && d > -GRB_EPS) {
d = 0;
if (std::abs(s) < GRB_EPS)
}
if (std::abs(s) < GRB_EPS) {
s = 0;
}

// Assert that v_0 >= 0, a >= 0, d > 0, s > 0
if (v_0 < 0 || a < 0 || d <= 0 || s < 0) {
throw exceptions::InvalidInputException(
"We need v_0 >= 0, a >= 0, d > 0, s >= 0");
"We need v_0 >= 0, a > 0, d > 0, s >= 0");
}

// ma(t) = v_0*t + 0.5*a*t^2 + (v_0+a*t)^2/(2d) != s + v_0^2/(2d)
Expand Down Expand Up @@ -285,14 +300,18 @@ double cda_rail::min_time_to_push_ma_fully_backward(double v_0, double a,

double cda_rail::min_time_to_push_ma_backward(double v_0, double a, double d,
double s) {
if (std::abs(v_0) < GRB_EPS)
if (std::abs(v_0) < GRB_EPS) {
v_0 = 0;
if (std::abs(a) < GRB_EPS)
}
if (std::abs(a) < GRB_EPS) {
a = 0;
if (d < 0 && d > -GRB_EPS)
}
if (d < 0 && d > -GRB_EPS) {
d = 0;
if (std::abs(s) < GRB_EPS)
}
if (std::abs(s) < GRB_EPS) {
s = 0;
}

// Assert that v_0 >= 0, a >= 0, d > 0, s > 0
if (v_0 < 0 || a < 0 || d <= 0 || s < 0) {
Expand Down Expand Up @@ -405,8 +424,9 @@ double cda_rail::min_time_from_front_to_ma_point(double v_1, double v_2,
const auto& s_1 = s_points.first;
const auto& s_2 = s_points.second;

if (std::abs(obd) < GRB_EPS)
if (std::abs(obd) < GRB_EPS) {
obd = 0;
}
if (obd < 0) {
throw exceptions::InvalidInputException(
"obd must be greater than or equal 0.");
Expand Down Expand Up @@ -449,8 +469,9 @@ double cda_rail::max_time_from_front_to_ma_point_no_stopping(
const auto& s_1 = s_points.first;
const auto& s_2 = s_points.second;

if (std::abs(obd) < GRB_EPS)
if (std::abs(obd) < GRB_EPS) {
obd = 0;
}
if (obd < 0) {
throw exceptions::InvalidInputException(
"obd must be greater than or equal 0.");
Expand All @@ -460,7 +481,7 @@ double cda_rail::max_time_from_front_to_ma_point_no_stopping(

// Speed at changing point
const double v_t_squared =
v_1 * v_1 + 2 * (v1_below_minimal_speed ? a : -d) * s_1;
(v_1 * v_1) + (2 * (v1_below_minimal_speed ? a : -d) * s_1);
const double v_t = std::sqrt(v_t_squared);

// Braking distances
Expand Down Expand Up @@ -558,18 +579,24 @@ double cda_rail::time_on_edge(double v_1, double v_2, double v_line, double a,
*/

// If any variable is within std::abs(GRB_EPS), set to 0
if (std::abs(v_1) < GRB_EPS)
if (std::abs(v_1) < GRB_EPS) {
v_1 = 0;
if (std::abs(v_2) < GRB_EPS)
}
if (std::abs(v_2) < GRB_EPS) {
v_2 = 0;
if (std::abs(v_line) < GRB_EPS)
}
if (std::abs(v_line) < GRB_EPS) {
v_line = 0;
if (std::abs(a) < GRB_EPS)
}
if (std::abs(a) < GRB_EPS) {
a = 0;
if (std::abs(d) < GRB_EPS)
}
if (std::abs(d) < GRB_EPS) {
d = 0;
if (std::abs(s) < GRB_EPS)
}
if (std::abs(s) < GRB_EPS) {
s = 0;
}

// Assert that all variables are >= 0 and a, d, s, v_line are >= GRB_EPS
if (v_1 < 0 || v_2 < 0 || v_line < GRB_EPS || a < GRB_EPS || d < GRB_EPS ||
Expand All @@ -585,15 +612,15 @@ double cda_rail::time_on_edge(double v_1, double v_2, double v_line, double a,
}

// First segment: v_1 -> v_line
double a1 = v_line >= v_1 ? a : -d;
double s1 = (v_line * v_line - v_1 * v_1) / (2 * a1);
double t1 = (v_line - v_1) / a1;
const double a1 = v_line >= v_1 ? a : -d;
const double s1 = (v_line * v_line - v_1 * v_1) / (2 * a1);
const double t1 = (v_line - v_1) / a1;
assert(t1 >= 0);

// Last segment: v_line -> v_2
double a2 = v_2 >= v_line ? a : -d;
double s2 = (v_2 * v_2 - v_line * v_line) / (2 * a2);
double t2 = (v_2 - v_line) / a2;
const double a2 = v_2 >= v_line ? a : -d;
const double s2 = (v_2 * v_2 - v_line * v_line) / (2 * a2);
const double t2 = (v_2 - v_line) / a2;
assert(t2 >= 0);

// If s1 + s2 > s, this is not possible
Expand All @@ -606,7 +633,7 @@ double cda_rail::time_on_edge(double v_1, double v_2, double v_line, double a,
return t1 + t2;
}

return t1 + t2 + (s - s1 - s2) / v_line;
return t1 + t2 + ((s - s1 - s2) / v_line);
}

double cda_rail::maximal_line_speed(double v_1, double v_2, double v_max,
Expand All @@ -618,8 +645,8 @@ double cda_rail::maximal_line_speed(double v_1, double v_2, double v_max,

// Terminal velocity
const double v_t_squared =
v_1 * v_1 + 2 * a * s_1; // Maximal velocity reached
return std::sqrt(v_t_squared); // = v_m if s_2 > s_1
(v_1 * v_1) + (2 * a * s_1); // Maximal velocity reached
return std::sqrt(v_t_squared); // = v_m if s_2 > s_1
}

double cda_rail::minimal_line_speed(double v_1, double v_2, double v_min,
Expand All @@ -632,9 +659,9 @@ double cda_rail::minimal_line_speed(double v_1, double v_2, double v_min,
(void)s_2; // unused

const double v_t_squared =
v_1 * v_1 +
2 * (v_1_below_minimal_speed ? a : -d) * s_1; // Minimal velocity reached
return std::sqrt(v_t_squared); // = v_m if s_2 > s_1
(v_1 * v_1) + (2 * (v_1_below_minimal_speed ? a : -d) *
s_1); // Minimal velocity reached
return std::sqrt(v_t_squared); // = v_m if s_2 > s_1
}

double cda_rail::get_line_speed(double v_1, double v_2, double v_min,
Expand Down Expand Up @@ -688,7 +715,10 @@ double cda_rail::pos_on_edge_at_time(double v_1, double v_2, double v_line,
const auto total_time_dv = time_on_edge(v_1, v_2, v_line_dv, a, d, s);
if (t > total_time_dv + GRB_EPS) {
throw exceptions::InvalidInputException(
"Time exceeds total travel time.");
"Time exceeds total travel time in cda_rail::pos_on_edge_at_time "
"with t = " +
std::to_string(t) +
" and total_time_dv = " + std::to_string(total_time_dv));
}
return s;
}
Expand All @@ -700,16 +730,16 @@ double cda_rail::pos_on_edge_at_time(double v_1, double v_2, double v_line,
const auto a2 = v_2 >= v_line ? a : -d;

const auto t1 = (v_line - v_1) / a1;
const auto t2 = total_time - (v_2 - v_line) / a2;
const auto t2 = total_time - ((v_2 - v_line) / a2);

if (t <= t1) {
return v_1 * t + 0.5 * a1 * t * t;
return (v_1 * t) + (0.5 * a1 * t * t);
}
if (t <= t2) {
return v_1 * t1 + 0.5 * a1 * t1 * t1 + v_line * (t - t1);
return (v_1 * t1) + (0.5 * a1 * t1 * t1) + (v_line * (t - t1));
}
return s + 0.5 * a2 * (total_time - t) * (total_time - t) -
v_2 * (total_time - t);
return s + (0.5 * a2 * (total_time - t) * (total_time - t)) -
(v_2 * (total_time - t));
}

double cda_rail::vel_on_edge_at_time(double v_1, double v_2, double v_line,
Expand All @@ -726,7 +756,10 @@ double cda_rail::vel_on_edge_at_time(double v_1, double v_2, double v_line,
const auto total_time_dv = time_on_edge(v_1, v_2, v_line_dv, a, d, s);
if (t > total_time_dv + GRB_EPS) {
throw exceptions::InvalidInputException(
"Time exceeds total travel time.");
"Time exceeds total travel time in cda_rail::vel_on_edge_at_time "
"with t = " +
std::to_string(t) +
" and total_time_dv = " + std::to_string(total_time_dv));
}
return v_2;
}
Expand All @@ -738,13 +771,13 @@ double cda_rail::vel_on_edge_at_time(double v_1, double v_2, double v_line,
const auto a2 = v_2 >= v_line ? a : -d;

const auto t1 = (v_line - v_1) / a1;
const auto t2 = total_time - (v_2 - v_line) / a2;
const auto t2 = total_time - ((v_2 - v_line) / a2);

if (t <= t1) {
return v_1 + a1 * t;
return v_1 + (a1 * t);
}
if (t <= t2) {
return v_line;
}
return v_2 - a2 * (total_time - t);
return v_2 - (a2 * (total_time - t));
}
16 changes: 1 addition & 15 deletions test/test_gurobi_vss_gen_using_mb_information.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#include "VSSModel.hpp"
#include "Definitions.hpp"
#include "solver/mip-based/VSSGenTimetableSolver.hpp"

#include "gtest/gtest.h"
#include <filesystem>
#include <iostream>
#include <string>

TEST(VSSGenMBInfoSolver, Default1) {
cda_rail::solver::mip_based::VSSGenTimetableSolverWithMovingBlockInformation
Expand Down Expand Up @@ -136,15 +134,3 @@ TEST(VSSGenMBInfoSolver, Default9) {
EXPECT_EQ(sol.get_obj(), 15);
EXPECT_EQ(sol.get_mip_obj(), 15);
}

TEST(VSSGenMBInfoSolver, Default10) {
cda_rail::solver::mip_based::VSSGenTimetableSolverWithMovingBlockInformation
solver("./example-networks-mb-solutions/Stammstrecke16Trains/");

const auto sol = solver.solve({5});

EXPECT_TRUE(sol.has_solution());
EXPECT_EQ(sol.get_status(), cda_rail::SolutionStatus::Optimal);
EXPECT_EQ(sol.get_obj(), 17);
EXPECT_EQ(sol.get_mip_obj(), 17);
}
Loading

0 comments on commit 7af816e

Please sign in to comment.