Skip to content

Commit

Permalink
Merge pull request #980 from sys-bio/issue-986-mcjit-copy-model
Browse files Browse the repository at this point in the history
Fix copying roadrunner models with MCJit.
  • Loading branch information
luciansmith committed Mar 29, 2022
2 parents ed30c47 + 9f15e8b commit 3b35d52
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 8 deletions.
39 changes: 36 additions & 3 deletions source/llvm/ModelResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,46 @@ namespace rrllvm {

void ModelResources::saveState(std::ostream &out) const {
symbols->saveState(out);

rr::saveBinary(out, sbmlMD5);

std::string moduleAsString = jit->getModuleAsString(sbmlMD5);
/**
* This bit of code is unintuitive really.
* The goal is to save the moduleStr variable for loading.
* Two situations arise:
* 1) moduleStr is not populated, but can be obtained from
* jit->compiledModuleBinaryStream (see MCJit::writeObjectToBinaryStream)
* 2) moduleStr is populated with the string we need
* Situation 1 occurs if this model has not been saved/loaded before.
* Situation 2 occurs if the model we are saving is itself a copy of an
* original model. This is a bad design but I can't figure out why
* jit->compiledModuleBinaryStream is empty second time around.
*/
std::string moduleAsString;
if (moduleStr.empty()){
/**
* If we already have a moduleStr, use it. This happens
* when a model has been saved and loaded already, like in:
* RoadRunner rr(OpenLinearFlux().str());
* RoadRunner rr2;
* rr2 = rr; // first time: rr.saveState used. rr2.loadState used
*/
moduleAsString = jit->getModuleAsString(sbmlMD5);
} else {
/**
* If we already have a moduleStr, use it. This happens
* when a model has been saved and loaded already, like in:
* RoadRunner rr(OpenLinearFlux().str());
* RoadRunner rr2;
* rr2 = rr; // first time: rr.saveState used. rr2.loadState used
* rr = rr2; // second time: rr2.saveState and rr.loadState
*/
moduleAsString = moduleStr;
}

rr::saveBinary(out, moduleAsString);

// MCJit uses this variable. LLJit does not.
// MCJit uses this variable. LLJit does not. Both are needed due to
// awkwardness of having old and new llvm based code.
bool usingCompiledModuleBinaryStream = false;

// see comment in loadState
Expand Down
61 changes: 61 additions & 0 deletions test/model_analysis/model_analysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "rrStringUtils.h"
#include "gtest/gtest.h"
#include "LLVMExecutableModel.h"
#include "rrConfig.h"
#include <filesystem>
#include "RoadRunnerTest.h"

Expand All @@ -21,6 +22,66 @@ class ModelAnalysisTests : public RoadRunnerTest {
};


TEST_F(ModelAnalysisTests, issue986a) {
//Config::setValue(Config::LLVM_BACKEND, Config::LLVM_BACKEND_VALUES::LLJIT);
rr::RoadRunner roadrunner((modelAnalysisModelsDir / "BIOMD0000000021.xml").string());
double t_start = 0;
double delta_time = 0.5;

//create copy for checkpoint
rr::RoadRunner roadrunnerB;

// save checkpoint
roadrunnerB = roadrunner;
std::stringstream* saved = roadrunnerB.saveStateS();
roadrunner.oneStep(t_start, delta_time);
std::cout << "P1: " << roadrunner.getValue("[P1]") << std::endl;

//go back to checkpoint if FEM did not converged
std::stringstream* saved2 = roadrunnerB.saveStateS();
//roadrunner = roadrunnerB;
roadrunner.loadStateS(saved2);
roadrunner = roadrunnerB;

//rerun with smaller timestep
roadrunner.oneStep(t_start, (delta_time / 2));

std::cout << "P1: " << roadrunner.getValue("[P1]") << std::endl;
}


TEST_F(ModelAnalysisTests, issue986b) {
rr::RoadRunner roadrunner((modelAnalysisModelsDir / "BIOMD0000000021.xml").string());
double t_start = 0;
double delta_time = 0.5;

// save State for checkpoint
std::stringstream* roadrunner_state = roadrunner.saveStateS();

roadrunner.oneStep(t_start, delta_time);
std::cout << "P1: " << roadrunner.getValue("[P1]") << std::endl;

//go back to checkpoint if FEM did not converged
roadrunner.loadStateS(roadrunner_state);

//save State again
std::stringstream* roadrunner_state2 = roadrunner.saveStateS();

//rerun with smaller timestep
roadrunner.oneStep(t_start, (delta_time / 2));

std::cout << "P1: " << roadrunner.getValue("[P1]") << std::endl;

//go back to checkpoint if FEM did not converged
roadrunner.loadStateS(roadrunner_state2);

//rerun with smaller timestep
roadrunner.oneStep(t_start, (delta_time / 4));

std::cout << "P1: " << roadrunner.getValue("[P1]") << std::endl;
}


TEST_F(ModelAnalysisTests, checkGetRatesOfChangeIds) {
//If a model has non-species with rates of change, 'getRatesOfChange' works fine,
// but the labels for those rates were wrong. This tests that they were fixed.
Expand Down
73 changes: 68 additions & 5 deletions test/serialization/state_saving.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "rrRoadRunner.h"
#include "rrUtils.h"
#include "sbml/SBMLDocument.h"
#include "Matrix.h"
#include "sbml/ListOf.h"
#include "sbml/Model.h"
#include "rrExecutableModel.h"
Expand Down Expand Up @@ -74,6 +75,8 @@ class StateSavingTests : public RoadRunnerTest {

};



bool StateSavingTests::RunStateSavingTest(void(*modification)(RoadRunner *, std::string), std::string version) {
bool result(false);
RoadRunner *rr;
Expand Down Expand Up @@ -421,10 +424,6 @@ bool StateSavingTests::StateRunTestModelFromScratch(void(*generate)(RoadRunner *
return result;
}





// IN LLVM 6.0.1, this test can result, depending on the OS,
// in llvm calling *exit* instead of throwing. We changed this
// in our own updated version of llvm, and updated the patch value
Expand All @@ -451,7 +450,6 @@ TEST_F(StateSavingTests, COPY_RR) {
ASSERT_TRUE(rr.getInstanceID() != rr2.getInstanceID());
}


TEST_F(StateSavingTests, COPY_RR_WITH_ASSIGNMENT) {
path f = rrTestSbmlTestSuiteDir_ / "semantic" / "00001" / "00001-sbml-l2v4.xml";
path stateFname = fs::current_path() / "save-state-tests.rr";
Expand All @@ -474,6 +472,71 @@ TEST_F(StateSavingTests, COPY_RR_TWICE) {
ASSERT_TRUE(rr.getInstanceID() != rr3.getInstanceID());
}

TEST_F(StateSavingTests, CopyModelLoadedConstructor) {
RoadRunner rr(OpenLinearFlux().str());
RoadRunner rr2(rr);
// need to use constructor to convert ls::Matrix to rr::Matrix
rr::Matrix<double> result = rr::Matrix<double>(*rr2.simulate(0, 10, 11));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result, 1e-3));
}

TEST_F(StateSavingTests, CopyModelLoadedAssignment) {
RoadRunner rr(OpenLinearFlux().str());
RoadRunner rr2 = rr;
// need to use constructor to convert ls::Matrix to rr::Matrix
rr::Matrix<double> result = rr::Matrix<double>(*rr2.simulate(0, 10, 11));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result, 1e-3));
}

TEST_F(StateSavingTests, CopyModelTwiceModelLoadedConstructor) {
RoadRunner rr(OpenLinearFlux().str());
RoadRunner rr2(rr);
RoadRunner rr3(rr);
// need to use constructor to convert ls::Matrix to rr::Matrix
rr::Matrix<double> result2 = rr::Matrix<double>(*rr2.simulate(0, 10, 11));
rr::Matrix<double> result3 = rr::Matrix<double>(*rr3.simulate(0, 10, 11));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result2, 1e-3));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result3, 1e-3));
}

