Docs / COP API Reference

COP API Reference

COP (Controllability Observability Program) - Probabilistic testability analysis using Bayesian networks for reconvergent fanout correlation modeling.

Overview

The COP module provides probability-based testability metrics:

Main Entry Point

run_cop()

Main function for COP analysis with integrated reconvergence detection.

from opentestability.core.cop import run_cop

run_cop(
    input_filename,
    output_filename,
    json_flag=False,
    reconvergence_algorithm='auto',
    reconvergence_data=None,
    auto_parallel=True,
    max_workers=4
)

Parameters:

Returns:

Example:

from opentestability.core.cop import run_cop

# Basic usage
run_cop('circuit.txt', 'cop_results.txt')

# With reconvergence and JSON output
run_cop('circuit.txt', 'cop_results.txt',
        json_flag=True,
        reconvergence_algorithm='simple')

# Large circuit with parallel processing
run_cop('large_design.txt', 'results.txt',
        reconvergence_algorithm='auto',
        auto_parallel=True,
        max_workers=8)

Controllability Module

build_controllability()

Compute C1 (probability of signal being 1) for all signals using forward propagation.

from opentestability.core.cop.controllability import build_controllability

C1 = build_controllability(nets, primary_inputs, gates, max_iterations=10000)

Parameters:

Returns:

Algorithm:

  1. Initialize primary inputs to C1 = 0.5 (random test patterns)
  2. Initialize other signals to None (uncomputed)
  3. Iteratively compute gate outputs when inputs are ready
  4. Continue until convergence or max iterations reached

Example:

nets = ['a', 'b', 'n1', 'z']
inputs = ['a', 'b']
gates = [
    ('AND2X1', 'n1', ['a', 'b']),
    ('INVX1', 'z', ['n1'])
]

C1 = build_controllability(nets, inputs, gates)
print(f"Signal z has C1 = {C1['z']:.4f}")

get_controllability_metrics()

Package controllability metrics for output.

from opentestability.core.cop.controllability import get_controllability_metrics

metrics = get_controllability_metrics(C1)

Returns:

{
    'signal_name': {
        'c1': 0.5,  # Probability signal is 1
        'c0': 0.5   # Probability signal is 0
    }
}

Gate Logic Module

Probabilistic transfer functions for various gate types.

calculate_gate_c1()

Main dispatcher for calculating C1 of any gate type.

from opentestability.core.cop.gate_logic import calculate_gate_c1

output_c1 = calculate_gate_c1(gate_type, input_c1_list)

Parameters:

Returns:

Supported Gates:

Example:

# 2-input AND gate with inputs at 0.5 probability
c1_out = calculate_gate_c1('AND2X1', [0.5, 0.5])
# Result: 0.25 (0.5 × 0.5)

Individual Gate Functions

calculate_inv_c1()

Inverter: out.C1 = 1.0 - in.C1

c1_out = calculate_inv_c1(input_c1)

calculate_and_c1()

AND gate: out.C1 = ∏(input.C1)

c1_out = calculate_and_c1([0.5, 0.5, 0.5])
# Result: 0.125

calculate_or_c1()

OR gate: out.C1 = 1.0 - ∏(1.0 - input.C1)

c1_out = calculate_or_c1([0.5, 0.5])
# Result: 0.75

calculate_xor_c1()

XOR gate: out.C1 = (a.C1 × (1-b.C1)) + ((1-a.C1) × b.C1)

c1_out = calculate_xor_c1([0.5, 0.5])
# Result: 0.5

Observability Module

build_observability()

Compute observability for all signals using backward propagation.

from opentestability.core.cop.observability import build_observability

Obs = build_observability(
    nets,
    primary_outputs,
    gates,
    C1,
    reconvergent_sites=None,
    max_iterations=10000
)

Parameters:

Returns:

Algorithm:

  1. Initialize primary outputs to Obs = 1.0 (fully observable)
  2. Initialize other signals to 0.0
  3. Iterate backward through gates, computing input observabilities
  4. For fanouts, combine branches using union formula (if independent)
  5. Mark reconvergent sites for Bayesian processing

Example:

Obs = build_observability(nets, outputs, gates, C1)
print(f"Signal 'a' observability: {Obs['a']:.4f}")

calculate_branch_observability()

Calculate observability of a signal through a specific gate branch.

from opentestability.core.cop.observability import calculate_branch_observability

branch_obs = calculate_branch_observability(
    signal, gate_type, gate_output, gate_inputs, output_obs, C1
)

Logic by Gate Type:

combine_fanout_observabilities()

Combine observabilities from multiple fanout branches.

from opentestability.core.cop.observability import combine_fanout_observabilities

combined_obs = combine_fanout_observabilities(branch_obs_list)

Formula: For independent branches: Obs = 1 - ∏(1 - Obs_branch)

This represents probability that signal is observable through at least one branch.


Reconvergence Module

Bayesian network-based reconvergence handling with correlation modeling.

extract_reconvergent_sites()

Extract set of reconvergent site signals from reconvergence data.

from opentestability.core.cop.reconvergence import extract_reconvergent_sites

sites = extract_reconvergent_sites(reconvergence_data)

Returns:

adjust_cop_for_reconvergence()

Adjust COP observability values for reconvergent sites using Bayesian correlation.

from opentestability.core.cop.reconvergence import adjust_cop_for_reconvergence

adjusted_obs = adjust_cop_for_reconvergence(Obs, C1, reconvergence_data, gates)

Parameters:

Returns:

How It Works:

  1. Identifies reconvergent fanouts from data
  2. Builds Bayesian network for reconvergent paths
  3. Calculates correlation coefficients
  4. Applies correlation-based adjustments instead of independence assumption

BayesianNetwork Class

