"""Some utility functions for the diagnosis toolbox."""
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
[docs]
def IsolabilityMatrix(fsm: np.ndarray) -> np.ndarray:
"""Compute isolability matrix based on a fault signature matrix"""
nf = fsm.shape[1]
im = np.ones((nf, nf), dtype=np.int)
for ri in fsm:
im[np.ix_(ri > 0, ri == 0)] = 0
return im
[docs]
def DiagnosesAndConfusionMatrix(data, residx=None) -> tuple[np.ndarray, np.ndarray]:
"""Compute consistency based diagnoses and corresponding confusion matrix
based on a dataset."""
if isinstance(residx, type(None)):
dx = SingleFaultIsolability(data['res'], data['fsm'])
else:
dx = SingleFaultIsolability(data['res'][:, residx],
data['fsm'][residx, :])
nf = data['fsm'].shape[1]
C = np.zeros((nf, nf))
for fi in range(nf):
for fj in range(nf):
fjIdx = data['mode'] == fj
C[fj, fi] = np.sum(dx[fjIdx, fi]) / np.sum(fjIdx)
return dx, C
def SingleFaultIsolability(res, fsm) -> np.ndarray:
"""Compute single fault consistency based diagnoses."""
N = res.shape[0]
nf = fsm.shape[1]
dx = np.zeros((N, nf))
for k, rk in enumerate(res):
alarm = (rk > 0.5)
if np.any(alarm):
dx[k] = np.all(fsm[alarm], axis=0)
else:
dx[k] = np.hstack((True, np.zeros(nf - 1)))
return dx
[docs]
def PlotConfusionMatrix(C, ax=None):
"""Plot a confusion matrix in a suitable colormap."""
if not ax:
ax = plt.gca()
nf = C.shape[0]
ax.imshow(C, cmap=summer_cmap)
for fi in range(nf):
for fj in range(nf):
ax.text(fi, fj, f'{(C[fj, fi] * 100):.1f}',
ha='center', va='center', color='k')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_xticks(np.arange(C.shape[1] + 1) - .5, minor=True)
ax.set_yticks(np.arange(C.shape[0] + 1) - .5, minor=True)
ax.tick_params(which="minor", bottom=False, left=False)
ax.grid(which="minor", color="w", linestyle='-', linewidth=3)
def make_colormap(seq):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples. The floats should be increasing
and in the interval (0,1).
"""
seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
cdict = {'red': [], 'green': [], 'blue': []}
for i, item in enumerate(seq):
if isinstance(item, float):
r1, g1, b1 = seq[i - 1]
r2, g2, b2 = seq[i + 1]
cdict['red'].append([item, r1, r2])
cdict['green'].append([item, g1, g2])
cdict['blue'].append([item, b1, b2])
return mcolors.LinearSegmentedColormap('CustomMap', cdict)
summer_cmap = make_colormap([
(0.000, 0.500, 0.400),
(0.016, 0.508, 0.400),
(0.032, 0.516, 0.400),
(0.048, 0.524, 0.400),
(0.063, 0.532, 0.400),
(0.079, 0.540, 0.400),
(0.095, 0.548, 0.400),
(0.111, 0.556, 0.400),
(0.127, 0.563, 0.400),
(0.143, 0.571, 0.400),
(0.159, 0.579, 0.400),
(0.175, 0.587, 0.400),
(0.190, 0.595, 0.400),
(0.206, 0.603, 0.400),
(0.222, 0.611, 0.400),
(0.238, 0.619, 0.400),
(0.254, 0.627, 0.400),
(0.270, 0.635, 0.400),
(0.286, 0.643, 0.400),
(0.302, 0.651, 0.400),
(0.317, 0.659, 0.400),
(0.333, 0.667, 0.400),
(0.349, 0.675, 0.400),
(0.365, 0.683, 0.400),
(0.381, 0.690, 0.400),
(0.397, 0.698, 0.400),
(0.413, 0.706, 0.400),
(0.429, 0.714, 0.400),
(0.444, 0.722, 0.400),
(0.460, 0.730, 0.400),
(0.476, 0.738, 0.400),
(0.492, 0.746, 0.400),
(0.508, 0.754, 0.400),
(0.524, 0.762, 0.400),
(0.540, 0.770, 0.400),
(0.556, 0.778, 0.400),
(0.571, 0.786, 0.400),
(0.587, 0.794, 0.400),
(0.603, 0.802, 0.400),
(0.619, 0.810, 0.400),
(0.635, 0.817, 0.400),
(0.651, 0.825, 0.400),
(0.667, 0.833, 0.400),
(0.683, 0.841, 0.400),
(0.698, 0.849, 0.400),
(0.714, 0.857, 0.400),
(0.730, 0.865, 0.400),
(0.746, 0.873, 0.400),
(0.762, 0.881, 0.400),
(0.778, 0.889, 0.400),
(0.794, 0.897, 0.400),
(0.810, 0.905, 0.400),
(0.825, 0.913, 0.400),
(0.841, 0.921, 0.400),
(0.857, 0.929, 0.400),
(0.873, 0.937, 0.400),
(0.889, 0.944, 0.400),
(0.905, 0.952, 0.400),
(0.921, 0.960, 0.400),
(0.937, 0.968, 0.400),
(0.952, 0.976, 0.400),
(0.968, 0.984, 0.400),
(0.984, 0.992, 0.400),
(1.000, 1.000, 0.400)])