Skip to content

[Model] FaultDetectionInLogicCircuits #934

@isPANN

Description

@isPANN

Motivation

FAULT DETECTION IN LOGIC CIRCUITS (MS17) from Garey & Johnson, A1.2. Given a combinational logic circuit (a DAG of AND/OR/NOT gates) and a subset V' of vertices (lines), determine whether all single stuck-at faults on lines in V' can be detected by some input test pattern. NP-complete even if V' = V (all lines) or V' consists of a single input vertex (Ibarra and Sahni 1975). Applications in VLSI testing, design-for-testability, and automatic test pattern generation (ATPG).

Associated rules:

Definition

Name: FaultDetectionInLogicCircuits
Reference: Garey & Johnson, Computers and Intractability, A1.2 MS17; Ibarra and Sahni 1975

Mathematical definition:

INSTANCE: A combinational logic circuit C (a directed acyclic graph where vertices represent gates — AND, OR, NOT — and edges represent signal lines), a subset V' ⊆ V of vertices (lines).
QUESTION: Can all single stuck-at faults (stuck-at-0 and stuck-at-1) on lines in V' be detected? A fault is detectable if there exists an input pattern such that the output of the faulty circuit differs from the output of the fault-free circuit.

Variables

  • Count: n_inputs (number of primary inputs)
  • Per-variable domain: {0, 1}
  • Meaning: Each variable represents a primary input value. The problem asks whether for every fault site v in V' and every stuck-at type (0 and 1), there exists an input assignment that detects the fault (produces a different primary output than the fault-free circuit). This is a feasibility problem with Value = Or.

Schema (data type)

Type name: FaultDetectionInLogicCircuits
Variants: none (unique input structure)

Field Type Description
num_inputs usize Number of primary input vertices
gates Vec<Gate> List of gates, each with type (AND/OR/NOT) and input wire indices
outputs Vec<usize> Indices of primary output gates
fault_vertices Vec<usize> Subset V' of vertices where faults must be detectable

where Gate has fields: gate_type: GateType (enum AND/OR/NOT), inputs: Vec<usize> (indices of input wires).

Notes:

  • Indexing convention: primary inputs are indexed 0..num_inputs, gates are indexed num_inputs..num_inputs + gates.len().
  • Key getter methods: num_inputs() (= number of primary inputs), num_gates() (= gates.len()), num_fault_vertices() (= fault_vertices.len()).
  • dims() = vec![2; num_inputs] — one binary variable per primary input.

Complexity

  • Decision complexity: NP-complete (Ibarra and Sahni, 1975; transformation from 3-Satisfiability per Garey & Johnson). NP-complete even if V' = V or V' = {single input vertex}.
  • Best known exact algorithm: O*(2^n_inputs) by enumerating all input patterns and checking detectability of each fault. In practice, ATPG tools use structural heuristics (D-algorithm, PODEM, FAN) that are efficient for most practical circuits but worst-case exponential.
  • Concrete complexity expression: "2^num_inputs" (for declare_variants!)
  • References:
    • O. H. Ibarra and S. Sahni (1975). "Polynomially complete fault detection problems." IEEE Transactions on Computers C-24(3), pp. 242-249.

Extra Remark

Full book text:

INSTANCE: Logic circuit C (a dag of AND, OR, NOT gates), subset V' ⊆ V of vertices of C.
QUESTION: Can all "single stuck-at faults" on lines corresponding to members of V' be "detected"?

Reference: [Ibarra and Sahni, 1975]. Transformation from 3-SATISFIABILITY.
Comment: NP-complete even if V' = V or V' is a single input vertex.

How to solve

  • It can be solved by (existing) bruteforce.
  • It can be solved by reducing to integer programming.
  • Other:

Note: Bruteforce: for each fault site v in V' and each fault type (stuck-at-0, stuck-at-1), enumerate all 2^n_inputs input patterns to find one that produces different output. If any fault is undetectable (no input distinguishes it), answer NO. This is O(|V'| * 2^n_inputs * circuit_size).

Example Instance

Positive instance (YES):

Circuit encoding the 3SAT formula (x₀ ∨ ¬x₁ ∨ x₂) ∧ (¬x₀ ∨ x₁ ∨ ¬x₂):

  • 3 primary inputs: x₀ (idx 0), x₁ (idx 1), x₂ (idx 2)
  • Gate g₀ (idx 3) = NOT x₁
  • Gate g₁ (idx 4) = NOT x₀
  • Gate g₂ (idx 5) = NOT x₂
  • Gate g₃ (idx 6) = OR(x₀, g₀, x₂) — clause C₁
  • Gate g₄ (idx 7) = OR(g₁, x₁, g₂) — clause C₂
  • Gate g₅ (idx 8) = AND(g₃, g₄) — output
  • Outputs: [8]
  • V' = {8} (stuck-at-0 on the output gate)