Simplified Bayesian network for modeling signal dependencies.

from opentestability.core.cop.reconvergence import BayesianNetwork

bn = BayesianNetwork()
bn.build_from_paths(path1, path2)
correlation = bn.calculate_correlation(path1, path2)

Methods:

add_dependency()

bn.add_dependency(parent_signal, child_signal)

build_from_paths()

bn.build_from_paths(path1, path2)

calculate_correlation()

correlation = bn.calculate_correlation(path1, path2)

Returns correlation coefficient in [0, 1] based on shared ancestor signals.

calculate_correlated_observability()

Calculate observability for reconvergent site using correlation.

from opentestability.core.cop.reconvergence import calculate_correlated_observability

adjusted_obs = calculate_correlated_observability(site, branch_obs_list, correlation)

Formula:


Vectorized Engine

The runtime COP computation uses a NumPy-vectorized engine rather than the pure-Python fixpoint loop.

from opentestability.core.cop.vectorized import build_cop_vectorized

C1, Obs = build_cop_vectorized(nets, inputs, outputs, gates)

How it works:

  1. Builds a CircuitIndex (integer-indexed CSR graph) once.
  2. Forward C1 (forward_c1): single topological pass; gate outputs computed by gate type using np.multiply.reduceat over the CSR fan-in.
  3. Backward Obs (backward_obs): iterative union fixpoint; per-gate side-input blocking products are precomputed once (they depend only on C1, which is fixed) and reused across iterations.

This is the path called by run_cop() and main.py. The pure-Python build_controllability / build_observability functions remain in the codebase as reference oracles for testing only.

Convergence note: The vectorized backward pass uses a Jacobi update (whole-array update per iteration) while the reference uses Gauss-Seidel (in-place). On ultra-stiff feedback circuits with many flip-flops, a small number of signals may converge to slightly different values at the shared iteration cap. Rank correlation is ≥ 0.9998 in practice; TPI rankings are unaffected.


Output Module

Output formatting for COP results.

write_cop()

Write COP results to text file.

from opentestability.core.cop.output import write_cop

write_cop(C1, Obs, filename)

Output Format:

============================================================
COP (Controllability Observability Program) Analysis
============================================================

--- CONTROLLABILITY PROBABILITIES (C1) ---
C1 = Probability that signal is 1
C0 = 1 - C1 (Probability that signal is 0)

signal_a              C1: 0.500000  C0: 0.500000
signal_b              C1: 0.500000  C0: 0.500000
...

--- OBSERVABILITY PROBABILITIES (Obs) ---
Obs = Probability that signal fault is observable at outputs

signal_a              Obs: 0.750000
signal_b              Obs: 0.750000
...

--- TESTABILITY METRICS ---
Testability = C1 * Obs (probability of setting and observing)

signal_a              Testability: 0.375000
signal_b              Testability: 0.375000
...

dump_json()

Write COP results to JSON file.

from opentestability.core.cop.output import dump_json

dump_json(C1, Obs, inputs, outputs, gates, filename)

JSON Schema:

{
  "analysis_type": "COP",
  "description": "Controllability Observability Program Analysis",
  "primary_inputs": ["a", "b"],
  "primary_outputs": ["z"],
  "metrics": [
    {
      "gate": "AND2X1_0",
      "output": "n1",
      "inputs": ["a", "b"],
      "c1": 0.25,
      "c0": 0.75,
      "obs": 1.0,
      "testability": 0.25
    }
  ]
}

format_cop_summary()

Generate summary of COP results for display.

from opentestability.core.cop.output import format_cop_summary

summary = format_cop_summary(C1, Obs, primary_inputs, primary_outputs)
print(summary)

Output:

============================================================
COP Analysis Summary
============================================================

Primary Inputs (2):
  a                     C1: 0.5000
  b                     C1: 0.5000

Primary Outputs (1):
  z                     Obs: 1.0000  Testability: 0.7500

Average C1: 0.5625
Average Observability: 0.8750

Testability Statistics:
  Average: 0.4531
  Minimum: 0.1250
  Maximum: 0.7500
============================================================

Complete Example

from opentestability.core.cop import run_cop
from opentestability.core.cop.vectorized import build_cop_vectorized
from opentestability.core.cop.reconvergence import adjust_cop_for_reconvergence
from opentestability.core.cop.output import write_cop, dump_json

# Method 1: Simple high-level API (recommended)
run_cop('circuit.txt', 'cop_results.txt',
        json_flag=True,
        reconvergence_algorithm='simple')

# Method 2: Vectorized engine directly
# (Assuming you have nets, inputs, outputs, gates from netlist parser)
C1, Obs = build_cop_vectorized(nets, inputs, outputs, gates)

# Apply reconvergence adjustments (if data available)
if reconvergence_data:
    Obs = adjust_cop_for_reconvergence(Obs, C1, reconvergence_data, gates)

# Write outputs
write_cop(C1, Obs, 'results.txt')
dump_json(C1, Obs, inputs, outputs, gates, 'results.json')

Performance Considerations

Observed timings (picorv32a, ~14 k gates)

Engine Time
Pure-Python reference (observability) ~160 s
NumPy vectorized (current runtime) ~3 s

The vectorized engine scales to hundreds of thousands of gates without further changes. At SoC scale (~1 M gates), the dominant cost shifts to CircuitIndex construction and dict output — the NumPy computation itself remains fast.

Memory usage

Optimization tips

  1. Use reconvergence_algorithm='simple' for best speed/accuracy tradeoff.
  2. Enable JSON output (json_flag=True) only when downstream tools need it (adds I/O time).
  3. For very large netlists where CircuitIndex build time dominates, consider pre-building the index once and reusing it across multiple analysis passes.

Additional Resources