TEST_F(StateSavingTests, CopyModelTwiceModelLoadedAssignment) {
RoadRunner rr(OpenLinearFlux().str());
RoadRunner rr2 = rr;
RoadRunner rr3 = rr;
// need to use constructor to convert ls::Matrix to rr::Matrix
rr::Matrix<double> result2 = rr::Matrix<double>(*rr2.simulate(0, 10, 11));
rr::Matrix<double> result3 = rr::Matrix<double>(*rr3.simulate(0, 10, 11));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result2, 1e-3));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result3, 1e-3));
}

// repeat with LLJit

TEST_F(StateSavingTests, CopyModelAndCopyBackMCJit) {
RoadRunner rr(OpenLinearFlux().str());
RoadRunner rr2;
rr2 = rr;
rr = rr2;
rr::Matrix<double> result = rr::Matrix<double>(*rr.simulate(0, 10, 11));
rr::Matrix<double> result2 = rr::Matrix<double>(*rr2.simulate(0, 10, 11));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result, 1e-3));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result2, 1e-3));
}

TEST_F(StateSavingTests, CopyModelAndCopyBackLLJit) {
Config::setValue(Config::LLVM_BACKEND, Config::LLJIT);
RoadRunner rr(OpenLinearFlux().str());
RoadRunner rr2;
rr2 = rr;
rr = rr2;
// need to use constructor to convert ls::Matrix to rr::Matrix
rr::Matrix<double> result = rr::Matrix<double>(*rr.simulate(0, 10, 11));
rr::Matrix<double> result2 = rr::Matrix<double>(*rr2.simulate(0, 10, 11));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result, 1e-3));
ASSERT_TRUE(OpenLinearFlux().timeSeriesResult().almostEquals(result2, 1e-3));
}


TEST_F(StateSavingTests, COPY_RR_TWICE2) {
RoadRunner rr(3, 2);
RoadRunner rr2(rr);
Expand Down

0 comments on commit 3b35d52

Please sign in to comment.