Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
119f73e
Add initial CLI support for CVaR queries
pjtimm Apr 14, 2026
3d1308a
logic: Add CvarFormula
pjtimm Apr 14, 2026
1764219
api: Rewrite CVaR queries to CvarFormula
pjtimm Apr 14, 2026
963b8b0
cvar: Add formula validation and extraction helper
pjtimm Apr 15, 2026
fa8ac2e
modelchecker: Wired CVaR formulas into sparse MDP checking
pjtimm Apr 15, 2026
beda413
cvar: Validate terminal rewards for sparse MDP queries
pjtimm Apr 16, 2026
cc4b624
cvar: Add backend input data for sparse MDP checking
pjtimm Apr 16, 2026
e522f3b
cvar: Add sparse backend helper stub
pjtimm Apr 16, 2026
a1c693d
cvar: Add thresholds and target partitions to preprocessing
pjtimm Apr 17, 2026
fd5d94b
cvar: add helper to build LP for given threshold
pjtimm Apr 17, 2026
abd3724
cvar: Solve threshold LPs and aggregate objective values
pjtimm Apr 17, 2026
ab360c6
Add CVaR query test for sparse MDPs
pjtimm Apr 20, 2026
7d15fab
Fix CVaR flow LP encoding for sparse MDPs
pjtimm Apr 20, 2026
06911bb
cvar: Add preprocessing of unreachable-target MECs
pjtimm Apr 20, 2026
34f50d8
Add CVaR preprocessing tests
pjtimm Apr 20, 2026
de2c0de
Add CVaR test MDP with alpha-sensitive and adaptive scheduling cases
pjtimm Apr 20, 2026
b33ac31
Apply code formatting
pjtimm Apr 21, 2026
2001c17
cvar: Add scheduler reconstruction via LP flow variables
pjtimm Apr 21, 2026
f962d59
Add CVaR scheduler tests and simplify CVaR test setup
pjtimm Apr 21, 2026
cbb1978
Add CVaR rational test
pjtimm Apr 21, 2026
f32a359
cvar: Added collapse for transient MEC
pjtimm Apr 23, 2026
ce98190
Refactor CVaR dispatch into query and problem classification
pjtimm Apr 27, 2026
7a23e1e
Refactor CVaR method selection and dispatch for SSP support
pjtimm Apr 28, 2026
1d1b2db
Add SSP CVaR model extraction with choice-cost lifting
pjtimm Apr 28, 2026
c3f8c47
Extract shared CVaR preprocessing utilities and added SSP preprocessing
pjtimm Apr 29, 2026
0651398
Clarify CVaR query and WR backend naming
pjtimm Apr 29, 2026
a0ab335
Extract SSP & WR CVaR preprocessing
pjtimm Apr 29, 2026
2be56a7
Refactor CVaR backend selection and dispatch
pjtimm Apr 29, 2026
9088170
Enforce SSP paper assumptions (including no 0 reward steps)
pjtimm Apr 29, 2026
ad82326
Add SSP expected cost-to-go preprocessing
pjtimm Apr 30, 2026
77ecc32
Add SSP Pareto front operations scaffolding
pjtimm Apr 30, 2026
b13c16f
Add SSP Pareto front iteration logic
pjtimm May 4, 2026
21fca7d
Implement SSP Pareto VI full loop
pjtimm May 4, 2026
18af168
Add deterministic SSP CVaR smoke test
pjtimm May 4, 2026
8f1291a
Implement and validate SSP CVaR Pareto VI semantics
pjtimm May 6, 2026
5cbc512
Corrected tail semantics
pjtimm May 6, 2026
a5f518e
Add binary search to find valid threshold range
pjtimm May 6, 2026
d6c45b8
format
pjtimm May 6, 2026
8b05116
Optimize CVaR threshold pruning
pjtimm May 7, 2026
4cbc796
Add singleton and empty fast paths for pareto set operations
pjtimm May 7, 2026
a5929bd
Skip SSP pareto union for single action states
pjtimm May 7, 2026
9f1b070
Optimize SSP Pareto VI hot loops
pjtimm May 7, 2026
bbdace7
Fuse scaled SSP Pareto Minkowski sums
pjtimm May 7, 2026
f35cdf1
Initialize SSP action fronts from first transition
pjtimm May 7, 2026
666a857
Build SSP action unions from point buffers
pjtimm May 7, 2026
436a43d
Optimize SSP Pareto-front canonicalization
pjtimm May 8, 2026
cef009a
Reduce SSP Pareto VI allocation overhead
pjtimm May 8, 2026
ba94231
Optimize SSP Pareto frontier construction
pjtimm May 8, 2026
ca393dc
Extract SSP Pareto value-iteration operator
pjtimm May 11, 2026
30bab6b
Add focused SSP Pareto VI tests
pjtimm May 11, 2026
1c4aa69
Merge remote-tracking branch 'upstream/master' into feature/cvar-mdp
pjtimm May 11, 2026
aa57589
Refactor CVaR model-checking dispatch
pjtimm Jun 10, 2026
b722dea
Use exact rational CVaR alpha and align wrapper integration
pjtimm Jun 10, 2026
5f69b78
Add focused tests for CVaR refactor
pjtimm Jun 10, 2026
9325b74
Simplified Storm rational parsing for CVaR alpha and type plumbing
pjtimm Jun 10, 2026
9e1e802
Support min-cost and max-reward tails in weighted CVaR LP
pjtimm Jun 11, 2026
6a23e47
Reduced option names
pjtimm Jun 11, 2026
1e06960
Add explicit CVaR reward interpretation selection
pjtimm Jun 16, 2026
1158598
Add CVaR interpretation type header
pjtimm Jun 16, 2026
f430641
Generalize SSP Pareto frontier orientation
pjtimm Jul 1, 2026
c64e70a
Add SSP reward preprocessing checks
pjtimm Jul 1, 2026
cac44df
Implement SSP reward lower-tail value iteration
pjtimm Jul 1, 2026
5f6c30d
Add SSP reward CVaR model tests
pjtimm Jul 1, 2026
e41587a
Format
pjtimm Jul 1, 2026
32ce646
Canonicalize SSP Pareto fronts after affine transforms
pjtimm Jul 16, 2026
4e5eb02
Merge upstream/master into feature/cvar-mdp
pjtimm Aug 6, 2026
42aaf88
Skip CVaR model tests without Z3
pjtimm Aug 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions resources/examples/testfiles/mdp/cvar_bad_mec_mdp.nm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mdp

module main
s : [0..3] init 0;

[] s=0 -> 1/2 : (s'=1) + 1/2 : (s'=2);
[] s=1 -> 1 : (s'=1);
[] s=2 -> 1 : (s'=3);
[] s=3 -> 1 : (s'=2);
endmodule

label "target" = s=1;

rewards "term"
s=1 : 4;
endrewards
41 changes: 41 additions & 0 deletions resources/examples/testfiles/mdp/cvar_branching_tradeoff_mdp.nm
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
mdp

module main
s : [0..10] init 0;

// Start: direct safe/balanced/risky options, plus an adaptive branch.
[safe] s=0 -> 1 : (s'=3);
[balanced] s=0 -> 3/4 : (s'=1) + 1/4 : (s'=5);
[adaptive] s=0 -> 1/2 : (s'=8) + 1/2 : (s'=9);
[risky] s=0 -> 1/2 : (s'=10) + 1/2 : (s'=7);

// Duplicate terminal rewards at 6 and 12 make threshold splitting observable.
[] s=1 -> 1 : (s'=1);
[] s=2 -> 1 : (s'=2);
[] s=3 -> 1 : (s'=3);
[] s=4 -> 1 : (s'=4);
[] s=5 -> 1 : (s'=5);
[] s=6 -> 1 : (s'=6);
[] s=7 -> 1 : (s'=7);

// After the adaptive probabilistic split, the scheduler can react to good/bad news.
[cash] s=8 -> 1 : (s'=4);
[push] s=8 -> 1/2 : (s'=6) + 1/2 : (s'=7);

[cash] s=9 -> 1 : (s'=2);
[push] s=9 -> 1/2 : (s'=10) + 1/2 : (s'=4);

[] s=10 -> 1 : (s'=10);
endmodule

label "target" = s=1 | s=2 | s=3 | s=4 | s=5 | s=6 | s=7 | s=10;

rewards "term"
s=1 : 6;
s=2 : 6;
s=3 : 7;
s=4 : 10;
s=5 : 12;
s=6 : 12;
s=7 : 20;
endrewards
15 changes: 15 additions & 0 deletions resources/examples/testfiles/mdp/cvar_nonabsorbing_target_mdp.nm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
mdp

module main
s : [0..2] init 0;

[] s=0 -> 1 : (s'=1);
[] s=1 -> 1 : (s'=2);
[] s=2 -> 1 : (s'=2);
endmodule

label "target" = s=1;

rewards "term"
s=1 : 5;
endrewards
19 changes: 19 additions & 0 deletions resources/examples/testfiles/mdp/cvar_simple_mdp.nm
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
mdp

module main
s : [0..3] init 0;

[] s=0 -> 1/2 : (s'=1) + 1/2 : (s'=2);
[] s=0 -> 1 : (s'=3);
[] s=1 -> 1 : (s'=1);
[] s=2 -> 1 : (s'=2);
[] s=3 -> 1 : (s'=3);
endmodule

label "target" = s=1 | s=2 | s=3;

rewards "term"
s=1 : 1;
s=2 : 3;
s=3 : 2;
endrewards
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

mdp

module main
s : [0..3] init 0;

[safe] s=0 -> 1 : (s'=3);
[risky] s=0 -> 1/2 : (s'=1) + 1/2 : (s'=2);
[low] s=1 -> 1 : (s'=3);
[high] s=2 -> 1 : (s'=3);
[] s=3 -> 1 : (s'=3);
endmodule

label "goal" = s=3;

rewards "cost"
[safe] true : 6;
[risky] true : 1;
[low] true : 1;
[high] true : 7;
endrewards
16 changes: 16 additions & 0 deletions resources/examples/testfiles/mdp/cvar_ssp_deterministic_mdp.nm
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mdp

module main
s : [0..2] init 0;

[step0] s=0 -> 1 : (s'=1);
[step1] s=1 -> 1 : (s'=2);
[] s=2 -> 1 : (s'=2);
endmodule

label "goal" = s=2;

rewards "cost"
[step0] true : 2;
[step1] true : 3;
endrewards
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
mdp

module main
s : [0..3] init 0;

[det] s=0 -> 1 : (s'=3);
[lottery] s=0 -> 1/2 : (s'=1) + 1/2 : (s'=2);
[low] s=1 -> 1 : (s'=3);
[high] s=2 -> 1 : (s'=3);
[] s=3 -> 1 : (s'=3);
endmodule

label "goal" = s=3;

rewards "reward"
[det] true : 6;
[lottery] true : 1;
[low] true : 7;
[high] true : 99;
endrewards
14 changes: 14 additions & 0 deletions resources/examples/testfiles/mdp/cvar_ssp_reward_geometric_mdp.nm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
mdp

module main
s : [0..1] init 0;

[step] s=0 -> 1/2 : (s'=0) + 1/2 : (s'=1);
[] s=1 -> 1 : (s'=1);
endmodule

label "goal" = s=1;

rewards "reward"
[step] true : 1;
endrewards
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mdp

module main
s : [0..1] init 0;

[loop] s=0 -> 1 : (s'=0);
[exit] s=0 -> 1 : (s'=1);
[] s=1 -> 1 : (s'=1);
endmodule

label "goal" = s=1;

rewards "reward"
[loop] true : 1;
[exit] true : 1;
endrewards
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mdp

module main
s : [0..2] init 0;

[split] s=0 -> 1/2 : (s'=1) + 1/2 : (s'=2);
[loop] s=1 -> 1 : (s'=1);
[] s=2 -> 1 : (s'=2);
endmodule

label "goal" = s=2;

rewards "reward"
[split] true : 1;
[loop] true : 1;
endrewards
20 changes: 20 additions & 0 deletions resources/examples/testfiles/mdp/cvar_ssp_reward_safe_risky_mdp.nm
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
mdp

module main
s : [0..3] init 0;

[safe] s=0 -> 1 : (s'=3);
[risky] s=0 -> 1/5 : (s'=1) + 4/5 : (s'=2);
[low] s=1 -> 1 : (s'=3);
[high] s=2 -> 1 : (s'=3);
[] s=3 -> 1 : (s'=3);
endmodule

label "goal" = s=3;

rewards "reward"
[safe] true : 6;
[risky] true : 1;
[low] true : 1;
[high] true : 9;
endrewards
23 changes: 23 additions & 0 deletions resources/examples/testfiles/mdp/cvar_target_reaching_mec_mdp.nm
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
mdp

module main
s : [0..4] init 0;

[] s=0 -> 1 : (s'=1);

[cycle12] s=1 -> 1 : (s'=2);
[takeLow] s=1 -> 1 : (s'=3);

[cycle21] s=2 -> 1 : (s'=1);
[takeRisk] s=2 -> 1/2 : (s'=3) + 1/2 : (s'=4);

[] s=3 -> 1 : (s'=3);
[] s=4 -> 1 : (s'=4);
endmodule

label "target" = s=3 | s=4;

rewards "term"
s=3 : 4;
s=4 : 10;
endrewards
11 changes: 11 additions & 0 deletions src/storm-cli-utilities/model-handling.h
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,9 @@ inline std::pair<SymbolicInput, ModelProcessingInformation> preprocessSymbolicIn
SymbolicInput output = input;

// Preprocess properties (if requested)
STORM_LOG_THROW(!(ioSettings.isPropertiesAsMultiSet() && ioSettings.isCvarSet()), storm::exceptions::InvalidArgumentException,
"Options '--propsasmulti' and '--cvar' can not be combined.");

if (ioSettings.isPropertiesAsMultiSet()) {
STORM_LOG_THROW(!input.properties.empty(), storm::exceptions::InvalidArgumentException,
"Can not translate properties to multi-objective formula because no properties were specified.");
Expand All @@ -440,6 +443,14 @@ inline std::pair<SymbolicInput, ModelProcessingInformation> preprocessSymbolicIn
output.properties = {storm::api::createMultiObjectiveProperty(output.properties, false)};
}

if (ioSettings.isCvarSet()) {
STORM_LOG_THROW(!input.properties.empty(), storm::exceptions::InvalidArgumentException,
"Can not translate properties to a CVaR formula because no properties were specified.");
STORM_LOG_THROW(output.properties.size() == 1, storm::exceptions::InvalidArgumentException,
"The '--cvar' option currently requires exactly one selected property.");
output.properties = {storm::api::createCvarProperty(output.properties.front(), ioSettings.getCvarAlpha())};
}

// Substitute constant definitions in symbolic input.
std::string constantDefinitionString = ioSettings.getConstantDefinitionString();
std::map<storm::expressions::Variable, storm::expressions::Expression> constantDefinitions;
Expand Down
30 changes: 29 additions & 1 deletion src/storm/api/properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,31 @@

#include <boost/algorithm/string.hpp>

#include "storm/logic/Formula.h"
#include "storm/exceptions/InvalidArgumentException.h"
#include "storm/logic/Formulas.h"
#include "storm/storage/SymbolicModelDescription.h"
#include "storm/storage/jani/Model.h"
#include "storm/storage/jani/Property.h"
#include "storm/storage/prism/Program.h"
#include "storm/utility/constants.h"
#include "storm/utility/macros.h"

namespace storm {
namespace api {
namespace {

storm::RationalNumber parseCvarAlpha(std::string const& input) {
std::string strippedInput = boost::algorithm::trim_copy(input);
STORM_LOG_THROW(!strippedInput.empty(), storm::exceptions::InvalidArgumentException, "Unable to parse CVaR alpha '" << input << "'.");

storm::RationalNumber alpha = storm::utility::convertNumber<storm::RationalNumber>(strippedInput);

STORM_LOG_THROW(storm::utility::zero<storm::RationalNumber>() < alpha && alpha < storm::utility::one<storm::RationalNumber>(),
storm::exceptions::InvalidArgumentException, "The CVaR alpha must be in the open interval (0, 1).");
return alpha;
}

} // namespace

std::vector<storm::jani::Property> substituteConstantsInProperties(std::vector<storm::jani::Property> const& properties,
std::map<storm::expressions::Variable, storm::expressions::Expression> const& substitution) {
Expand Down Expand Up @@ -67,6 +84,17 @@ std::vector<std::shared_ptr<storm::logic::Formula const>> extractFormulasFromPro
return formulas;
}

storm::jani::Property createCvarProperty(storm::jani::Property const& property, storm::RationalNumber const& alpha) {
STORM_LOG_WARN_COND(property.getFilter().isDefault(),
"Non-default property filter of property " << property.getName() << " will be dropped during conversion to CVaR property.");
auto cvarFormula = std::make_shared<storm::logic::CvarFormula>(alpha, property.getRawFormula());
return storm::jani::Property(property.getName(), cvarFormula, property.getUndefinedConstants(), property.getComment());
}

storm::jani::Property createCvarProperty(storm::jani::Property const& property, std::string const& alpha) {
return createCvarProperty(property, parseCvarAlpha(alpha));
}

storm::jani::Property createMultiObjectiveProperty(std::vector<storm::jani::Property> const& properties, bool lexicographic) {
std::set<storm::expressions::Variable> undefConstants;
std::string name = "";
Expand Down
4 changes: 4 additions & 0 deletions src/storm/api/properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <string>
#include <vector>

#include "storm/adapters/RationalNumberAdapter.h"

namespace storm {

namespace jani {
Expand Down Expand Up @@ -36,6 +38,8 @@ std::vector<storm::jani::Property> substituteTranscendentalNumbersInProperties(s
std::vector<storm::jani::Property> filterProperties(std::vector<storm::jani::Property> const& properties,
boost::optional<std::set<std::string>> const& propertyFilter);
std::vector<std::shared_ptr<storm::logic::Formula const>> extractFormulasFromProperties(std::vector<storm::jani::Property> const& properties);
storm::jani::Property createCvarProperty(storm::jani::Property const& property, storm::RationalNumber const& alpha);
storm::jani::Property createCvarProperty(storm::jani::Property const& property, std::string const& alpha);
storm::jani::Property createMultiObjectiveProperty(std::vector<storm::jani::Property> const& properties, bool lexicographic);

} // namespace api
Expand Down
1 change: 1 addition & 0 deletions src/storm/environment/SubEnvironment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ void SubEnvironment<EnvironmentType>::assertInitialized() const {

template class SubEnvironment<InternalEnvironment>;

template class SubEnvironment<CvarModelCheckerEnvironment>;
template class SubEnvironment<ConditionalModelCheckerEnvironment>;
template class SubEnvironment<MultiObjectiveModelCheckerEnvironment>;
template class SubEnvironment<ModelCheckerEnvironment>;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include "storm/environment/modelchecker/ConditionalModelCheckerEnvironment.h"
#include "storm/environment/modelchecker/CvarModelCheckerEnvironment.h"
#include "storm/environment/modelchecker/ModelCheckerEnvironment.h"
#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h"
#include "storm/environment/modelchecker/MultiObjectiveModelCheckerEnvironment.h"
34 changes: 34 additions & 0 deletions src/storm/environment/modelchecker/CvarModelCheckerEnvironment.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "storm/environment/modelchecker/CvarModelCheckerEnvironment.h"

#include "storm/settings/SettingsManager.h"
#include "storm/settings/modules/CvarSettings.h"

namespace storm {

CvarModelCheckerEnvironment::CvarModelCheckerEnvironment() {
auto const& cvarSettings = storm::settings::getModule<storm::settings::modules::CvarSettings>();
method = cvarSettings.getCvarMethod();
interpretationSelection = cvarSettings.getInterpretationSelection();
}

CvarModelCheckerEnvironment::~CvarModelCheckerEnvironment() {
// Intentionally left empty
}

storm::modelchecker::cvar::CvarMethod const& CvarModelCheckerEnvironment::getMethod() const {
return method;
}

void CvarModelCheckerEnvironment::setMethod(storm::modelchecker::cvar::CvarMethod value) {
method = value;
}

storm::modelchecker::cvar::CvarInterpretationSelection const& CvarModelCheckerEnvironment::getInterpretationSelection() const {
return interpretationSelection;
}

void CvarModelCheckerEnvironment::setInterpretationSelection(storm::modelchecker::cvar::CvarInterpretationSelection value) {
interpretationSelection = value;
}

} // namespace storm
Loading
Loading