Skip to content

packages/engine/scram-node/src/substitution.cc

Implementation of Substitution members.

Namespaces

Name
scram
scram::mef

Source code

cpp
/*
 * Copyright (C) 2014-2018 Olzhas Rakhimov
 * Copyright (C) 2023 OpenPRA ORG Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "substitution.h"

#include "error.h"
#include "ext/algorithm.h"

namespace scram::mef {

void Substitution::Add(BasicEvent* source_event) {
  if (ext::any_of(source_, [source_event](BasicEvent* arg) {
        return arg->id() == source_event->id();
      })) {
    SCRAM_THROW(DuplicateElementError())
        << errinfo_element(source_event->id(), "source event");
  }
  source_.push_back(source_event);
}

void Substitution::Validate() const {
  assert(hypothesis_ && "Missing substitution hypothesis.");
  if (ext::any_of(hypothesis_->args(), [](const Formula::Arg& arg) {
        return !std::holds_alternative<BasicEvent*>(arg.event);
      })) {
    SCRAM_THROW(ValidityError(
        "Substitution hypothesis must be built over basic events only."))
        << errinfo_element(Element::name(), kTypeString);
  }

  if (ext::any_of(hypothesis_->args(),
                  [](const Formula::Arg& arg) { return arg.complement; })) {
    SCRAM_THROW(ValidityError("Substitution hypotheses must be coherent."))
        << errinfo_element(Element::name(), kTypeString);
  }

  if (declarative()) {
    switch (hypothesis_->connective()) {
      case kNull:
      case kAnd:
      case kAtleast:
      case kOr:
        break;
      default:
        SCRAM_THROW(ValidityError("Substitution hypotheses must be coherent."))
            << errinfo_element(Element::name(), kTypeString)
            << errinfo_connective(
                   kConnectiveToString[hypothesis_->connective()]);
    }
    const bool* constant = std::get_if<bool>(&target_);
    if (constant && *constant)
      SCRAM_THROW(ValidityError("Substitution has no effect."))
          << errinfo_element(Element::name(), kTypeString);
  } else {  // Non-declarative.
    switch (hypothesis_->connective()) {
      case kNull:
      case kAnd:
      case kOr:
        break;
      default:
        SCRAM_THROW(
            ValidityError("Non-declarative substitution hypotheses only allow "
                          "AND/OR/NULL connectives."))
            << errinfo_element(Element::name(), kTypeString)
            << errinfo_connective(
                   kConnectiveToString[hypothesis_->connective()]);
    }
    const bool* constant = std::get_if<bool>(&target_);
    if (constant && !*constant)
      SCRAM_THROW(ValidityError("Substitution source set is irrelevant."))
          << errinfo_element(Element::name(), kTypeString);
  }
}

std::optional<Substitution::Type> Substitution::type() const {
  auto in_hypothesis = [this](const BasicEvent* source_arg) {
    return ext::any_of(hypothesis_->args(),
                       [source_arg](const Formula::Arg& arg) {
                         return std::get<BasicEvent*>(arg.event) == source_arg;
                       });
  };

  auto is_mutually_exclusive = [](const Formula& formula) {
    switch (formula.connective()) {
      case kAtleast:
        return formula.min_number() == 2;
      case kAnd:
        return formula.args().size() == 2;
      default:
        return false;
    }
  };

  if (source_.empty()) {
    if (const bool* constant = std::get_if<bool>(&target_)) {
      assert(!*constant && "Substitution has no effect.");
      if (is_mutually_exclusive(*hypothesis_))
        return kDeleteTerms;
    } else if (std::holds_alternative<BasicEvent*>(target_)) {
      if (hypothesis_->connective() == kAnd)
        return kRecoveryRule;
    }
    return {};
  }
  if (!std::holds_alternative<BasicEvent*>(target_))
    return {};
  if (hypothesis_->connective() != kAnd && hypothesis_->connective() != kNull)
    return {};

  if (source_.size() == hypothesis_->args().size()) {
    if (ext::all_of(source_, in_hypothesis))
      return kRecoveryRule;
  } else if (source_.size() == 1) {
    if (in_hypothesis(source_.front()))
      return kExchangeEvent;
  }
  return {};
}

}  // namespace scram::mef

Updated on 2025-11-11 at 16:51:09 +0000