packages feed

hanalyze-0.2.0.0: src/Hanalyze/Model/ReliabilityBlockDiagram.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE BangPatterns #-}

-- |
-- Module      : Hanalyze.Model.ReliabilityBlockDiagram
-- Description : 信頼性ブロック図 (RBD) の直列/並列/k-out-of-n 再帰合成による系全体信頼度計算
-- Copyright   : (c) 2026 Aelysce Project (Toshiaki Honda)
-- License     : BSD-3-Clause
--
-- Reliability Block Diagram (RBD).
--
-- Computes system reliability from a structural composition of components
-- with known individual reliabilities. The three primitive combinators
-- are the textbook ones (e.g. O'Connor & Kleyner, /Practical Reliability
-- Engineering/):
--
--   * Series (every block must work):
--       @R = ∏ Rᵢ@
--   * Parallel (any block working suffices):
--       @R = 1 − ∏ (1 − Rᵢ)@
--   * k-out-of-n (at least @k@ of @n@ blocks must work):
--       @R = Σ_{i = k}^{n} P(exactly i succeed)@
--       computed by Poisson-binomial DP — works with heterogeneous block
--       reliabilities (the binomial closed form is the homogeneous
--       special case).
--
-- Blocks can be arbitrarily nested. Failure independence between blocks
-- is assumed (the standard RBD assumption).
--
-- @
-- import Hanalyze.Model.ReliabilityBlockDiagram
--
-- -- Two-out-of-three redundancy of three series strings:
-- let sys = KofN 2 [ Series [Leaf 0.95, Leaf 0.99]
--                  , Series [Leaf 0.95, Leaf 0.99]
--                  , Series [Leaf 0.95, Leaf 0.99] ]
--     r   = reliabilityOf sys
-- @
--
-- == Implemented
--
--   * 'RBDBlock' — composable tree of components.
--   * 'reliabilityOf' — recursive evaluation.
module Hanalyze.Model.ReliabilityBlockDiagram
  ( RBDBlock (..)
  , reliabilityOf
  ) where

-- ---------------------------------------------------------------------------
-- Types
-- ---------------------------------------------------------------------------

-- | A block in a reliability diagram. @Leaf p@ is a single component with
-- reliability @p ∈ [0, 1]@; the other constructors compose sub-blocks.
data RBDBlock
  = Leaf     !Double
  | Series   ![RBDBlock]
  | Parallel ![RBDBlock]
  | KofN     !Int ![RBDBlock]
  deriving (Show, Eq)

-- ---------------------------------------------------------------------------
-- Evaluation
-- ---------------------------------------------------------------------------

-- | System reliability of a block, in @[0, 1]@. Component reliabilities
-- are assumed independent (the standard RBD assumption).
reliabilityOf :: RBDBlock -> Double
reliabilityOf (Leaf p)         = p
reliabilityOf (Series bs)      = product (map reliabilityOf bs)
reliabilityOf (Parallel bs)    = 1 - product [ 1 - reliabilityOf b | b <- bs ]
reliabilityOf (KofN k bs)
  | k <= 0           = 1                   -- always satisfied
  | k > length bs    = 0                   -- impossible
  | otherwise        =
      let ps     = map reliabilityOf bs
          n      = length ps
          -- Poisson-binomial DP: pmf!!i = P(exactly i blocks work).
          pmf    = foldr step [1.0] ps
            where
              step pi acc =
                -- acc = pmf of current partial product (length = current j + 1).
                let len = length acc
                in [ let aPrev = if i - 1 >= 0    then acc !! (i - 1) else 0
                         aHere = if i     <  len then acc !! i         else 0
                     in pi * aPrev + (1 - pi) * aHere
                   | i <- [0 .. len] ]
      in sum (drop k pmf)