Skip to content

Commit

Permalink
Merge pull request #11 from scipopt/writemodel
Browse files Browse the repository at this point in the history
Add Model::writeOrigProblem
  • Loading branch information
hedtke authored Oct 13, 2023
2 parents 08ea089 + 92bdfd5 commit e7c78c4
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 2 deletions.
23 changes: 23 additions & 0 deletions include/scippp/model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <algorithm>
#include <array>
#include <filesystem>
#include <functional>
#include <optional>
#include <scip/scip.h>
Expand Down Expand Up @@ -273,6 +274,28 @@ class Model {
}
}

/**
* Writes original problem to file.
*
* @since 1.1.0
* @param filename output file name including extension
* @param genericNames using generic variable (x0, x1, ...) and constraint names (c0, c1, ...) instead of the
* user-given names?
* @attention Do not use an std::string or std::filesystem::path as argument \p filename,
* as this will call the other overload instead!
*/
void writeOrigProblem(const std::filesystem::directory_entry& filename, bool genericNames = false) const;

/**
* Writes original problem to standard output.
*
* @since 1.1.0
* @param extension file extension to derive the output format from
* @param genericNames using generic variable (x0, x1, ...) and constraint names (c0, c1, ...) instead of the
* user-given names?
*/
void writeOrigProblem(const std::string& extension, bool genericNames = false) const;

/**
* Returns a pointer to the underlying %SCIP object.
*
Expand Down
10 changes: 10 additions & 0 deletions source/model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ void Model::setObjsense(Sense objsense)
m_scipCallWrapper(SCIPsetObjsense(m_scip, static_cast<SCIP_Objsense>(objsense)));
}

void Model::writeOrigProblem(const std::filesystem::directory_entry& filename, bool genericNames) const
{
m_scipCallWrapper(SCIPwriteOrigProblem(m_scip, filename.path().string().c_str(), nullptr, genericNames));
}

void Model::writeOrigProblem(const std::string& extension, bool genericNames) const
{
m_scipCallWrapper(SCIPwriteOrigProblem(m_scip, nullptr, extension.data(), genericNames));
}

Scip* Model::scip() const
{
return m_scip;
Expand Down
9 changes: 7 additions & 2 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
find_package(Boost CONFIG REQUIRED COMPONENTS unit_test_framework)
find_package(Boost CONFIG REQUIRED COMPONENTS unit_test_framework filesystem)
if (NOT TARGET Boost::unit_test_framework)
add_library(Boost::unit_test_framework IMPORTED INTERFACE)
set_property(TARGET Boost::unit_test_framework PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR})
set_property(TARGET Boost::unit_test_framework PROPERTY INTERFACE_LINK_LIBRARIES ${Boost_LIBRARIES})
endif ()
if (NOT TARGET Boost::filesystem)
add_library(Boost::filesystem IMPORTED INTERFACE)
set_property(TARGET Boost::filesystem PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR})
set_property(TARGET Boost::filesystem PROPERTY INTERFACE_LINK_LIBRARIES ${Boost_LIBRARIES})
endif ()

file(GLOB TEST_SOURCES *.cpp)
add_executable(tests ${TEST_SOURCES})
target_include_directories(tests SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
target_link_libraries(tests PRIVATE ScipPP Boost::unit_test_framework)
target_link_libraries(tests PRIVATE ScipPP Boost::unit_test_framework Boost::filesystem)
106 changes: 106 additions & 0 deletions test/test_io.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>
#include <boost/test/unit_test.hpp>
#include <fstream>
#include <sstream>

#include "scippp/model.hpp"

using namespace boost::algorithm;
using namespace scippp;
using namespace std;

class TempFile {
filesystem::path m_path { filesystem::temp_directory_path() };

public:
explicit TempFile(const string& extension)
{
m_path += boost::filesystem::unique_path("/%%%%-%%%%-%%%%-%%%%." + extension).string();
}
~TempFile()
{
filesystem::remove(m_path);
}
[[nodiscard]] filesystem::directory_entry path() const
{
return filesystem::directory_entry(m_path);
}
[[nodiscard]] string content() const
{
ifstream t(m_path);
ostringstream sstr;
sstr << t.rdbuf();
return sstr.str();
}
};

auto createModel()
{
Model model("Simple");
auto x1 = model.addVar("x_1", 1);
auto x2 = model.addVar("x_2", 1);
model.addConstr(x1 + x2 >= 1, "capacity");
model.addConstr(x1 == x2, "equal");
model.setObjsense(Sense::MINIMIZE);
return model;
}

BOOST_AUTO_TEST_SUITE(IO)

BOOST_AUTO_TEST_CASE(FileLP)
{
auto model { createModel() };

TempFile tf("lp");
model.writeOrigProblem(tf.path());
BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY);
auto content { tf.content() };
BOOST_TEST(contains(content, "Obj: +1 x_1 +1 x_2"));
BOOST_TEST(contains(content, "capacity:"));
BOOST_TEST(contains(content, "equal:"));
BOOST_TEST(contains(content, "Minimize"));
}

BOOST_AUTO_TEST_CASE(FileLPGenericNames)
{
auto model { createModel() };

TempFile tf("lp");
model.writeOrigProblem(tf.path(), true);
BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY);
auto content { tf.content() };
BOOST_TEST(contains(content, "Obj: +1 x0 +1 x1"));
BOOST_TEST(contains(content, "c0:"));
BOOST_TEST(contains(content, "c1:"));
BOOST_TEST(contains(content, "Minimize"));
}

BOOST_AUTO_TEST_CASE(FileMPS)
{
auto model { createModel() };

TempFile tf("mps");
model.writeOrigProblem(tf.path());
BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY);
auto content { tf.content() };
BOOST_TEST(contains(content, "G capacity"));
BOOST_TEST(contains(content, "E equal"));
BOOST_TEST(contains(content, "\n MIN"));
}

BOOST_AUTO_TEST_CASE(StdoutLP)
{
auto model { createModel() };
model.writeOrigProblem("lp");
BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY);
}

BOOST_AUTO_TEST_CASE(StdoutWithInvalidExtension)
{
auto model { createModel() };
model.writeOrigProblem("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
BOOST_TEST(model.getLastReturnCode() == SCIP_PLUGINNOTFOUND);
}

BOOST_AUTO_TEST_SUITE_END()

0 comments on commit e7c78c4

Please sign in to comment.