Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InitialSolution #31

Merged
merged 14 commits into from
Jan 10, 2025
47 changes: 37 additions & 10 deletions .github/workflows/main.yml

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be in it's own separate PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be. Seems to be more overhead to fix the pipeline somewhere else, merge, rebase .... instead of doing it on the fly here :-)

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ on:

jobs:
coverage:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
env:
CC: gcc-11
CXX: g++-11
steps:
- uses: actions/checkout@v3
- uses: turtlebrowser/get-conan@main
Expand Down Expand Up @@ -55,7 +58,10 @@ jobs:
- name: Run Tests
run: ./build/test/Release/tests.exe
test_without_conan:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
env:
CC: gcc-11
CXX: g++-11
steps:
- uses: actions/checkout@v3
- name: Install parallelism library for C++
Expand All @@ -74,11 +80,29 @@ jobs:
make -j tests
- name: Run tests
run: ./test/tests
test_release_inx:
strategy:
matrix:
os: [ ubuntu-latest, macos-13 ]
runs-on: ${{ matrix.os }}
test_release_mac:
runs-on: macos-13
steps:
- uses: actions/checkout@v3
- uses: turtlebrowser/get-conan@main
- name: Run Conan Install
run: |
conan profile detect
pushd ~/.conan2/profiles
sed -i'' -e 's/gnu17/17/g' *
popd
conan install -of . -o with_tests=True --build=missing .
- name: Run CMake
run: cmake --preset conan-release .
- name: Compile
run: cmake --build build/Release --target tests
- name: Run Tests
run: ./build/Release/test/tests
test_release_linux:
runs-on: ubuntu-22.04
env:
CC: gcc-11
CXX: g++-11
steps:
- uses: actions/checkout@v3
- uses: turtlebrowser/get-conan@main
Expand All @@ -96,7 +120,10 @@ jobs:
- name: Run Tests
run: ./build/Release/test/tests
clang_tidy:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
env:
CC: gcc-11
CXX: g++-11
steps:
- uses: actions/checkout@v3
- uses: turtlebrowser/get-conan@main
Expand All @@ -112,7 +139,7 @@ jobs:
- name: Run Clang-Tidy
run: clang-tidy-14 -p build/Release source/*.cpp include/**/*.hpp
clang_format:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: DoozyX/[email protected]
Expand All @@ -121,7 +148,7 @@ jobs:
extensions: 'hpp,cpp'
clangFormatVersion: 17
doxygen:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: ssciwr/doxygen-install@v1
Expand Down
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Added

- [PR31](https://github.com/scipopt/SCIPpp/pull/31) Add `InitialSolution` and `Model::addSolution`.
- [PR28](https://github.com/scipopt/SCIPpp/pull/28) Add `Var::getVar`.

## [1.2.0] - 2024-05-21
Expand Down
42 changes: 42 additions & 0 deletions include/scippp/initial_solution.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include <map>

namespace scippp {

// forward declare
class Var;

/**
* A primal solution to be added to %SCIP's solution pool.
*
* @since 1.3.0
*/
class InitialSolution {
friend class Model;
//! Variable assignment in the initial solution.
std::map<const Var*, double> m_values {};

public:
/**
* Sets the value for a variable in the solution.
*
* @since 1.3.0
* @param var Variable to assign a value to.
* @param value to assign to the variable.
*/
void setValue(const Var& var, double value);

/**
* Access the mutable value assigned to the variable.
*
* Initializes the assigned value to 0 if no value was assigned to the variable so far.
*
* @since 1.3.0
* @param var Variable to manipulate the value in the solution for.
* @return Mutable value assigned to the variable.
*/
double& operator()(const Var& var);
};

}
21 changes: 21 additions & 0 deletions include/scippp/model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <vector>

#include "scippp/constant_coefficient.hpp"
#include "scippp/initial_solution.hpp"
#include "scippp/lin_expr.hpp"
#include "scippp/lin_ineq.hpp"
#include "scippp/param.hpp"
Expand Down Expand Up @@ -326,5 +327,25 @@ class Model {
That is only required for use-cases not supported by SCIP++.
Consider adding the feature you are using to SCIP++!)")]] [[nodiscard]] Scip*
scip() const;

/**
* Adds a solution to %SCIP's solution pool.
*
* @since 1.3.0
* @param initialSolution to add to the solution pool.
* @param printReason Should all reasons of violations be printed?
* @param completely Should all violations be checked if \p printReason is true?
* @param checkBounds Should the bounds of the variables be checked?
* @param checkIntegrality Should integrality be checked?
* @param checkLpRows Do constraints represented by rows in the current LP have to be checked?
kahessler marked this conversation as resolved.
Show resolved Hide resolved
* @return \c true iff the solution was feasible and stored in the solution storage (i.e, good enough to keep).
*/
bool addSolution(
const InitialSolution& initialSolution,
bool printReason = true,
bool completely = true,
bool checkBounds = true,
bool checkIntegrality = true,
bool checkLpRows = true);
};
}
15 changes: 15 additions & 0 deletions source/initial_solution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "scippp/initial_solution.hpp"