The formula is satisfiable (e.g., x₀=1, x₁=1, x₂=0), so the circuit output can be 1, meaning stuck-at-0 on the output is detectable. Stuck-at-1 is also detectable (any input making the output 0 detects it, e.g., x₀=0, x₁=0, x₂=0 gives C₁ = 0 ∨ 1 ∨ 0 = 1, C₂ = 1 ∨ 0 ∨ 1 = 1 → output 1; but x₀=0, x₁=1, x₂=1 gives C₁ = 0 ∨ 0 ∨ 1 = 1, C₂ = 1 ∨ 1 ∨ 0 = 1 → output 1. Actually trying x₀=0, x₁=0, x₂=1: C₁ = 0 ∨ 1 ∨ 1 = 1, C₂ = 1 ∨ 0 ∨ 0 = 1 → output 1. The formula is a tautology-like formula. Let's check x₀=1, x₁=0, x₂=1: C₁ = 1 ∨ 1 ∨ 1 = 1, C₂ = 0 ∨ 0 ∨ 0 = 0 → output 0. So input (1,0,1) makes the output 0, detecting stuck-at-1.)

Expected Outcome: YES — all stuck-at faults on V' = {8} are detectable.

  • Stuck-at-0 detected by input (1, 1, 0): fault-free output = 1, faulty output = 0.
  • Stuck-at-1 detected by input (1, 0, 1): fault-free output = 0, faulty output = 1.

Negative instance (NO):

Circuit encoding (x₀) ∧ (¬x₀) — unsatisfiable:

  • 1 primary input: x₀ (idx 0)
  • Gate g₀ (idx 1) = NOT x₀
  • Gate g₁ (idx 2) = AND(x₀, g₀) — output (always 0)
  • Outputs: [2]
  • V' = {2} (stuck-at-0 on the output gate)

The output is always 0 regardless of input. Stuck-at-0 on the output produces the same value (0) for every input, so it is undetectable.

Expected Outcome: NO — stuck-at-0 fault on line 2 is undetectable.

Python validation script
"""Validate FaultDetectionInLogicCircuits examples."""
from itertools import product

AND, OR, NOT = "AND", "OR", "NOT"

def check_fault_detection(num_inputs, gates, outputs, fault_vertices):
    """Check if all single stuck-at faults on fault_vertices are detectable."""
    all_inputs = list(product([0, 1], repeat=num_inputs))

    for fv in fault_vertices:
        for stuck_val in [0, 1]:
            detected = False
            for inp in all_inputs:
                # Fault-free evaluation
                ff = list(inp)
                for gt, gi in gates:
                    r = [ff[i] for i in gi]
                    if gt == NOT: ff.append(1 - r[0])
                    elif gt == AND: ff.append(1 if all(r) else 0)
                    elif gt == OR: ff.append(1 if any(r) else 0)
                ff_out = tuple(ff[o] for o in outputs)

                # Faulty evaluation
                if fv < num_inputs:
                    fa = list(inp); fa[fv] = stuck_val
                    for gt, gi in gates:
                        r = [fa[i] for i in gi]
                        if gt == NOT: fa.append(1 - r[0])
                        elif gt == AND: fa.append(1 if all(r) else 0)
                        elif gt == OR: fa.append(1 if any(r) else 0)
                else:
                    fa = list(inp)
                    for idx, (gt, gi) in enumerate(gates):
                        r = [stuck_val if i == fv else fa[i] for i in gi]
                        line = num_inputs + idx
                        if line == fv:
                            fa.append(stuck_val)
                        elif gt == NOT: fa.append(1 - r[0])
                        elif gt == AND: fa.append(1 if all(r) else 0)
                        elif gt == OR: fa.append(1 if any(r) else 0)
                fa_out = tuple(fa[o] for o in outputs)

                if ff_out != fa_out:
                    detected = True
                    break
            if not detected:
                return False, fv, stuck_val
    return True, None, None

# Example 1: Positive (YES)
# (x0 OR NOT x1 OR x2) AND (NOT x0 OR x1 OR NOT x2)
gates1 = [
    (NOT, [1]),       # idx 3: NOT x1
    (NOT, [0]),       # idx 4: NOT x0
    (NOT, [2]),       # idx 5: NOT x2
    (OR, [0, 3, 2]),  # idx 6: clause C1
    (OR, [4, 1, 5]),  # idx 7: clause C2
    (AND, [6, 7]),    # idx 8: output
]
r1, _, _ = check_fault_detection(3, gates1, [8], [8])
assert r1, "Expected YES"
print(f"Example 1 (positive): {'YES' if r1 else 'NO'}")

# Example 2: Negative (NO)
# (x0) AND (NOT x0) — always 0
gates2 = [
    (NOT, [0]),       # idx 1: NOT x0
    (AND, [0, 1]),    # idx 2: output (always 0)
]
r2, fv, sv = check_fault_detection(1, gates2, [2], [2])
assert not r2, "Expected NO"
print(f"Example 2 (negative): {'YES' if r2 else 'NO'} (undetectable: line {fv}, stuck-at-{sv})")

print("All assertions passed!")

References

  • [Ibarra and Sahni, 1975]: O. H. Ibarra and S. Sahni (1975). "Polynomially complete fault detection problems." IEEE Transactions on Computers C-24(3), pp. 242-249.
  • [Garey and Johnson, 1979]: M. R. Garey and D. S. Johnson (1979). Computers and Intractability: A Guide to the Theory of NP-Completeness. W. H. Freeman, pp. 227 (MS17).

Metadata

Metadata

Assignees

No one assigned

    Labels

    GoodAn issue passed all checks.modelA model problem to be implemented.

    Type

    No type

    Projects

    Status

    Ready

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions