837 lines
48 KiB
837 lines
48 KiB
#include "DFTASFChecker.h"
|
|
#include "SmtConstraint.cpp"
|
|
#include <string>
|
|
|
|
#include "storm/utility/file.h"
|
|
#include "storm/utility/bitoperations.h"
|
|
#include "storm-parsers/parser/ExpressionCreator.h"
|
|
#include "storm/solver/SmtSolver.h"
|
|
#include "storm/storage/expressions/ExpressionManager.h"
|
|
#include "storm/storage/expressions/Type.h"
|
|
#include "storm/exceptions/NotImplementedException.h"
|
|
#include "storm/exceptions/NotSupportedException.h"
|
|
|
|
namespace storm {
|
|
|
|
namespace modelchecker {
|
|
DFTASFChecker::DFTASFChecker(storm::storage::DFT<ValueType> const &dft) : dft(dft) {
|
|
// Intentionally left empty.
|
|
}
|
|
|
|
void DFTASFChecker::activateExperimentalMode() {
|
|
STORM_LOG_WARN("DFT-SMT-Checker now runs in experimental mode, no guarantee for correct results is given!");
|
|
experimentalMode = true;
|
|
}
|
|
|
|
uint64_t DFTASFChecker::getClaimVariableIndex(uint64_t spare, uint64_t child) const {
|
|
return claimVariables.at(SpareAndChildPair(spare, child));
|
|
}
|
|
|
|
void DFTASFChecker::convert() {
|
|
std::vector<uint64_t> beVariables;
|
|
notFailed = dft.nrBasicElements() + 1; // Value indicating the element is not failed
|
|
|
|
// Initialize variables
|
|
for (size_t i = 0; i < dft.nrElements(); ++i) {
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(i);
|
|
varNames.push_back("t_" + element->name());
|
|
timePointVariables.emplace(i, varNames.size() - 1);
|
|
switch (element->type()) {
|
|
case storm::storage::DFTElementType::BE_EXP:
|
|
beVariables.push_back(varNames.size() - 1);
|
|
break;
|
|
case storm::storage::DFTElementType::BE_CONST:
|
|
STORM_LOG_THROW(experimentalMode, storm::exceptions::NotSupportedException,
|
|
"Constant BEs are not supported in SMT translation.");
|
|
STORM_LOG_WARN("Constant BEs are only experimentally supported");
|
|
break;
|
|
case storm::storage::DFTElementType::SPARE:
|
|
{
|
|
auto spare = std::static_pointer_cast<storm::storage::DFTSpare<double> const>(element);
|
|
for (auto const &spareChild : spare->children()) {
|
|
varNames.push_back("c_" + element->name() + "_" + spareChild->name());
|
|
claimVariables.emplace(SpareAndChildPair(element->id(), spareChild->id()),
|
|
varNames.size() - 1);
|
|
}
|
|
break;
|
|
}
|
|
case storm::storage::DFTElementType::PDEP: {
|
|
varNames.push_back("dep_" + element->name());
|
|
dependencyVariables.emplace(element->id(), varNames.size() - 1);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
// Initialize variables indicating Markovian states
|
|
for (size_t i = 0; i < dft.nrBasicElements(); ++i) {
|
|
varNames.push_back("m_" + std::to_string(i));
|
|
markovianVariables.emplace(i, varNames.size() - 1);
|
|
}
|
|
|
|
|
|
// Generate constraints
|
|
|
|
// All BEs have to fail (first part of constraint 12)
|
|
for (auto const &beV : beVariables) {
|
|
constraints.push_back(std::make_shared<BetweenValues>(beV, 1, dft.nrBasicElements()));
|
|
}
|
|
|
|
// No two BEs fail at the same time (second part of constraint 12)
|
|
constraints.push_back(std::make_shared<PairwiseDifferent>(beVariables));
|
|
constraints.back()->setDescription("No two BEs fail at the same time");
|
|
|
|
// Initialize claim variables in [1, |BE|+1]
|
|
for (auto const &claimVariable : claimVariables) {
|
|
constraints.push_back(std::make_shared<BetweenValues>(claimVariable.second, 0, notFailed));
|
|
}
|
|
|
|
// Encoding for gates
|
|
for (size_t i = 0; i < dft.nrElements(); ++i) {
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(i);
|
|
STORM_LOG_ASSERT(i == element->id(), "Id and index should match.");
|
|
|
|
// Get indices for gate children
|
|
std::vector<uint64_t> childVarIndices;
|
|
if (element->isGate()) {
|
|
std::shared_ptr<storm::storage::DFTGate<ValueType> const> gate = dft.getGate(i);
|
|
for (auto const &child : gate->children()) {
|
|
childVarIndices.push_back(timePointVariables.at(child->id()));
|
|
}
|
|
}
|
|
|
|
switch (element->type()) {
|
|
case storm::storage::DFTElementType::BE_EXP:
|
|
case storm::storage::DFTElementType::BE_CONST:
|
|
// BEs were already considered before
|
|
break;
|
|
case storm::storage::DFTElementType::AND:
|
|
generateAndConstraint(i, childVarIndices, element);
|
|
break;
|
|
case storm::storage::DFTElementType::OR:
|
|
generateOrConstraint(i, childVarIndices, element);
|
|
break;
|
|
case storm::storage::DFTElementType::VOT:
|
|
generateVotConstraint(i, childVarIndices, element);
|
|
break;
|
|
case storm::storage::DFTElementType::PAND:
|
|
generatePandConstraint(i, childVarIndices, element);
|
|
break;
|
|
case storm::storage::DFTElementType::POR:
|
|
generatePorConstraint(i, childVarIndices, element);
|
|
break;
|
|
case storm::storage::DFTElementType::SEQ:
|
|
generateSeqConstraint(childVarIndices, element);
|
|
break;
|
|
case storm::storage::DFTElementType::SPARE:
|
|
generateSpareConstraint(i, childVarIndices, element);
|
|
break;
|
|
case storm::storage::DFTElementType::PDEP:
|
|
generatePdepConstraint(i, childVarIndices, element);
|
|
break;
|
|
default:
|
|
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException,
|
|
"SMT encoding for type '" << element->type() << "' is not supported.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Constraint (8) intentionally commented out for testing purposes
|
|
//addClaimingConstraints();
|
|
|
|
// Handle dependencies
|
|
addMarkovianConstraints();
|
|
}
|
|
|
|
// Constraint Generator Functions
|
|
|
|
void DFTASFChecker::generateAndConstraint(size_t i, std::vector<uint64_t> childVarIndices,
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element) {
|
|
// Constraint for AND gate (constraint 1)
|
|
constraints.push_back(std::make_shared<IsMaximum>(timePointVariables.at(i), childVarIndices));
|
|
constraints.back()->setDescription("AND gate " + element->name());
|
|
}
|
|
|
|
void DFTASFChecker::generateOrConstraint(size_t i, std::vector<uint64_t> childVarIndices,
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element) {
|
|
// Constraint for OR gate (constraint 2)
|
|
constraints.push_back(std::make_shared<IsMinimum>(timePointVariables.at(i), childVarIndices));
|
|
constraints.back()->setDescription("OR gate " + element->name());
|
|
}
|
|
|
|
void DFTASFChecker::generateVotConstraint(size_t i, std::vector<uint64_t> childVarIndices,
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element) {
|
|
auto vot = std::static_pointer_cast<storm::storage::DFTVot<double> const>(element);
|
|
// VOTs are implemented via OR over ANDs with all possible combinations
|
|
std::vector<uint64_t> tmpVars;
|
|
size_t k = 0;
|
|
// Generate all permutations of k out of n
|
|
size_t combination = smallestIntWithNBitsSet(static_cast<size_t>(vot->threshold()));
|
|
do {
|
|
// Construct selected children from combination
|
|
std::vector<uint64_t> combinationChildren;
|
|
for (size_t j = 0; j < vot->nrChildren(); ++j) {
|
|
if (combination & (1 << j)) {
|
|
combinationChildren.push_back(childVarIndices.at(j));
|
|
}
|
|
}
|
|
// Introduce temporary variable for this AND
|
|
varNames.push_back("v_" + vot->name() + "_" + std::to_string(k));
|
|
size_t index = varNames.size() - 1;
|
|
tmpVars.push_back(index);
|
|
tmpTimePointVariables.push_back(index);
|
|
// AND over the selected children
|
|
constraints.push_back(std::make_shared<IsMaximum>(index, combinationChildren));
|
|
constraints.back()->setDescription("VOT gate " + element->name() + ": AND no. " + std::to_string(k));
|
|
// Generate next permutation
|
|
combination = nextBitPermutation(combination);
|
|
++k;
|
|
} while (combination < (1 << vot->nrChildren()) && combination != 0);
|
|
|
|
// Constraint is OR over all possible combinations
|
|
constraints.push_back(std::make_shared<IsMinimum>(timePointVariables.at(i), tmpVars));
|
|
constraints.back()->setDescription("VOT gate " + element->name() + ": OR");
|
|
}
|
|
|
|
void DFTASFChecker::generatePandConstraint(size_t i, std::vector<uint64_t> childVarIndices,
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element) {
|
|
// Constraint for PAND gate (constraint 3)
|
|
std::shared_ptr<SmtConstraint> ifC = std::make_shared<Sorted>(childVarIndices);
|
|
std::shared_ptr<SmtConstraint> thenC = std::make_shared<IsEqual>(timePointVariables.at(i),
|
|
childVarIndices.back());
|
|
std::shared_ptr<SmtConstraint> elseC = std::make_shared<IsConstantValue>(timePointVariables.at(i),
|
|
notFailed);
|
|
constraints.push_back(std::make_shared<IfThenElse>(ifC, thenC, elseC));
|
|
constraints.back()->setDescription("PAND gate " + element->name());
|
|
}
|
|
|
|
void DFTASFChecker::generatePorConstraint(size_t i, std::vector<uint64_t> childVarIndices,
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element) {
|
|
// Constraint for POR gate
|
|
// First child fails before all others
|
|
std::vector<std::shared_ptr<SmtConstraint>> firstSmallestC;
|
|
uint64_t timeFirstChild = childVarIndices.front();
|
|
for (uint64_t i = 1; i < childVarIndices.size(); ++i) {
|
|
firstSmallestC.push_back(std::make_shared<IsLess>(timeFirstChild, childVarIndices.at(i)));
|
|
}
|
|
std::shared_ptr<SmtConstraint> ifC = std::make_shared<And>(firstSmallestC);
|
|
std::shared_ptr<SmtConstraint> thenC = std::make_shared<IsEqual>(timePointVariables.at(i),
|
|
childVarIndices.front());
|
|
std::shared_ptr<SmtConstraint> elseC = std::make_shared<IsConstantValue>(timePointVariables.at(i),
|
|
notFailed);
|
|
constraints.push_back(std::make_shared<IfThenElse>(ifC, thenC, elseC));
|
|
constraints.back()->setDescription("POR gate " + element->name());
|
|
}
|
|
|
|
void DFTASFChecker::generateSeqConstraint(std::vector<uint64_t> childVarIndices,
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element) {
|
|
// Constraint for SEQ gate (constraint 4)
|
|
// As the restriction is not a gate we have to enumerate its children here
|
|
auto seq = std::static_pointer_cast<storm::storage::DFTRestriction<double> const>(element);
|
|
for (auto const &child : seq->children()) {
|
|
childVarIndices.push_back(timePointVariables.at(child->id()));
|
|
}
|
|
|
|
constraints.push_back(std::make_shared<Sorted>(childVarIndices));
|
|
constraints.back()->setDescription("SEQ gate " + element->name());
|
|
}
|
|
|
|
void DFTASFChecker::generateSpareConstraint(size_t i, std::vector<uint64_t> childVarIndices,
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element) {
|
|
auto spare = std::static_pointer_cast<storm::storage::DFTSpare<double> const>(element);
|
|
auto const &children = spare->children();
|
|
uint64_t firstChild = children.front()->id();
|
|
uint64_t lastChild = children.back()->id();
|
|
|
|
// First child of each spare is claimed in the beginning
|
|
constraints.push_back(std::make_shared<IsConstantValue>(getClaimVariableIndex(spare->id(), firstChild), 0));
|
|
constraints.back()->setDescription("SPARE gate " + spare->name() + " claims first child");
|
|
|
|
// If last child is claimed before failure, then the spare fails when the last child fails (constraint 5)
|
|
std::shared_ptr<SmtConstraint> leftC = std::make_shared<IsLess>(
|
|
getClaimVariableIndex(spare->id(), lastChild), childVarIndices.back());
|
|
constraints.push_back(std::make_shared<Implies>(leftC, std::make_shared<IsEqual>(timePointVariables.at(i),
|
|
childVarIndices.back())));
|
|
constraints.back()->setDescription("Last child & claimed -> SPARE fails");
|
|
|
|
// Construct constraint for trying to claim next child
|
|
STORM_LOG_ASSERT(children.size() >= 2, "Spare has only one child");
|
|
for (uint64_t currChild = 0; currChild < children.size() - 1; ++currChild) {
|
|
uint64_t timeCurrChild = childVarIndices.at(currChild); // Moment when current child fails
|
|
// If i-th child fails after being claimed, then try to claim next child (constraint 6)
|
|
std::shared_ptr<SmtConstraint> tryClaimC = generateTryToClaimConstraint(spare, currChild + 1,
|
|
timeCurrChild);
|
|
constraints.push_back(std::make_shared<Iff>(
|
|
std::make_shared<IsLess>(getClaimVariableIndex(spare->id(), children.at(currChild)->id()),
|
|
timeCurrChild), tryClaimC));
|
|
constraints.back()->setDescription("Try to claim " + std::to_string(currChild + 2) + "th child");
|
|
}
|
|
}
|
|
|
|
std::shared_ptr<SmtConstraint>
|
|
DFTASFChecker::generateTryToClaimConstraint(std::shared_ptr<storm::storage::DFTSpare<ValueType> const> spare,
|
|
uint64_t childIndex, uint64_t timepoint) const {
|
|
auto child = spare->children().at(childIndex);
|
|
uint64_t timeChild = timePointVariables.at(child->id()); // Moment when the child fails
|
|
uint64_t claimChild = getClaimVariableIndex(spare->id(), child->id()); // Moment the spare claims the child
|
|
|
|
std::vector<std::shared_ptr<SmtConstraint>> noClaimingPossible;
|
|
// Child cannot be claimed.
|
|
if (childIndex + 1 < spare->children().size()) {
|
|
// Consider next child for claiming (second case in constraint 7)
|
|
noClaimingPossible.push_back(generateTryToClaimConstraint(spare, childIndex + 1, timepoint));
|
|
} else {
|
|
// Last child: spare fails at same point as this child (third case in constraint 7)
|
|
noClaimingPossible.push_back(std::make_shared<IsEqual>(timePointVariables.at(spare->id()), timepoint));
|
|
}
|
|
std::shared_ptr<SmtConstraint> elseCaseC = std::make_shared<And>(noClaimingPossible);
|
|
|
|
// Check if next child is available (first case in constraint 7)
|
|
std::vector<std::shared_ptr<SmtConstraint>> claimingPossibleC;
|
|
// Next child is not yet failed
|
|
claimingPossibleC.push_back(std::make_shared<IsLess>(timepoint, timeChild));
|
|
// Child is not yet claimed by a different spare
|
|
for (auto const &otherSpare : child->parents()) {
|
|
if (otherSpare->id() == spare->id()) {
|
|
// not a different spare.
|
|
continue;
|
|
}
|
|
claimingPossibleC.push_back(std::make_shared<IsLess>(timepoint,
|
|
getClaimVariableIndex(otherSpare->id(),
|
|
child->id())));
|
|
}
|
|
|
|
// Claim child if available
|
|
std::shared_ptr<SmtConstraint> firstCaseC = std::make_shared<IfThenElse>(
|
|
std::make_shared<And>(claimingPossibleC), std::make_shared<IsEqual>(claimChild, timepoint),
|
|
elseCaseC);
|
|
return firstCaseC;
|
|
}
|
|
|
|
void DFTASFChecker::addClaimingConstraints() {
|
|
// Only one spare can claim a child (constraint 8)
|
|
// and only not failed children can be claimed (addition to constrain 8)
|
|
for (size_t i = 0; i < dft.nrElements(); ++i) {
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(i);
|
|
if (element->isSpareGate()) {
|
|
auto spare = std::static_pointer_cast<storm::storage::DFTSpare<double> const>(element);
|
|
for (auto const &child : spare->children()) {
|
|
std::vector<std::shared_ptr<SmtConstraint>> additionalC;
|
|
uint64_t timeClaiming = getClaimVariableIndex(spare->id(), child->id());
|
|
std::shared_ptr<SmtConstraint> leftC = std::make_shared<IsLessConstant>(timeClaiming,
|
|
notFailed);
|
|
// Child must be operational at time of claiming
|
|
additionalC.push_back(
|
|
std::make_shared<IsLess>(timeClaiming, timePointVariables.at(child->id())));
|
|
// No other spare claims this child
|
|
for (auto const &parent : child->parents()) {
|
|
if (parent->isSpareGate() && parent->id() != spare->id()) {
|
|
// Different spare
|
|
additionalC.push_back(std::make_shared<IsConstantValue>(
|
|
getClaimVariableIndex(parent->id(), child->id()), notFailed));
|
|
}
|
|
}
|
|
constraints.push_back(std::make_shared<Implies>(leftC, std::make_shared<And>(additionalC)));
|
|
constraints.back()->setDescription(
|
|
"Child " + child->name() + " must be operational at time of claiming by spare " +
|
|
spare->name() + " and can only be claimed by one spare.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void DFTASFChecker::generatePdepConstraint(size_t i, std::vector<uint64_t> childVarIndices,
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element) {
|
|
auto dependency = std::static_pointer_cast<storm::storage::DFTDependency<double> const>(element);
|
|
auto const &dependentEvents = dependency->dependentEvents();
|
|
auto const &trigger = dependency->triggerEvent();
|
|
std::vector<uint64_t> dependentIndices;
|
|
for (size_t j = 0; j < dependentEvents.size(); ++j) {
|
|
dependentIndices.push_back(timePointVariables.at(dependentEvents[j]->id()));
|
|
}
|
|
|
|
constraints.push_back(std::make_shared<IsMaximum>(dependencyVariables.at(i), dependentIndices));
|
|
constraints.back()->setDescription("Dependency " + element->name() + ": Last element");
|
|
constraints.push_back(
|
|
std::make_shared<IsEqual>(timePointVariables.at(i), timePointVariables.at(trigger->id())));
|
|
constraints.back()->setDescription("Dependency " + element->name() + ": Trigger element");
|
|
}
|
|
|
|
void DFTASFChecker::addMarkovianConstraints() {
|
|
uint64_t nrMarkovian = dft.nrBasicElements();
|
|
std::set<size_t> depElements;
|
|
// Vector containing (non-)Markovian constraints for each timepoint
|
|
std::vector<std::vector<std::shared_ptr<SmtConstraint>>> markovianC(nrMarkovian);
|
|
std::vector<std::vector<std::shared_ptr<SmtConstraint>>> nonMarkovianC(nrMarkovian);
|
|
std::vector<std::vector<std::shared_ptr<SmtConstraint>>> notColdC(nrMarkovian);
|
|
|
|
// All dependent events of a failed trigger have failed as well (constraint 9)
|
|
for (size_t j = 0; j < dft.nrElements(); ++j) {
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(j);
|
|
if (element->hasOutgoingDependencies()) {
|
|
for (uint64_t i = 0; i < nrMarkovian; ++i) {
|
|
std::shared_ptr<SmtConstraint> triggerFailed = std::make_shared<IsLessEqualConstant>(
|
|
timePointVariables.at(j), i);
|
|
std::vector<std::shared_ptr<SmtConstraint>> depFailed;
|
|
for (auto const &dependency : element->outgoingDependencies()) {
|
|
for (auto const &depElement : dependency->dependentEvents()) {
|
|
depFailed.push_back(
|
|
std::make_shared<IsLessEqualConstant>(timePointVariables.at(depElement->id()),
|
|
i));
|
|
}
|
|
}
|
|
markovianC[i].push_back(
|
|
std::make_shared<Implies>(triggerFailed, std::make_shared<And>(depFailed)));
|
|
}
|
|
}
|
|
}
|
|
for (uint64_t i = 0; i < nrMarkovian; ++i) {
|
|
constraints.push_back(
|
|
std::make_shared<Iff>(std::make_shared<IsBoolValue>(markovianVariables.at(i), true),
|
|
std::make_shared<And>(markovianC[i])));
|
|
constraints.back()->setDescription("Markovian (" + std::to_string(i) +
|
|
") iff all dependent events which trigger failed also failed.");
|
|
}
|
|
|
|
// In non-Markovian steps the next failed element is a dependent BE (constraint 10) + additions to specification in paper
|
|
for (size_t j = 0; j < dft.nrElements(); ++j) {
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(j);
|
|
if (element->isBasicElement()) {
|
|
auto be = std::static_pointer_cast<storm::storage::DFTBE<double> const>(element);
|
|
|
|
if (be->hasIngoingDependencies()) {
|
|
depElements.emplace(j);
|
|
for (uint64_t i = 0; i < nrMarkovian - 1; ++i) {
|
|
std::shared_ptr<SmtConstraint> nextFailure = std::make_shared<IsConstantValue>(
|
|
timePointVariables.at(j), i + 1);
|
|
std::vector<std::shared_ptr<SmtConstraint>> triggerFailed;
|
|
for (auto const &dependency : be->ingoingDependencies()) {
|
|
triggerFailed.push_back(std::make_shared<IsLessEqualConstant>(
|
|
timePointVariables.at(dependency->triggerEvent()->id()), i));
|
|
}
|
|
nonMarkovianC[i].push_back(
|
|
std::make_shared<Implies>(nextFailure, std::make_shared<Or>(triggerFailed)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (uint64_t i = 0; i < nrMarkovian; ++i) {
|
|
std::vector<std::shared_ptr<SmtConstraint>> dependentConstr;
|
|
for (auto dependentEvent: depElements) {
|
|
std::shared_ptr<SmtConstraint> nextFailure = std::make_shared<IsConstantValue>(
|
|
timePointVariables.at(dependentEvent), i + 1);
|
|
dependentConstr.push_back(nextFailure);
|
|
}
|
|
// Add Constraint that any DEPENDENT event has to fail next
|
|
nonMarkovianC[i].push_back(std::make_shared<Or>(dependentConstr));
|
|
constraints.push_back(
|
|
std::make_shared<Implies>(std::make_shared<IsBoolValue>(markovianVariables.at(i), false),
|
|
std::make_shared<And>(nonMarkovianC[i])));
|
|
constraints.back()->setDescription(
|
|
"Non-Markovian (" + std::to_string(i) + ") -> next failure is dependent BE.");
|
|
}
|
|
|
|
// In Markovian steps the failure rate is positive (constraint 11)
|
|
for (size_t j = 0; j < dft.nrElements(); ++j) {
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(j);
|
|
if (element->isBasicElement()) {
|
|
auto be = std::static_pointer_cast<storm::storage::DFTBE<double> const>(element);
|
|
for (uint64_t i = 0; i < nrMarkovian; ++i) {
|
|
std::shared_ptr<SmtConstraint> nextFailure = std::make_shared<IsConstantValue>(
|
|
timePointVariables.at(j), i + 1);
|
|
// BE is not cold
|
|
// TODO: implement use of activation variables here
|
|
notColdC[i].push_back(std::make_shared<Implies>(nextFailure, std::make_shared<IsTrue>(be->canFail())));
|
|
}
|
|
}
|
|
}
|
|
for (uint64_t i = 0; i < nrMarkovian; ++i) {
|
|
constraints.push_back(
|
|
std::make_shared<Implies>(std::make_shared<IsBoolValue>(markovianVariables.at(i), true),
|
|
std::make_shared<And>(notColdC[i])));
|
|
constraints.back()->setDescription("Markovian (" + std::to_string(i) + ") -> positive failure rate.");
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void DFTASFChecker::toFile(std::string const &filename) {
|
|
std::ofstream stream;
|
|
storm::utility::openFile(filename, stream);
|
|
stream << "; time point variables" << std::endl;
|
|
for (auto const &timeVarEntry : timePointVariables) {
|
|
stream << "(declare-fun " << varNames[timeVarEntry.second] << "() Int)" << std::endl;
|
|
}
|
|
stream << "; claim variables" << std::endl;
|
|
for (auto const &claimVarEntry : claimVariables) {
|
|
stream << "(declare-fun " << varNames[claimVarEntry.second] << "() Int)" << std::endl;
|
|
}
|
|
stream << "; Markovian variables" << std::endl;
|
|
for (auto const &markovianVarEntry : markovianVariables) {
|
|
stream << "(declare-fun " << varNames[markovianVarEntry.second] << "() Bool)" << std::endl;
|
|
}
|
|
stream << "; Dependency variables" << std::endl;
|
|
for (auto const &depVarEntry : dependencyVariables) {
|
|
stream << "(declare-fun " << varNames[depVarEntry.second] << "() Int)" << std::endl;
|
|
}
|
|
if (!tmpTimePointVariables.empty()) {
|
|
stream << "; Temporary variables" << std::endl;
|
|
for (auto const &tmpVar : tmpTimePointVariables) {
|
|
stream << "(declare-fun " << varNames[tmpVar] << "() Int)" << std::endl;
|
|
}
|
|
}
|
|
for (auto const &constraint : constraints) {
|
|
if (!constraint->description().empty()) {
|
|
stream << "; " << constraint->description() << std::endl;
|
|
}
|
|
stream << "(assert " << constraint->toSmtlib2(varNames) << ")" << std::endl;
|
|
}
|
|
stream << "(check-sat)" << std::endl;
|
|
storm::utility::closeFile(stream);
|
|
}
|
|
|
|
void DFTASFChecker::toSolver() {
|
|
// First convert the DFT
|
|
convert();
|
|
|
|
std::shared_ptr<storm::expressions::ExpressionManager> manager(new storm::expressions::ExpressionManager());
|
|
solver = storm::utility::solver::SmtSolverFactory().create(
|
|
*manager);
|
|
//Add variables to manager
|
|
for (auto const &timeVarEntry : timePointVariables) {
|
|
manager->declareIntegerVariable(varNames[timeVarEntry.second]);
|
|
}
|
|
for (auto const &claimVarEntry : claimVariables) {
|
|
manager->declareIntegerVariable(varNames[claimVarEntry.second]);
|
|
}
|
|
for (auto const &markovianVarEntry : markovianVariables) {
|
|
manager->declareBooleanVariable(varNames[markovianVarEntry.second]);
|
|
}
|
|
if (!tmpTimePointVariables.empty()) {
|
|
for (auto const &tmpVar : tmpTimePointVariables) {
|
|
manager->declareIntegerVariable(varNames[tmpVar]);
|
|
}
|
|
}
|
|
for (auto const &depVarEntry : dependencyVariables) {
|
|
manager->declareIntegerVariable(varNames[depVarEntry.second]);
|
|
}
|
|
// Add constraints to solver
|
|
for (auto const &constraint : constraints) {
|
|
solver->add(constraint->toExpression(varNames, manager));
|
|
}
|
|
|
|
}
|
|
|
|
storm::solver::SmtSolver::CheckResult DFTASFChecker::checkTleFailsWithEq(uint64_t bound) {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
|
|
// Set backtracking marker to check several properties without reconstructing DFT encoding
|
|
solver->push();
|
|
// Constraint that toplevel element can fail with less or equal 'bound' failures
|
|
std::shared_ptr<SmtConstraint> tleFailedConstr = std::make_shared<IsConstantValue>(
|
|
timePointVariables.at(dft.getTopLevelIndex()), bound);
|
|
std::shared_ptr<storm::expressions::ExpressionManager> manager = solver->getManager().getSharedPointer();
|
|
solver->add(tleFailedConstr->toExpression(varNames, manager));
|
|
storm::solver::SmtSolver::CheckResult res = solver->check();
|
|
solver->pop();
|
|
return res;
|
|
}
|
|
|
|
storm::solver::SmtSolver::CheckResult DFTASFChecker::checkTleFailsWithLeq(uint64_t bound) {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
|
|
// Set backtracking marker to check several properties without reconstructing DFT encoding
|
|
solver->push();
|
|
// Constraint that toplevel element can fail with less or equal 'bound' failures
|
|
std::shared_ptr<SmtConstraint> tleNeverFailedConstr = std::make_shared<IsLessEqualConstant>(
|
|
timePointVariables.at(dft.getTopLevelIndex()), bound);
|
|
std::shared_ptr<storm::expressions::ExpressionManager> manager = solver->getManager().getSharedPointer();
|
|
solver->add(tleNeverFailedConstr->toExpression(varNames, manager));
|
|
storm::solver::SmtSolver::CheckResult res = solver->check();
|
|
solver->pop();
|
|
return res;
|
|
}
|
|
|
|
void DFTASFChecker::setSolverTimeout(uint_fast64_t milliseconds) {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, timeout cannot be set");
|
|
solver->setTimeout(milliseconds);
|
|
}
|
|
|
|
void DFTASFChecker::unsetSolverTimeout() {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, timeout cannot be unset");
|
|
solver->unsetTimeout();
|
|
}
|
|
|
|
storm::solver::SmtSolver::CheckResult DFTASFChecker::checkTleNeverFailed() {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
return checkTleFailsWithEq(notFailed);
|
|
}
|
|
|
|
storm::solver::SmtSolver::CheckResult
|
|
DFTASFChecker::checkFailsLeqWithEqNonMarkovianState(uint64_t checkbound, uint64_t nrNonMarkovian) {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
std::vector<uint64_t> markovianIndices;
|
|
checkbound = std::min<int>(checkbound, markovianVariables.size());
|
|
// Get Markovian variable indices up until given timepoint
|
|
for (uint64_t i = 0; i < checkbound; ++i) {
|
|
markovianIndices.push_back(markovianVariables.at(i));
|
|
}
|
|
// Set backtracking marker to check several properties without reconstructing DFT encoding
|
|
solver->push();
|
|
// Constraint that TLE fails before or during given timepoint
|
|
std::shared_ptr<SmtConstraint> tleFailedConstr = std::make_shared<IsLessEqualConstant>(
|
|
timePointVariables.at(dft.getTopLevelIndex()), checkbound);
|
|
std::shared_ptr<storm::expressions::ExpressionManager> manager = solver->getManager().getSharedPointer();
|
|
solver->add(tleFailedConstr->toExpression(varNames, manager));
|
|
// Constraint that a given number of non-Markovian states are visited
|
|
std::shared_ptr<SmtConstraint> nonMarkovianConstr = std::make_shared<FalseCountIsEqualConstant>(
|
|
markovianIndices, nrNonMarkovian);
|
|
solver->add(nonMarkovianConstr->toExpression(varNames, manager));
|
|
storm::solver::SmtSolver::CheckResult res = solver->check();
|
|
solver->pop();
|
|
return res;
|
|
}
|
|
|
|
storm::solver::SmtSolver::CheckResult
|
|
DFTASFChecker::checkFailsAtTimepointWithOnlyMarkovianState(uint64_t timepoint) {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
std::vector<uint64_t> markovianIndices;
|
|
// Get Markovian variable indices
|
|
for (uint64_t i = 0; i < timepoint; ++i) {
|
|
markovianIndices.push_back(markovianVariables.at(i));
|
|
}
|
|
// Set backtracking marker to check several properties without reconstructing DFT encoding
|
|
solver->push();
|
|
// Constraint that toplevel element can fail with less than 'checkNumber' Markovian states visited
|
|
std::shared_ptr<SmtConstraint> countConstr = std::make_shared<TrueCountIsConstantValue>(
|
|
markovianIndices, timepoint);
|
|
// Constraint that TLE fails at timepoint
|
|
std::shared_ptr<SmtConstraint> timepointConstr = std::make_shared<IsConstantValue>(
|
|
timePointVariables.at(dft.getTopLevelIndex()), timepoint);
|
|
std::shared_ptr<storm::expressions::ExpressionManager> manager = solver->getManager().getSharedPointer();
|
|
solver->add(countConstr->toExpression(varNames, manager));
|
|
solver->add(timepointConstr->toExpression(varNames, manager));
|
|
storm::solver::SmtSolver::CheckResult res = solver->check();
|
|
solver->pop();
|
|
return res;
|
|
}
|
|
|
|
storm::solver::SmtSolver::CheckResult
|
|
DFTASFChecker::checkDependencyConflict(uint64_t dep1Index, uint64_t dep2Index, uint64_t timeout) {
|
|
//TODO make constraints easier?
|
|
std::vector<std::shared_ptr<SmtConstraint>> andConstr;
|
|
std::vector<std::shared_ptr<SmtConstraint>> orConstr;
|
|
STORM_LOG_DEBUG(
|
|
"Check " << dft.getElement(dep1Index)->name() << " and " << dft.getElement(dep2Index)->name());
|
|
andConstr.clear();
|
|
// AND FDEP1 is triggered before FDEP2 is resolved
|
|
andConstr.push_back(std::make_shared<IsGreaterEqual>(
|
|
timePointVariables.at(dep1Index), timePointVariables.at(dep2Index)));
|
|
andConstr.push_back(std::make_shared<IsLess>(
|
|
timePointVariables.at(dep1Index), dependencyVariables.at(dep2Index)));
|
|
std::shared_ptr<SmtConstraint> betweenConstr1 = std::make_shared<And>(andConstr);
|
|
|
|
andConstr.clear();
|
|
// AND FDEP2 is triggered before FDEP1 is resolved
|
|
andConstr.push_back(std::make_shared<IsGreaterEqual>(
|
|
timePointVariables.at(dep2Index), timePointVariables.at(dep1Index)));
|
|
andConstr.push_back(std::make_shared<IsLess>(
|
|
timePointVariables.at(dep2Index), dependencyVariables.at(dep1Index)));
|
|
std::shared_ptr<SmtConstraint> betweenConstr2 = std::make_shared<And>(andConstr);
|
|
|
|
orConstr.clear();
|
|
// Either one of the above constraints holds
|
|
orConstr.push_back(betweenConstr1);
|
|
orConstr.push_back(betweenConstr2);
|
|
|
|
// Both FDEPs were triggered before dependent elements have failed
|
|
andConstr.clear();
|
|
andConstr.push_back(std::make_shared<IsLess>(
|
|
timePointVariables.at(dep1Index), dependencyVariables.at(dep1Index)));
|
|
andConstr.push_back(std::make_shared<IsLess>(
|
|
timePointVariables.at(dep2Index), dependencyVariables.at(dep2Index)));
|
|
andConstr.push_back(std::make_shared<Or>(orConstr));
|
|
|
|
std::shared_ptr<SmtConstraint> checkConstr = std::make_shared<And>(andConstr);
|
|
|
|
std::shared_ptr<storm::expressions::ExpressionManager> manager = solver->getManager().getSharedPointer();
|
|
solver->push();
|
|
solver->add(checkConstr->toExpression(varNames, manager));
|
|
setSolverTimeout(timeout * 1000);
|
|
storm::solver::SmtSolver::CheckResult res = solver->check();
|
|
unsetSolverTimeout();
|
|
solver->pop();
|
|
return res;
|
|
}
|
|
|
|
uint64_t DFTASFChecker::correctLowerBound(uint64_t bound, uint_fast64_t timeout) {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
STORM_LOG_DEBUG("Lower bound correction - try to correct bound " << std::to_string(bound));
|
|
uint64_t boundCandidate = bound;
|
|
uint64_t nrDepEvents = 0;
|
|
uint64_t nrNonMarkovian = 0;
|
|
// Count dependent events
|
|
for (size_t i = 0; i < dft.nrElements(); ++i) {
|
|
std::shared_ptr<storm::storage::DFTElement<ValueType> const> element = dft.getElement(i);
|
|
if (element->type() == storm::storage::DFTElementType::BE_EXP ||
|
|
element->type() == storm::storage::DFTElementType::BE_CONST) {
|
|
auto be = std::static_pointer_cast<storm::storage::DFTBE<double> const>(element);
|
|
if (be->hasIngoingDependencies()) {
|
|
++nrDepEvents;
|
|
}
|
|
}
|
|
}
|
|
// Only need to check as long as bound candidate + nr of non-Markovians to check is smaller than number of dependent events
|
|
while (nrNonMarkovian <= nrDepEvents && boundCandidate > 0) {
|
|
STORM_LOG_TRACE(
|
|
"Lower bound correction - check possible bound " << std::to_string(boundCandidate) << " with "
|
|
<< std::to_string(nrNonMarkovian)
|
|
<< " non-Markovian states");
|
|
setSolverTimeout(timeout * 1000);
|
|
storm::solver::SmtSolver::CheckResult tmp_res =
|
|
checkFailsLeqWithEqNonMarkovianState(boundCandidate + nrNonMarkovian, nrNonMarkovian);
|
|
unsetSolverTimeout();
|
|
switch (tmp_res) {
|
|
case storm::solver::SmtSolver::CheckResult::Sat:
|
|
/* If SAT, there is a sequence where only boundCandidate-many BEs fail directly and rest is nonMarkovian.
|
|
* Bound candidate is vaild, therefore check the next one */
|
|
STORM_LOG_TRACE("Lower bound correction - SAT");
|
|
--boundCandidate;
|
|
break;
|
|
case storm::solver::SmtSolver::CheckResult::Unknown:
|
|
// If any query returns unknown, we cannot be sure about the bound and fall back to the naive one
|
|
STORM_LOG_DEBUG("Lower bound correction - Solver returned 'Unknown', corrected to 1");
|
|
return 1;
|
|
default:
|
|
// if query is UNSAT, increase number of non-Markovian states and try again
|
|
STORM_LOG_TRACE("Lower bound correction - UNSAT");
|
|
++nrNonMarkovian;
|
|
break;
|
|
}
|
|
}
|
|
// if for one candidate all queries are UNSAT, it is not valid. Return last valid candidate
|
|
STORM_LOG_DEBUG("Lower bound correction - corrected bound to " << std::to_string(boundCandidate + 1));
|
|
return boundCandidate + 1;
|
|
}
|
|
|
|
uint64_t DFTASFChecker::correctUpperBound(uint64_t bound, uint_fast64_t timeout) {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
STORM_LOG_DEBUG("Upper bound correction - try to correct bound " << std::to_string(bound));
|
|
|
|
while (bound > 1) {
|
|
setSolverTimeout(timeout * 1000);
|
|
storm::solver::SmtSolver::CheckResult tmp_res =
|
|
checkFailsAtTimepointWithOnlyMarkovianState(bound);
|
|
unsetSolverTimeout();
|
|
switch (tmp_res) {
|
|
case storm::solver::SmtSolver::CheckResult::Sat:
|
|
STORM_LOG_DEBUG("Upper bound correction - corrected bound to " << std::to_string(bound));
|
|
return bound;
|
|
case storm::solver::SmtSolver::CheckResult::Unknown:
|
|
STORM_LOG_DEBUG("Upper bound correction - Solver returned 'Unknown', corrected to ");
|
|
return bound;
|
|
default:
|
|
--bound;
|
|
break;
|
|
|
|
}
|
|
}
|
|
STORM_LOG_DEBUG("Upper bound correction - corrected bound to " << std::to_string(bound));
|
|
return bound;
|
|
}
|
|
|
|
uint64_t DFTASFChecker::getLeastFailureBound(uint_fast64_t timeout) {
|
|
STORM_LOG_TRACE("Compute lower bound for number of BE failures necessary for the DFT to fail");
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
uint64_t bound = 0;
|
|
while (bound < notFailed) {
|
|
setSolverTimeout(timeout * 1000);
|
|
storm::solver::SmtSolver::CheckResult tmp_res = checkTleFailsWithLeq(bound);
|
|
unsetSolverTimeout();
|
|
switch (tmp_res) {
|
|
case storm::solver::SmtSolver::CheckResult::Sat:
|
|
if (!dft.getDependencies().empty()) {
|
|
return correctLowerBound(bound, timeout);
|
|
} else {
|
|
return bound;
|
|
}
|
|
case storm::solver::SmtSolver::CheckResult::Unknown:
|
|
STORM_LOG_DEBUG("Lower bound: Solver returned 'Unknown'");
|
|
return bound;
|
|
default:
|
|
++bound;
|
|
break;
|
|
}
|
|
|
|
}
|
|
return bound;
|
|
}
|
|
|
|
uint64_t DFTASFChecker::getAlwaysFailedBound(uint_fast64_t timeout) {
|
|
STORM_LOG_TRACE("Compute bound for number of BE failures such that the DFT always fails");
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
if (checkTleNeverFailed() == storm::solver::SmtSolver::CheckResult::Sat) {
|
|
return notFailed;
|
|
}
|
|
uint64_t bound = notFailed - 1;
|
|
while (bound >= 0) {
|
|
setSolverTimeout(timeout * 1000);
|
|
storm::solver::SmtSolver::CheckResult tmp_res = checkTleFailsWithEq(bound);
|
|
unsetSolverTimeout();
|
|
switch (tmp_res) {
|
|
case storm::solver::SmtSolver::CheckResult::Sat:
|
|
if (!dft.getDependencies().empty()) {
|
|
return correctUpperBound(bound, timeout);
|
|
} else {
|
|
return bound;
|
|
}
|
|
case storm::solver::SmtSolver::CheckResult::Unknown:
|
|
STORM_LOG_DEBUG("Upper bound: Solver returned 'Unknown'");
|
|
return bound;
|
|
default:
|
|
--bound;
|
|
break;
|
|
}
|
|
}
|
|
return bound;
|
|
}
|
|
|
|
std::vector<std::pair<uint64_t, uint64_t>> DFTASFChecker::getDependencyConflicts(uint_fast64_t timeout) {
|
|
STORM_LOG_ASSERT(solver, "SMT Solver was not initialized, call toSolver() before checking queries");
|
|
std::vector<std::pair<uint64_t, uint64_t>> res;
|
|
uint64_t dep1Index;
|
|
uint64_t dep2Index;
|
|
for (size_t i = 0; i < dft.getDependencies().size(); ++i) {
|
|
dep1Index = dft.getDependencies().at(i);
|
|
for (size_t j = i + 1; j < dft.getDependencies().size(); ++j) {
|
|
dep2Index = dft.getDependencies().at(j);
|
|
if (dft.getDependency(dep1Index)->triggerEvent() == dft.getDependency(dep2Index)->triggerEvent()) {
|
|
STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and "
|
|
<< dft.getElement(dep2Index)->name() << ": Same trigger");
|
|
res.emplace_back(std::pair<uint64_t, uint64_t>(dep1Index, dep2Index));
|
|
} else {
|
|
switch (checkDependencyConflict(dep1Index, dep2Index, timeout)) {
|
|
case storm::solver::SmtSolver::CheckResult::Sat:
|
|
STORM_LOG_DEBUG("Conflict between " << dft.getElement(dep1Index)->name() << " and "
|
|
<< dft.getElement(dep2Index)->name());
|
|
res.emplace_back(std::pair<uint64_t, uint64_t>(dep1Index, dep2Index));
|
|
break;
|
|
case storm::solver::SmtSolver::CheckResult::Unknown:
|
|
STORM_LOG_DEBUG(
|
|
"Unknown: Conflict between " << dft.getElement(dep1Index)->name() << " and "
|
|
<< dft.getElement(dep2Index)->name());
|
|
res.emplace_back(std::pair<uint64_t, uint64_t>(dep1Index, dep2Index));
|
|
break;
|
|
default:
|
|
STORM_LOG_DEBUG("No conflict between " << dft.getElement(dep1Index)->name() << " and "
|
|
<< dft.getElement(dep2Index)->name());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
}
|