Skip to content

Commit

Permalink
refactor: formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
mimizh2418 committed Oct 27, 2024
1 parent 49b8a8a commit d428402
Show file tree
Hide file tree
Showing 19 changed files with 103 additions and 63 deletions.
3 changes: 2 additions & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
ColumnLimit: 120
ConstructorInitializerAllOnOneLineOrOnePerLine: true
Standard: c++20
SortIncludes: Never
Standard: c++20
2 changes: 1 addition & 1 deletion .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,4 @@ Checks:
readability-string-compare,
readability-uniqueptr-delete-release,
readability-use-anyofallof'
FormatStyle: file
FormatStyle: file
1 change: 0 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,3 @@ jobs:

- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}

12 changes: 12 additions & 0 deletions .styleguide
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
cppHeaderFileInclude {
\.h$
}

cppSrcFileInclude {
\.cpp$
}

includeOtherLibs {
^Eigen/
^gsl/
}
1 change: 1 addition & 0 deletions .styleguide-license
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Copyright (c) 2024 Alvin Zhang.
12 changes: 6 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,26 +18,26 @@ target_compile_options(suboptimal PRIVATE -Wall -Wextra -Wpedantic -Werror)
include(FetchContent)

find_package(Microsoft.GSL CONFIG)
if (NOT Microsoft.GSL_FOUND)
if(NOT Microsoft.GSL_FOUND)
message(STATUS "GSL not found, fetching from GitHub")
FetchContent_Declare(
fetchcontent_declare(
GSL
GIT_REPOSITORY "https://github.com/microsoft/GSL"
GIT_TAG "v4.0.0"
GIT_SHALLOW ON
)
FetchContent_MakeAvailable(GSL)
fetchcontent_makeavailable(GSL)
endif()
target_link_libraries(suboptimal PRIVATE Microsoft.GSL::GSL)

find_package(Eigen3 3.4 CONFIG)
if (NOT Eigen3_FOUND)
if(NOT Eigen3_FOUND)
message(STATUS "Eigen3 not found, fetching from GitLab")
FetchContent_Declare(
fetchcontent_declare(
Eigen3
GIT_REPOSITORY "https://gitlab.com/libeigen/eigen.git"
GIT_TAG "3.4"
)
FetchContent_MakeAvailable(Eigen3)
fetchcontent_makeavailable(Eigen3)
endif()
target_link_libraries(suboptimal PUBLIC Eigen3::Eigen)
14 changes: 7 additions & 7 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
Copyright (c) 2024 Alvin Zhang

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ trivially determinable basic feasible solutions.
- Interior-point method
- Sometime in the not too near future
- Other methods (like SQP, etc.)
- Maybe?
- Maybe?
5 changes: 4 additions & 1 deletion include/suboptimal/LinearProblem.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
// Copyright (c) 2024 Alvin Zhang.

#pragma once

#include <Eigen/Core>
#include <string>
#include <vector>

#include <Eigen/Core>

namespace suboptimal {
class LinearProblem {
public:
Expand Down
6 changes: 4 additions & 2 deletions include/suboptimal/solvers/SolverExitStatus.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright (c) 2024 Alvin Zhang.

#pragma once

#include <string>
Expand All @@ -16,7 +18,7 @@ enum class SolverExitStatus {
kTimeout
};

inline std::string toString(const SolverExitStatus& status) {
constexpr std::string toString(const SolverExitStatus& status) {
switch (status) {
case SolverExitStatus::kSuccess:
return "solver found an optimal solution";
Expand All @@ -32,4 +34,4 @@ inline std::string toString(const SolverExitStatus& status) {
return "unknown status";
}
}
} // namespace suboptimal
} // namespace suboptimal
6 changes: 4 additions & 2 deletions include/suboptimal/solvers/linear/SimplexPivotRule.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright (c) 2024 Alvin Zhang.

#pragma once

#include <string>
Expand All @@ -12,7 +14,7 @@ enum class SimplexPivotRule {
kLexicographic
};

inline std::string toString(const SimplexPivotRule& rule) {
constexpr std::string toString(const SimplexPivotRule& rule) {
switch (rule) {
case SimplexPivotRule::kDantzig:
return "Dantzig";
Expand All @@ -24,4 +26,4 @@ inline std::string toString(const SimplexPivotRule& rule) {
return "unknown pivot rule";
}
}
} // namespace suboptimal
} // namespace suboptimal
4 changes: 3 additions & 1 deletion include/suboptimal/solvers/linear/SimplexSolverConfig.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright (c) 2024 Alvin Zhang.

#pragma once

#include <chrono>
Expand All @@ -16,4 +18,4 @@ struct SimplexSolverConfig {
// Pivot rule to use
SimplexPivotRule pivot_rule = SimplexPivotRule::kLexicographic;
};
} // namespace suboptimal
} // namespace suboptimal
2 changes: 2 additions & 0 deletions include/suboptimal/solvers/linear/simplex.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright (c) 2024 Alvin Zhang.

#pragma once

#include <Eigen/Core>
Expand Down
24 changes: 13 additions & 11 deletions src/LinearProblem.cpp
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
// Copyright (c) 2024 Alvin Zhang.

#include "suboptimal/LinearProblem.h"

#include <Eigen/Core>
#include <format>
#include <gsl/narrow>
#include <stdexcept>
#include <string>
#include <vector>

#include <Eigen/Core>
#include <gsl/narrow>

#include "util/expression_util.h"

using namespace suboptimal;
using namespace std;
using namespace Eigen;

LinearProblem::LinearProblem(const VectorXd& objective_coeffs)
: objective_coeffs(objective_coeffs), num_decision_vars(objective_coeffs.size()) {
if (objective_coeffs.size() < 1) {
throw invalid_argument("Objective function must have at least one coefficient");
throw std::invalid_argument("Objective function must have at least one coefficient");
}
if ((objective_coeffs.array() == 0).any()) {
throw invalid_argument("Objective function coefficients must be non-zero");
throw std::invalid_argument("Objective function coefficients must be non-zero");
}
}

Expand All @@ -41,7 +43,7 @@ void LinearProblem::addEqualityConstraint(const VectorXd& constraint_coeffs, con

void LinearProblem::addConstraintImpl(const VectorXd& constraint_coeffs, const double rhs, const int constraint_type) {
if (constraint_coeffs.size() != num_decision_vars) {
throw invalid_argument("Constraint coefficients must have same dimension as decision variables");
throw std::invalid_argument("Constraint coefficients must have same dimension as decision variables");
}

VectorXd new_constraint(constraint_coeffs.size() + 1);
Expand Down Expand Up @@ -105,21 +107,21 @@ std::string LinearProblem::objectiveFunctionString() const {
return expressionFromCoeffs(objective_coeffs, "x");
}

vector<string> LinearProblem::constraintStrings() const {
vector<string> ret(num_constraints);
std::vector<std::string> LinearProblem::constraintStrings() const {
std::vector<std::string> ret(num_constraints);
size_t current_constraint_index = 0;
for (size_t i = 0; i < equality_constraints.size(); i++, current_constraint_index++) {
ret[current_constraint_index] = expressionFromCoeffs(equality_constraints[i].head(num_decision_vars).eval(), "x");
ret[current_constraint_index] += format(" = {}", equality_constraints[i](num_decision_vars));
ret[current_constraint_index] += std::format(" = {}", equality_constraints[i](num_decision_vars));
}
for (size_t i = 0; i < less_than_constraints.size(); i++, current_constraint_index++) {
ret[current_constraint_index] = expressionFromCoeffs(less_than_constraints[i].head(num_decision_vars).eval(), "x");
ret[current_constraint_index] += format(" ≤ {}", less_than_constraints[i](num_decision_vars));
ret[current_constraint_index] += std::format(" ≤ {}", less_than_constraints[i](num_decision_vars));
}
for (size_t i = 0; i < greater_than_constraints.size(); i++, current_constraint_index++) {
ret[current_constraint_index] =
expressionFromCoeffs(greater_than_constraints[i].head(num_decision_vars).eval(), "x");
ret[current_constraint_index] += format(" ≥ {}", greater_than_constraints[i](num_decision_vars));
ret[current_constraint_index] += std::format(" ≥ {}", greater_than_constraints[i](num_decision_vars));
}
return ret;
}
2 changes: 2 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright (c) 2024 Alvin Zhang.

#include "suboptimal/solvers/linear/SimplexPivotRule.h"
#include "suboptimal/solvers/linear/SimplexSolverConfig.h"
#include "suboptimal/solvers/linear/simplex.h"
Expand Down
55 changes: 29 additions & 26 deletions src/solvers/linear/simplex.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
// Copyright (c) 2024 Alvin Zhang.

#include "suboptimal/solvers/linear/simplex.h"

#include <Eigen/Core>
#include <algorithm>
#include <iostream>
#include <limits>
#include <stdexcept>

#include <Eigen/Core>

#include "suboptimal/LinearProblem.h"
#include "suboptimal/solvers/SolverExitStatus.h"
#include "suboptimal/solvers/linear/SimplexPivotRule.h"
Expand All @@ -15,7 +18,6 @@

using namespace suboptimal;
using namespace Eigen;
using namespace std;

int findPivotPosition(const MatrixXd& tableau, const VectorX<Index>& basic_vars, const SimplexPivotRule pivot_rule,
Index& pivot_row, Index& pivot_col) {
Expand Down Expand Up @@ -47,7 +49,7 @@ int findPivotPosition(const MatrixXd& tableau, const VectorX<Index>& basic_vars,
}

// Leaving variable selection
double min_ratio = numeric_limits<double>::infinity();
double min_ratio = std::numeric_limits<double>::infinity();
const auto rhs_col = tableau.col(tableau.cols() - 1).head(tableau.rows() - 1);
for (Index i = 0; i < rhs_col.size(); i++) {
const double ratio = rhs_col(i) / tableau(i, pivot_col);
Expand All @@ -64,8 +66,8 @@ int findPivotPosition(const MatrixXd& tableau, const VectorX<Index>& basic_vars,
// Lexicographic rule: select the variable with the lexicographically smallest row
const RowVectorXd row_candidate = tableau.row(i) / tableau(i, pivot_col);
const RowVectorXd current_best_row = tableau.row(pivot_row) / tableau(pivot_row, pivot_col);
if (ranges::lexicographical_compare(row_candidate, current_best_row,
[](const double a, const double b) { return approxLT<double>(a, b); })) {
if (std::ranges::lexicographical_compare(row_candidate, current_best_row,
[](const double a, const double b) { return approxLT<double>(a, b); })) {
pivot_row = i;
}
} else if (isApprox<double>(ratio, min_ratio) && pivot_rule == SimplexPivotRule::kBland) {
Expand Down Expand Up @@ -153,18 +155,18 @@ SolverExitStatus solveSimplex(const LinearProblem& problem, VectorXd& solution,
VectorXd constraint_rhs;
problem.buildConstraints(constraint_matrix, constraint_rhs);
if (problem.numConstraints() == 0) {
throw invalid_argument("Problem must have at least one constraint");
throw std::invalid_argument("Problem must have at least one constraint");
}

if (config.verbose) {
cout << "Solving linear problem: " << endl;
cout << "Maximize: " << problem.objectiveFunctionString() << endl;
cout << "Subject to: " << endl;
std::cout << "Solving linear problem: " << std::endl;
std::cout << "Maximize: " << problem.objectiveFunctionString() << std::endl;
std::cout << "Subject to: " << std::endl;
for (const auto constraint_strings = problem.constraintStrings();
const auto& constraint_string : constraint_strings) {
cout << " " << constraint_string << endl;
std::cout << " " << constraint_string << std::endl;
}
cout << "Using pivot rule: " << toString(config.pivot_rule) << endl << endl;
std::cout << "Using pivot rule: " << toString(config.pivot_rule) << std::endl << std::endl;
}

// Initialize tableau
Expand All @@ -175,7 +177,7 @@ SolverExitStatus solveSimplex(const LinearProblem& problem, VectorXd& solution,

if (!problem.hasInitialBFS()) {
if (config.verbose) {
cout << "No trivial BFS found, solving auxiliary LP" << endl;
std::cout << "No trivial BFS found, solving auxiliary LP" << std::endl;
}

// Set up auxiliary LP
Expand Down Expand Up @@ -206,21 +208,22 @@ SolverExitStatus solveSimplex(const LinearProblem& problem, VectorXd& solution,

if (config.verbose) {
const auto total_time = aux_profiler.getAvgIterationTime() * aux_profiler.numIterations();
cout << format("Auxiliary LP solve time: {:.3f} ms ({} iterations; {:.3f} ms average)", total_time.count(),
aux_profiler.numIterations(), aux_profiler.getAvgIterationTime().count())
<< endl
<< endl;
std::cout << std::format("Auxiliary LP solve time: {:.3f} ms ({} iterations; {:.3f} ms average)",
total_time.count(), aux_profiler.numIterations(),
aux_profiler.getAvgIterationTime().count())
<< std::endl
<< std::endl;
}

if (aux_exit == SolverExitStatus::kMaxIterationsExceeded) {
if (config.verbose) {
cout << "Max iterations exceeded while solving auxiliary LP" << endl;
std::cout << "Max iterations exceeded while solving auxiliary LP" << std::endl;
}
return aux_exit;
}
if (aux_exit == SolverExitStatus::kUnbounded ||
!isApprox<double>(tableau(tableau.rows() - 1, tableau.cols() - 1), 0)) {
cout << "The problem is infeasible" << endl;
std::cout << "The problem is infeasible" << std::endl;
return SolverExitStatus::kInfeasible;
}

Expand All @@ -240,10 +243,10 @@ SolverExitStatus solveSimplex(const LinearProblem& problem, VectorXd& solution,

if (config.verbose) {
const auto total_time = profiler.getAvgIterationTime() * profiler.numIterations();
cout << format("Solve time: {:.3f} ms ({} iterations; {:.3f} ms average)", total_time.count(),
profiler.numIterations(), profiler.getAvgIterationTime().count())
<< endl
<< "Status: " << toString(exit_status) << endl;
std::cout << std::format("Solve time: {:.3f} ms ({} iterations; {:.3f} ms average)", total_time.count(),
profiler.numIterations(), profiler.getAvgIterationTime().count())
<< std::endl
<< "Status: " << toString(exit_status) << std::endl;
}

if (exit_status == SolverExitStatus::kSuccess) {
Expand All @@ -258,16 +261,16 @@ SolverExitStatus solveSimplex(const LinearProblem& problem, VectorXd& solution,
objective_value = tableau(tableau.rows() - 1, tableau.cols() - 1);

if (config.verbose) {
cout << "Solution: " << endl;
std::cout << "Solution: " << std::endl;
for (Index i = 0; i < problem.numDecisionVars(); i++) {
cout << " x_" << i + 1 << " = " << solution(i) << endl;
std::cout << " x_" << i + 1 << " = " << solution(i) << std::endl;
}
cout << "Objective value: " << objective_value << endl;
std::cout << "Objective value: " << objective_value << std::endl;
}
}

if (config.verbose) {
cout << endl;
std::cout << std::endl;
}

return exit_status;
Expand Down
2 changes: 2 additions & 0 deletions src/util/SolverProfiler.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Copyright (c) 2024 Alvin Zhang.

#pragma once

#include <chrono>
Expand Down
Loading

0 comments on commit d428402

Please sign in to comment.