namespace scippp {

void InitialSolution::setValue(const Var& var, double value)
{
m_values[&var] = value;
}

double& InitialSolution::operator()(const Var& var)
{
return m_values[&var];
}

}
25 changes: 25 additions & 0 deletions source/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,29 @@ double Model::getPrimalbound() const
return SCIPgetPrimalbound(m_scip);
}

bool Model::addSolution(
const InitialSolution& initialSolution,
bool printReason,
bool completely,
bool checkBounds,
bool checkIntegrality,
bool checkLpRows)
{
SCIP_Sol* sol { nullptr };
m_scipCallWrapper(SCIPcreateSol(m_scip, &sol, nullptr));
for (const auto& [var, value] : initialSolution.m_values) {
m_scipCallWrapper(SCIPsetSolVal(m_scip, sol, var->getVar(), value));
}
SCIP_Bool isFeasible { false };
m_scipCallWrapper(SCIPcheckSol(
m_scip, sol, printReason, completely, checkBounds, checkIntegrality, checkLpRows, &isFeasible));
SCIP_Bool isStored { false };
if (isFeasible) {
m_scipCallWrapper(SCIPaddSolFree(m_scip, &sol, &isStored));
} else {
m_scipCallWrapper(SCIPfreeSol(m_scip, &sol));
}
return isStored;
}

}
57 changes: 57 additions & 0 deletions test/test_initial_solution.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <boost/test/unit_test.hpp>

#include <boost/test/data/test_case.hpp>

#include "scippp/model.hpp"

using namespace scippp;
using namespace std;
namespace bdata = boost::unit_test::data;

BOOST_AUTO_TEST_SUITE(InitSolution)

BOOST_DATA_TEST_CASE(InitialSolutionStored, bdata::make({ 1, 2, 3 }), indexSetToOne)
{
Model model("Simple");
auto x1 = model.addVar("x_1", 1, VarType::BINARY);
auto x2 = model.addVar("x_2", 1, VarType::BINARY);
auto x3 = model.addVar("x_3", 1, VarType::BINARY);
model.addConstr(x1 + x2 + x3 <= 1, "upperBound");
model.setObjsense(Sense::MAXIMIZE);

InitialSolution is;
is.setValue(x1, indexSetToOne == 1 ? 1 : 0);
is.setValue(x2, indexSetToOne == 2 ? 1 : 0);
is.setValue(x3, indexSetToOne == 3 ? 1 : 0);
BOOST_TEST(model.addSolution(is));

BOOST_REQUIRE(model.getNSols() > 0);
auto sol = model.getBestSol();
BOOST_TEST(x1.getSolValAsInt(sol) == (indexSetToOne == 1 ? 1 : 0));
BOOST_TEST(x2.getSolValAsInt(sol) == (indexSetToOne == 2 ? 1 : 0));
BOOST_TEST(x3.getSolValAsInt(sol) == (indexSetToOne == 3 ? 1 : 0));
}

BOOST_AUTO_TEST_CASE(Infeasible)
{
Model model("Simple");
auto x1 = model.addVar("x_1", 1, VarType::BINARY);
model.setObjsense(Sense::MAXIMIZE);
InitialSolution is;
is.setValue(x1, 2);
BOOST_TEST(!model.addSolution(is));
}

BOOST_AUTO_TEST_CASE(UpdateSolution)
{
Model model("Simple");
const auto& [x1, x2] = model.addVars<2>("x_");
InitialSolution is;
is(x1) = 4;
is(x1) += 38;
is(x2) += 42;
BOOST_TEST(is(x1) == 42);
BOOST_TEST(is(x2) == 42);
}

BOOST_AUTO_TEST_SUITE_END()
Loading