kika.cov

The cov module provides functionality for working with covariance matrices from various sources.

kika.cov.read_coverx(file_path: str, ascending: bool = True, energy_unit: str = 'eV') → CrossSectionCovariance[source]

Read a COVERX covariance file (text or binary) and return a CrossSectionCovariance object.

The format is auto-detected: if the first bytes are printable ASCII the file is treated as text; otherwise it is parsed as a Fortran unformatted binary COVERX file.

Parameters:
  • file_path (str) – Path to the COVERX covariance file

  • ascending (bool, optional) – If True, energies are reordered in ascending order (default True)

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'

Returns:

Parsed covariance data

Return type:

CrossSectionCovariance

Raises:
  • EmptyParsingError – If no valid covariance matrices were found in the file

  • InvalidDataFormatError – If the binary structure is corrupted

kika.cov.write_coverx(data: CrossSectionCovariance, file_path: str, fmt: str = 'binary', title: str = '', endian: str = '>') → None[source]

Write a CrossSectionCovariance to a COVERX covariance file (text or binary).

Parameters:
  • data (CrossSectionCovariance) – Covariance data to write.

  • file_path (str) – Output file path.

  • fmt (str, optional) – 'binary' (default) or 'text'.

  • title (str, optional) – File title / description.

  • endian (str, optional) – Endianness for binary format: '>' big-endian (default).

kika.cov.read_covfil(file_path: str, energy_unit: str = 'eV') → CrossSectionCovariance | LegendreCovariance[source]

Parse an NJOY-generated COVFIL/GENDF covariance file.

Returns a CrossSectionCovariance for MF33 files (cross-section covariances) or an LegendreCovariance for MF34 files (angular-distribution covariances).

MF3 cross sections are stored in CrossSectionCovariance.cross_sections (MF33 case only).

Parameters:
  • file_path (str) – Path to the NJOY-generated covariance file.

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'.

Returns:

Parsed covariance data.

Return type:

CrossSectionCovariance or LegendreCovariance

Raises:

EmptyParsingError – If no valid covariance matrices were found.

kika.cov.write_covfil(data: CrossSectionCovariance | LegendreCovariance, file_path: str, tape_label: str = '', temperature: float | None = None, awr: float | None = None) → None[source]

Write a CrossSectionCovariance or LegendreCovariance to an NJOY COVFIL/GENDF text file.

Parameters:
  • data (CrossSectionCovariance or LegendreCovariance) – Covariance data to write.

  • file_path (str) – Output file path.

  • tape_label (str, optional) – Label for the tape header line (max 66 chars). Default ''.

  • temperature (float, optional) – Temperature in K written into MF1 MT451 CONT record. If None (default), uses data.metadata['temperature'] (set by read_covfil()), falling back to 0.0.

  • awr (float, optional) – Atomic weight ratio written into MF1 MT451 HEAD and each MF33/MF34 section HEAD. If None (default), uses data.metadata['awr'] (set by read_covfil()), falling back to 0.0.

kika.cov.read_boxer(file_path: str, energy_unit: str = 'eV') → CrossSectionCovariance[source]

Read a BOXER card-image (ASCII) covariance file and return a CrossSectionCovariance.

BOXER is produced by NJOY’s COVR module. It stores energy boundaries, cross-section vectors, standard-deviation vectors, and covariance or correlation matrices in a compressed card-image format.

Parameters:
  • file_path (str) – Path to the BOXER file.

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'.

Returns:

Parsed covariance data.

Return type:

CrossSectionCovariance

Raises:

EmptyParsingError – If no valid covariance matrices were found.

kika.cov.write_boxer(data: CrossSectionCovariance, file_path: str, hlibid: str = '', hdescr: str = '', nvf: int = 10) → None[source]

Write a CrossSectionCovariance to a BOXER card-image (ASCII) file.

Parameters:
  • data (CrossSectionCovariance) – Covariance data to write.

  • file_path (str) – Output file path.

  • hlibid (str, optional) – Library identifier (3 chars max). Default ''.

  • hdescr (str, optional) – Description (32 chars max). Default ''.

  • nvf (int, optional) – Value format code (7–14). Default 10 (1P8E10.3).

kika.cov.read_scale_covmat(file_path: str, ascending: bool = True, energy_unit: str = 'eV') → CrossSectionCovariance

Read a COVERX covariance file (text or binary) and return a CrossSectionCovariance object.

The format is auto-detected: if the first bytes are printable ASCII the file is treated as text; otherwise it is parsed as a Fortran unformatted binary COVERX file.

Parameters:
  • file_path (str) – Path to the COVERX covariance file

  • ascending (bool, optional) – If True, energies are reordered in ascending order (default True)

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'

Returns:

Parsed covariance data

Return type:

CrossSectionCovariance

Raises:
  • EmptyParsingError – If no valid covariance matrices were found in the file

  • InvalidDataFormatError – If the binary structure is corrupted

kika.cov.read_njoy_covmat(file_path: str, energy_unit: str = 'eV') → CrossSectionCovariance | LegendreCovariance

Parse an NJOY-generated COVFIL/GENDF covariance file.

Returns a CrossSectionCovariance for MF33 files (cross-section covariances) or an LegendreCovariance for MF34 files (angular-distribution covariances).

MF3 cross sections are stored in CrossSectionCovariance.cross_sections (MF33 case only).

Parameters:
  • file_path (str) – Path to the NJOY-generated covariance file.

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'.

Returns:

Parsed covariance data.

Return type:

CrossSectionCovariance or LegendreCovariance

Raises:

EmptyParsingError – If no valid covariance matrices were found.

class kika.cov.LegendreCovariance(isotope_rows: ~typing.List[int] = <factory>, reaction_rows: ~typing.List[int] = <factory>, l_rows: ~typing.List[int] = <factory>, isotope_cols: ~typing.List[int] = <factory>, reaction_cols: ~typing.List[int] = <factory>, l_cols: ~typing.List[int] = <factory>, energy_grids: ~typing.List[~typing.List[float]] = <factory>, matrices: ~typing.List[~numpy.ndarray] = <factory>, is_relative: ~typing.List[bool] = <factory>, frame: ~typing.List[str] = <factory>, energy_unit: str = 'eV', metadata: ~typing.Dict[str, ~typing.Any] = <factory>, legendre_coefficients: ~typing.Dict[~typing.Tuple[int, int, int], ~numpy.ndarray] = <factory>, mt_metadata: ~typing.Dict[~typing.Tuple[int, int], ~typing.Dict[str, ~typing.Any]] = <factory>)[source]

Bases: object

Format-agnostic covariance of Legendre expansion coefficients.

The canonical fields (matrices, energy grids, isotope/reaction/L tags, frame, is_relative) hold the data in a representation that is shared across source formats. Format-specific extras travel in two metadata dictionaries so that round-trips back to the same format produce a compatible (and where possible identical) section.

Canonical fields

isotope_rows, reaction_rows, l_rowsList[int]

Row tags (ZA, MT, Legendre order) per matrix.

isotope_cols, reaction_cols, l_colsList[int]

Column tags per matrix.

energy_gridsList[List[float]]

Energy boundaries for each matrix (NE points → matrix is (NE-1)x(NE-1) or rectangular for cross-L blocks).

matricesList[np.ndarray]

Covariance matrix per (isotope, reaction, L) row/col tuple.

is_relativeList[bool]

True when the matrix stores relative covariance, False for absolute (LB=0).

frameList[str]

Reference frame: "same-as-MF4" (LCT=0), "LAB" (LCT=1), "CM" (LCT=2), or "unknown LCT=…".

energy_unitstr

"eV" (default) or "MeV".

legendre_coefficientsDict[(isotope, mt, l), np.ndarray]

Optional cell-averaged nominal coefficients on the matrix grids.

Format-specific metadata

metadataDict[str, Any]

Whole-object extras. Conventional keys:

  • "source_format": "endf", "covfil", "gendf", …

  • "source_path": original file path (string)

  • any other format-specific defaults the user wants to round-trip.

mt_metadataDict[(isotope, mt), Dict[str, Any]]

Per-section header data. ENDF MF34 keys:

  • "za": ZA = 1000*Z + A (float)

  • "awr": atomic-weight ratio

  • "mat": ENDF MAT number

  • "ltt": Legendre representation flag (1 = a_1…, 2 = a_0…).

Populated by from_endf() / MF34MT.to_ang_covmat() and consulted by to_mf34() to fill the MF34 header. Caller overrides on to_mf34() always win.

add_matrix(isotope_row: int, reaction_row: int, l_row: int, isotope_col: int, reaction_col: int, l_col: int, matrix: ndarray, energy_grid: List[float], is_relative: bool, frame: str)[source]

Add an angular covariance matrix to the collection.

Parameters:
  • isotope_row (int) – Row isotope ID

  • reaction_row (int) – Row reaction MT number

  • l_row (int) – Row Legendre coefficient index

  • isotope_col (int) – Column isotope ID

  • reaction_col (int) – Column reaction MT number

  • l_col (int) – Column Legendre coefficient index

  • matrix (np.ndarray) – Covariance matrix

  • energy_grid (List[float]) – Energy grid for this covariance matrix

  • is_relative (bool) – True if matrix values are relative, False if absolute (LB=0 present)

  • frame (str) – Reference frame: “same-as-MF4”, “LAB”, “CM”, or “unknown LCT=X”

attach_magnitude_covariance(xs_covariance, *, isotope: int, mt: int, magnitude_mt: int | None = None, atol: float = 1e-12) → LegendreCovariance[source]

Fill the null (0, 0) block from MF33, so the L=0 row can be read.

Returns a copy; the receiver is left alone because MF34MT.to_ang_covmat() caches the object it hands out and a mutation here would leak into every later caller.

The MF33 self-covariance is inserted on its own energy grid, not projected onto the MF34 one. compute_union_energy_grids() merges every grid that mentions the (isotope, mt, 0) triplet, so the L=0 axis becomes union(MF33 grid, a_0 grid) – a common refinement of both, which is the only grid on which the piecewise-constant lift of both legs is exact. The two are genuinely interleaved in practice: for U-238 ENDF/B-VIII.0, 35 of the 76 MF33/MT2 edges are absent from the MF34 grid and 37 of its 78 are absent from MF33’s, so projecting either onto the other would coarsen it.

Parameters:
  • xs_covariance (CrossSectionCovariance) – Typically endf.mf[33].mt[mt].to_xs_covmat(). Must carry a relative self-block for (isotope, magnitude_mt).

  • isotope (int) – The MF34 section whose (0, 0) block is being filled.

  • mt (int) – The MF34 section whose (0, 0) block is being filled.

  • magnitude_mt (int, optional) – MT to read from MF33. Defaults to mt – the magnitude that pairs with MT2’s a_l is MT2’s own cross section, and likewise for MT51. Override only to couple a section to a different reaction.

  • atol (float, default 1e-12) – Tolerance for the “existing (0, 0) block is null” check.

Raises:

ValueError – If the section has no a_0 blocks, if its (0, 0) block is not null (the file already states the magnitude self-covariance, and adding MF33’s would double count the magnitude variance), if MF33 has no self-block for the reaction, or if that block is absolute while the a_0 family is relative.

cholesky_decomposition(*, space: str = 'log', psd_method: str = 'auto', jitter_scale: float = 1e-10, max_jitter_ratio: float = 0.001, verbose: bool = True, logger=None) → ndarray[source]

Robust Cholesky factor L such that M ≈ L L^T.

Parameters:
  • space (str) – “linear” or “log” space for decomposition

  • psd_method (str) – PSD correction: “auto” (default; clip when negatives are tiny, else Higham), “higham”, “clip”, or “none” (jitter on Cholesky failure).

  • jitter_scale (float) – Base jitter scale (only used when psd_method=”jitter”).

  • max_jitter_ratio (float) – Maximum jitter relative to matrix norm.

  • verbose (bool) – Whether to log progress

  • logger (optional) – Logger instance for output

Returns:

Lower triangular Cholesky factor L

Return type:

np.ndarray

property clipped_correlation_matrix: ndarray

Return the correlation matrix clipped to [-1, 1] range. Diagonal elements are forced to 1.0, undefined entries become NaN.

compute_union_energy_grids(atol: float = 1e-12)[source]

Compute union energy grids for all parameter triplets.

This method creates a unified energy grid for each (isotope, reaction, legendre) triplet by merging all energy grids that involve that triplet, removing duplicates within tolerance.

Parameters:

atol (float, default 1e-12) – Absolute tolerance for merging energy points

Returns:

Dictionary mapping (isotope, reaction, legendre) triplets to union energy grids

Return type:

Dict[Tuple[int, int, int], np.ndarray]

copy() → LegendreCovariance[source]

Return a deep copy of this object.

property correlation_matrix: ndarray

Return the correlation matrix computed from the covariance matrix. Diagonal elements are forced to 1.0, undefined entries become NaN.

property covariance_matrix: ndarray

Return the full covariance matrix.

If ensure_psd() has been called, the cached PSD matrix is returned directly instead of re-assembling from sub-matrices.

Returns:

Full covariance matrix of shape (N*G_max, N*G_max)

Return type:

np.ndarray

describe(i: int) → str[source]

Pretty single-matrix summary in plain text.

Parameters:

i (int) – Index of the matrix to describe

Returns:

Human-readable description of the matrix

Return type:

str

eigen_decomposition(*, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, logger=None) → Tuple[ndarray, ndarray][source]

Eigendecomposition with PSD correction.

Parameters:
  • space (str) – “linear” or “log” space for decomposition

  • clip_negatives (bool) – Deprecated. Use psd_method instead.

  • psd_method (str or None) – “auto” (default), “higham”, “clip”, or “none”.

  • verbose (bool) – Whether to log progress

  • logger (optional) – Logger instance for output

Returns:

Eigenvalues and eigenvectors

Return type:

Tuple[np.ndarray, np.ndarray]

energy_unit: str = 'eV'
ensure_psd(*, preserve_diagonal: bool = True, max_iter: int = 1000, tol: float = 1e-10, eigval_floor: float = 0.0, verbose: bool = True, logger=None) → Tuple[LegendreCovariance, dict][source]

Return a copy whose covariance_matrix is the nearest PSD matrix.

Uses Higham’s alternating-projection algorithm. The original sub-matrices are not modified; instead, the assembled PSD matrix is cached and returned by the covariance_matrix / log_covariance_matrix properties.

Parameters:
  • preserve_diagonal (bool) – If True, preserve the original variances (diagonal elements).

  • max_iter (int) – Maximum number of Higham iterations.

  • tol (float) – Convergence tolerance on relative Frobenius change.

  • eigval_floor (float) – Floor for eigenvalues in the PSD projection step.

  • verbose (bool) – Whether to print diagnostic information.

  • logger (optional) – Logger instance for file output.

Returns:

(psd_copy, info) where psd_copy has the PSD cache set and info is the diagnostic dict from nearest_psd_higham.

Return type:

Tuple[LegendreCovariance, dict]

filter_by_isotope_reaction(isotope: int, mt: int) → LegendreCovariance[source]

Return a new LegendreCovariance containing only matrices for the specified isotope and MT reaction.

This method filters the covariance matrices to include only those where both row and column parameters match the specified isotope and MT reaction.

Parameters:
  • isotope (int) – Isotope ID to filter by

  • mt (int) – Reaction MT number to filter by

Returns:

New LegendreCovariance object containing only the filtered matrices

Return type:

LegendreCovariance

classmethod from_covariance_suite(suite, nuclide: int = 0, energy_unit: str = 'eV') → LegendreCovariance[source]

Every MF34 section of a GNDS covarianceSuite, as one carrier.

The covariance half of what phase 4 set out to do: the multigroup collapse can be driven from a reactionSuite on both sides now — a_l through legendre_source_from_model(), and the covariance through here.

The model is treated as one more source format, on the same footing as GENDF, COVFIL, BOXER and COVERX, exactly as from_covariance_section() already does. Duck-typed rather than imported, and for the same reason: kika.cov does not depend on the model, and phase 4’s P4 had just finished removing the last import-time reason it might.

A suite rather than a section, unlike the cross-section twin, because the two are keyed differently. There, several MF35 bands share one MT and would be indistinguishable inside one object. Here the key is (isotope, MT, l) and every MF34 section carries a distinct one, so a whole file assembles without collision — which is what the collapse needs, since it walks the blocks looking for (l_row, l_col) pairs.

Sections whose rowData.ENDF_MFMT is not 34/… are skipped, so an MF31 or MF33 section living in the same suite is left alone rather than swept in as an angular block.

Parameters:
  • suite – A CovarianceSuite: anything iterable of sections, or exposing covarianceSections. Each section needs form.matrix, form.rowGrid, form.isRelative and rowData.ENDF_MFMT, with rowData.legendreOrder giving l.

  • nuclide (int, optional) – ZAID to file the matrices under, used when section.provenance.za is absent. The model identifies a covariance by href rather than by a material header.

  • energy_unit (str, optional) – Unit of form.rowGrid. The model keeps ENDF’s native eV.

Notes

⚠ The model states two grids and this carrier stores one. Where a section’s row and column grids differ, the model is the one telling the truth and this conversion loses the distinction — the same asymmetry kika.sampling.model_blocks._mf34_entries documents from the sampling side. A warning is raised rather than the difference being absorbed silently. No MF34 seen so far states two.

⚠ A section that states no single matrix raises. It used to be continue, which dropped the section and returned a smaller carrier that looked complete — the one place in the library where meeting a §25.2 mixed was silent rather than loud. What raises now, and why kika will not merge the components for you, is kika._covariance_forms.require_single_matrix().

classmethod from_covfil(file_path: str | Path, energy_unit: str = 'eV') → LegendreCovariance[source]

Create a LegendreCovariance instance from an NJOY-generated COVFIL/GENDF file.

Parameters:
  • file_path (str or Path) – Path to the COVFIL/GENDF covariance file

  • energy_unit (str, optional) – Energy unit for the energy grid: ‘eV’ (default) or ‘MeV’

Returns:

LegendreCovariance instance loaded from the file

Return type:

LegendreCovariance

Raises:

TypeError – If the file contains MF33 data instead of MF34

classmethod from_endf(file_path: str | Path, energy_unit: str = 'eV') → LegendreCovariance[source]

Create a LegendreCovariance instance from an ENDF file containing MF34 data.

This is a convenience class method that reads an ENDF file, extracts MF34 (angular distribution covariance) data, and converts it to a LegendreCovariance object.

Parameters:
  • file_path (str or Path) – Path to the ENDF file containing MF34 data

  • energy_unit (str, optional) – Energy unit for the energy grid: ‘eV’ (default) or ‘MeV’

Returns:

LegendreCovariance instance with angular distribution covariance data

Return type:

LegendreCovariance

Raises:

Examples

>>> mf34_covmat = LegendreCovariance.from_endf('path/to/endf_file.txt')
>>> print(f"Loaded {mf34_covmat.num_matrices} angular covariance matrices")
>>> # Or specify MeV if needed
>>> mf34_covmat_mev = LegendreCovariance.from_endf('path/to/file.txt', energy_unit='MeV')

Notes

This method internally: 1. Reads the ENDF file using read_endf() 2. Extracts MF34 data 3. Converts to LegendreCovariance using the to_ang_covmat() method

See also

read_endf

Function to read ENDF files

get_ll_prime_correlations(isotope: int, mt: int) → Dict[str, Any][source]

Extract per-energy l-l’ correlation blocks from ENDF MF34 data.

For each energy bin, builds the L×L covariance sub-block from all stored (l_row, l_col) sub-matrices that share the same energy grid, then converts to correlation.

Parameters:
  • isotope (int) – Isotope ID (e.g. 2631 for Fe-56).

  • mt (int) – Reaction MT number (e.g. 2 for elastic).

Returns:

‘energy_grid’: np.ndarray — bin boundaries (N+1 points) ‘l_values’: list of int — sorted Legendre orders present ‘correlations’: list of np.ndarray — one L×L correlation matrix per energy bin ‘mean_abs_offdiag’: float — mean |off-diagonal| across all bins

Return type:

dict

get_uncertainties_for_legendre_coefficient(isotope: int, mt: int, l_coefficient: int | List[int]) → Dict[str, ndarray] | None | Dict[int, Dict[str, ndarray] | None][source]

Extract standard uncertainties (square root of diagonal variance) for Legendre coefficient(s).

IMPORTANT: MF34 data is typically stored as RELATIVE covariances (fractional uncertainties δA_ℓ/A_ℓ). This method returns the uncertainties as stored in MF34, along with an ‘is_relative’ flag.

To convert relative uncertainties to absolute: σ_abs = σ_rel × |A_ℓ| where A_ℓ are the Legendre coefficients from ENDF MF=4 data.

Parameters:
  • isotope (int) – Isotope ID

  • mt (int) – Reaction MT number

  • l_coefficient (int or list of int) – Legendre coefficient index (L value) or list of L values

Returns:

For single int: Dictionary containing:
  • ’energies’: np.ndarray - Energy bin boundaries (N+1 points for N bins) in eV or MeV

  • ’uncertainties’: np.ndarray - Uncertainties (√diagonal of covariance) for each bin

  • ’is_relative’: bool - True if relative (δA_ℓ/A_ℓ), False if absolute (δA_ℓ)

For list of ints: Dictionary mapping L coefficient to uncertainty data (or None if not found).

Return type:

dict or dict of dicts

Notes

  • If is_relative=True, you must convert to absolute uncertainties by multiplying by the Legendre coefficients A_ℓ from ENDF MF=4 before using in propagation formulas.

  • The LB flag in ENDF-6 format determines if data is relative (LB=1,2,5) or absolute (LB=0).

  • Energies are returned as BIN BOUNDARIES, not bin centers. Each uncertainty value applies to the energy bin defined by consecutive boundary pairs [E[i], E[i+1]).

get_union_energy_grids()[source]
has_uniform_energy_grid() → bool[source]

Check if all matrices have the same energy grid.

Returns:

True if all energy grids are identical, False otherwise. Returns True for empty collections (vacuous truth).

Return type:

bool

property isotopes: Set[int]

Get the set of unique isotope IDs in the covariance matrices.

Returns:

Sorted list of unique isotope IDs

Return type:

Set[int]

property legendre_indices: Set[int]

Get the set of unique Legendre coefficient indices in the covariance matrices.

Returns:

Sorted list of unique Legendre coefficient indices

Return type:

Set[int]

property log_covariance_matrix: ndarray

Return the log-space covariance matrix. Converts relative covariance to log-space using log1p transformation.

magnitude_cross_summary(isotope: int, mt: int, *, atol: float = 1e-12) → Dict[str, Any][source]

Describe the a_0 (L=0) blocks this carrier holds for one section.

ENDF-6 Sec. 34.1 lets a file express the covariance between the cross section magnitude and the Legendre coefficients through the a_0 coefficient, even though a_0 = 1 in the ENDF system, and Sec. 34.2 reserves LTT=3 for a section where L or L1 = 0 appears anywhere. Sec. 34.3 then says the (0, 0) magnitude self-block belongs to MF33 and must not be repeated here, so a conforming file writes it null.

That null is exactly what makes the L=0 row unplottable on its own: correlation_matrix divides by sqrt(C_00), so every entry of the L=0 row and column becomes NaN, including the (0, L1) cross terms that do carry data. attach_magnitude_covariance() repairs it by supplying the missing self-block from MF33.

A file may also declare LTT=3 and then write the cross blocks as numerical zero – ENDF/B-VIII.1 does this for U-235 and U-238, where VIII.0 carried real terms. cross_is_null separates the two so a caller can say “this file has no cross terms” instead of drawing an empty block.

Parameters:
  • isotope (int) – The section to describe.

  • mt (int) – The section to describe.

  • atol (float, default 1e-12) – Below this the blocks count as null. Chosen above the ~1e-19 round-off that VIII.1 leaves behind and far below the ~1e-3 of a real term.

Returns:

present – any (0, L1) or (L, 0) block exists. self_present – the (0, 0) block exists. self_is_null – (0, 0) is zero, i.e. MF33 still owes it. cross_orders – sorted L1 with a (0, L1) block, 0 excluded. cross_max_abs – largest absolute cross-term value. cross_is_null – every cross block is zero to within atol.

Return type:

dict

property num_matrices: int

Get the number of covariance matrices stored.

Returns:

Number of matrices

Return type:

int

plot_covariance_heatmap(nuclide: int | str, mt: int, legendre_coeffs: int | List[int] | Tuple[int, int], ax: Axes | None = None, *, matrix_type: str = 'corr', figsize: Tuple[float, float] = (6, 6), dpi: int = 300, font_family: str = 'serif', vmax: float | None = None, vmin: float | None = None, show_uncertainties: bool = False, cmap: any | None = None, scale: str = 'log', energy_range: Tuple[float, float] | None = None, title: str | None = 'default', **imshow_kwargs) → Figure[source]

Draw a covariance or correlation matrix heatmap for MF34 angular distribution data.

Parameters:
  • nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)

  • mt (int) – Reaction MT number

  • legendre_coeffs (int, list of int, or tuple of (row_l, col_l)) – Legendre coefficient(s). Can be: - Single int: diagonal block for that L - List of ints: diagonal blocks for those L values - Tuple of (row_l, col_l): off-diagonal block between row and column L

  • ax (plt.Axes, optional) – Matplotlib axes to draw into (deprecated, only used when show_uncertainties=False)

  • matrix_type (str, default "corr") – Type of matrix to plot: “corr”/”correlation” for correlation matrix, or “cov”/”covariance” for covariance matrix

  • figsize (tuple) – Figure size in inches (width, height)

  • dpi (int) – Dots per inch for figure resolution

  • font_family (str) – Font family for text elements

  • vmax (float, optional) – Color scale limits

  • vmin (float, optional) – Color scale limits

  • show_uncertainties (bool) – Whether to show uncertainty plots above the heatmap

  • cmap (str or matplotlib.colors.Colormap, optional) – Colormap to use for the heatmap. Can be a string name of any matplotlib colormap (e.g., ‘viridis’, ‘plasma’, ‘RdYlBu’, ‘coolwarm’) or a matplotlib Colormap object. If None, defaults to ‘RdYlGn’ for correlation matrices and ‘viridis’ for covariance matrices.

  • scale (str, default "log") – Energy axis scale: “log”/”logarithmic” or “lin”/”linear”

  • energy_range (tuple of float, optional) – Energy range (min, max) for filtering. Values in eV.

  • title (str or None, default "default") – Plot title. If “default”, auto-generates from nuclide, MT, and Legendre coefficient. If a string, uses that as the title. If None, suppresses the title.

  • **imshow_kwargs – Additional arguments passed to imshow (deprecated)

Returns:

The matplotlib figure containing the heatmap and optional uncertainty plots

Return type:

plt.Figure

plot_uncertainties(isotope: int, mt: int, legendre_coeffs: int | List[int], ax: Axes | None = None, *, uncertainty_type: str = 'relative', style: str = 'default', figsize: Tuple[float, float] = (8, 5), dpi: int = 100, font_family: str = 'serif', legend_loc: str = 'best', energy_range: Tuple[float, float] | None = None, **kwargs) → Figure[source]

Plot uncertainties for MF34 angular distribution data for specific Legendre coefficients.

This method extracts and plots the diagonal uncertainties from the covariance matrix for the specified isotope, MT reaction, and Legendre coefficients.

Parameters:
  • isotope (int) – Isotope ID

  • mt (int) – Reaction MT number

  • legendre_coeffs (int or list of int) – Legendre coefficient(s) to plot uncertainties for. Can be a single int or a list of ints.

  • ax (plt.Axes, optional) – Matplotlib axes to draw into. If None, creates new figure.

  • uncertainty_type (str, default "relative") – Type of uncertainty to plot: “relative” (%) or “absolute”

  • style (str, default "default") – Plot style: ‘default’, ‘dark’, ‘paper’, ‘publication’, ‘presentation’

  • figsize (tuple, default (8, 5)) – Figure size in inches (width, height)

  • dpi (int, default 100) – Dots per inch for figure resolution

  • font_family (str, default "serif") – Font family for text elements

  • legend_loc (str, default "best") – Legend location

  • energy_range (tuple of float, optional) – Energy range (min, max) for x-axis. If None, uses the full data range. Values are used directly without clamping to data range.

  • **kwargs – Additional arguments passed to matplotlib plot functions

Returns:

The matplotlib figure containing the uncertainty plots

Return type:

plt.Figure

Examples

Plot relative uncertainties for Legendre coefficients L=1,2,3:

>>> fig = mf34_covmat.plot_uncertainties(isotope=92235, mt=2,
...                                     legendre_coeffs=[1, 2, 3])
>>> fig.show()

Plot absolute uncertainties for a single Legendre coefficient:

>>> fig = mf34_covmat.plot_uncertainties(isotope=92235, mt=2,
...                                     legendre_coeffs=1,
...                                     uncertainty_type="absolute")
>>> fig.show()
property reactions: Set[int]

Get the set of unique reaction MT numbers in the covariance matrices.

Returns:

Sorted list of unique reaction MT numbers

Return type:

Set[int]

remap_mt(old_mt: int, new_mt: int) → LegendreCovariance[source]

Return a copy with all occurrences of old_mt replaced by new_mt.

This is useful when NJOY GENDF files use different MT numbering (e.g. MT=251 for elastic scattering) that needs to be mapped back to the standard ENDF MT numbers (e.g. MT=2).

Parameters:
  • old_mt (int) – MT number to replace.

  • new_mt (int) – MT number to use instead.

Returns:

New instance with remapped MT numbers.

Return type:

LegendreCovariance

summary() → DataFrame[source]

Create a summary DataFrame with one row per matrix.

Returns:

DataFrame with columns: isotope_row, reaction_row, L_row, isotope_col, reaction_col, L_col, NE (len(energy_grid)), M (NE-1), is_relative, frame

Return type:

pd.DataFrame

svd_decomposition(*, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, full_matrices: bool = False, logger=None) → Tuple[ndarray, ndarray, ndarray][source]

SVD with PSD pre-processing.

Parameters:
  • space (str) – “linear” or “log” space for decomposition

  • clip_negatives (bool) – Deprecated. Use psd_method instead.

  • psd_method (str or None) – “auto” (default), “higham”, “clip”, or “none”.

  • verbose (bool) – Whether to log progress

  • full_matrices (bool) – Whether to return full-sized U and V matrices

  • logger (optional) – Logger instance for output

Returns:

U, singular values, V^T matrices

Return type:

Tuple[np.ndarray, np.ndarray, np.ndarray]

to_covfil(file_path: str | Path, tape_label: str = '', temperature: float = 0.0) → None[source]

Write this LegendreCovariance to an NJOY COVFIL/GENDF text file.

Parameters:
  • file_path (str or Path) – Output file path

  • tape_label (str, optional) – Label for the tape header line (max 66 chars)

  • temperature (float, optional) – Temperature in K for MF1 MT451 CONT record

to_dataframe() → DataFrame[source]

Convert the angular covariance matrix data to a pandas DataFrame.

Returns:

DataFrame containing the covariance matrix data with columns: ISO_H, REAC_H, L_H, ISO_V, REAC_V, L_V, ENE, STD

Return type:

pd.DataFrame

to_endf(source_endf: str | Path, output_path: str | Path, isotope: int, mt: int, *, replace_existing: bool = True, update_directory: bool = True, **mf34_overrides) → str[source]

Write a single (isotope, MT) MF34 section into an ENDF file.

Convenience wrapper that calls to_mf34() to build the section and kika.endf.writers.write_mf34_to_file() to splice it into a template file. Extra keyword arguments are forwarded to to_mf34() (za, awr, mat, ltt, frame).

to_heatmap_data(nuclide: int | str, mt: int, legendre_coeffs: int | List[int] | Tuple[int, int], *, matrix_type: str = 'corr', scale: str = 'log', energy_range: Tuple[float, float] | None = None, **kwargs) → LegendreHeatmapData[source]

Prepare MF34 covariance heatmap data for PlotBuilder rendering.

This method handles the complex MF34 matrix structure including per-Legendre energy grids and prepares data for visualization.

Parameters:
  • nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)

  • mt (int) – MT reaction number

  • legendre_coeffs (int, list of int, or tuple of (row_l, col_l)) – Legendre coefficient(s). Can be: - Single int: diagonal block for that L - List of ints: diagonal blocks for those L values - Tuple of (row_l, col_l): off-diagonal block between row and column L

  • matrix_type (str, default 'corr') – Type of matrix: ‘corr’/’correlation’ for correlation matrix, or ‘cov’/’covariance’ for covariance matrix

  • scale (str, default 'log') – Energy axis scale: ‘log’/’logarithmic’ or ‘lin’/’linear’

  • energy_range (tuple of float, optional) – Energy window (emin, emax). Only bins overlapping the window are kept.

  • **kwargs – Additional parameters (reserved for future use)

Returns:

Heatmap data object ready for PlotBuilder.add_heatmap()

Return type:

LegendreHeatmapData

Examples

>>> # Simple usage with PlotBuilder
>>> from kika.plotting import PlotBuilder
>>> heatmap_data = mf34_covmat.to_heatmap_data(
...     nuclide=92235, mt=2, legendre_coeffs=[1, 2, 3]
... )
>>> fig = PlotBuilder(style='light').add_heatmap(heatmap_data)
>>> fig.show()
>>> # Can also use string symbols
>>> heatmap_data = mf34_covmat.to_heatmap_data(
...     nuclide='U235', mt=2, legendre_coeffs=1, matrix_type='cov'
... )
to_mf34(isotope: int, mt: int, *, za: float | None = None, awr: float | None = None, mat: int | None = None, ltt: int | None = None, frame: str | None = None) → MF34MT[source]

Build an MF34MT from the entries tagged (isotope, mt).

Matrices with isotope_row == isotope and reaction_row == mt are grouped by reaction_col (MT1) into one subsection each, and within a subsection by (L, L1) into one sub-subsection per pair. For symmetric self-correlation (MT1 == MT) only the upper triangle L <= L1 is emitted. Diagonal blocks (L == L1) are written as LB=5 and off-diagonal blocks as LB=6.

Parameters:
  • isotope (int) – Selector keys; matrices with matching row tags are included.

  • mt (int) – Selector keys; matrices with matching row tags are included.

  • za (optional) – Override the corresponding MF34 header fields. When omitted, values are taken from self.mt_metadata[(isotope, mt)] if available, then from sensible defaults (za=isotope, awr=1.0, mat=0, ltt inferred — 2 if any L=0 pair is present, else 1).

  • awr (optional) – Override the corresponding MF34 header fields. When omitted, values are taken from self.mt_metadata[(isotope, mt)] if available, then from sensible defaults (za=isotope, awr=1.0, mat=0, ltt inferred — 2 if any L=0 pair is present, else 1).

  • mat (optional) – Override the corresponding MF34 header fields. When omitted, values are taken from self.mt_metadata[(isotope, mt)] if available, then from sensible defaults (za=isotope, awr=1.0, mat=0, ltt inferred — 2 if any L=0 pair is present, else 1).

  • ltt (optional) – Override the corresponding MF34 header fields. When omitted, values are taken from self.mt_metadata[(isotope, mt)] if available, then from sensible defaults (za=isotope, awr=1.0, mat=0, ltt inferred — 2 if any L=0 pair is present, else 1).

  • frame (optional) – Override the per-matrix frame (LCT) for every emitted sub-subsection. Accepts "same-as-MF4", "LAB", or "CM".

Return type:

MF34MT

Notes

Multiple LIST records (NI > 1) per sub-subsection in the original file are collapsed to a single LB=5/LB=6 record on the matrix’s stored grid; the numeric content is preserved but the original per-record split is not.

to_plot_data(nuclide: int | str, mt: int, order: int, sigma: float = 1.0, uncertainty_type: str = 'relative', label: str = None, **styling_kwargs)[source]

Create a PlotData object for Legendre coefficient uncertainties.

This is a convenience method to easily convert MF34 covariance data into a plottable format using the new plotting infrastructure.

Parameters:
  • nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)

  • mt (int) – Reaction MT number

  • order (int) – Legendre polynomial order

  • sigma (float, default 1.0) – Sigma level for uncertainty scaling (e.g., 1.0 for 1σ, 2.0 for 2σ)

  • uncertainty_type (str, default 'relative') – Type of uncertainty: ‘relative’ (%) or ‘absolute’

  • label (str, optional) – Custom label for the plot. If None, auto-generates from isotope and order. Note: Energy values are returned in eV (native ENDF-6 format) to ensure compatibility when combining with MF4 data.

  • **styling_kwargs – Additional styling kwargs (color, linestyle, linewidth, etc.)

Returns:

Tuple containing: - coeff_data: Legendre coefficient data if available in legendre_coefficients, else None - unc_data: Uncertainty data for the Legendre coefficients

Return type:

tuple of (LegendreCoeffPlotData or None, LegendreUncertaintyPlotData)

Raises:

ValueError – If uncertainty data is not available for the specified parameters

Examples

>>> # Extract uncertainty data from LegendreCovariance - both notation styles work
>>> mf34_covmat = endf.mf[34].mt[2].to_ang_covmat()
>>> coeff_data, unc_data = mf34_covmat.to_plot_data(nuclide=26056, mt=2, order=1)
>>> # Note: coeff_data will be None (MF34 only has uncertainties, not values)
>>>
>>> # Build a plot with just uncertainties
>>> from kika.plotting import PlotBuilder
>>> fig = PlotBuilder().add_data(unc_data).build()
validate_union_grids(verbose: bool = True) → bool[source]

Validate that union grids are properly constructed and aligned.

Parameters:

verbose (bool, default True) – Whether to print validation details

Returns:

True if validation passes, False otherwise

Return type:

bool

isotope_rows: List[int]
reaction_rows: List[int]
l_rows: List[int]
isotope_cols: List[int]
reaction_cols: List[int]
l_cols: List[int]
energy_grids: List[List[float]]
matrices: List[ndarray]
is_relative: List[bool]
frame: List[str]
metadata: Dict[str, Any]
legendre_coefficients: Dict[Tuple[int, int, int], ndarray]
mt_metadata: Dict[Tuple[int, int], Dict[str, Any]]
kika.cov.MF34CovMat

alias of LegendreCovariance

class kika.cov.CrossSectionCovariance(num_groups: int = 0, energy_grid: ~typing.List[float] | None = None, energy_grids: ~typing.List[~typing.List[float]] = <factory>, isotope_rows: ~typing.List[int] = <factory>, reaction_rows: ~typing.List[int] = <factory>, isotope_cols: ~typing.List[int] = <factory>, reaction_cols: ~typing.List[int] = <factory>, matrices: ~typing.List[~numpy.ndarray] = <factory>, is_relative: ~typing.List[bool] = <factory>, cross_sections: ~typing.Dict[~typing.Tuple[int, int], ~numpy.ndarray] = <factory>, energy_unit: str = 'eV', metadata: ~typing.Dict[str, ~typing.Any] = <factory>)[source]

Bases: object

Format-agnostic multigroup cross-section covariance.

num_groups

Number of energy groups in the covariance matrices

Type:

int

energy_grid

List of energy grid boundaries (if available)

Type:

Optional[List[float]]

isotope_rows

List of row isotope IDs

Type:

List[int]

reaction_rows

List of row reaction MT numbers

Type:

List[int]

isotope_cols

List of column isotope IDs

Type:

List[int]

reaction_cols

List of column reaction MT numbers

Type:

List[int]

matrices

List of covariance matrices (one for each row-column combination)

Type:

List[np.ndarray]

energy_unit

Energy unit for energy_grid: ‘eV’ (default) or ‘MeV’

Type:

str

add_matrix(isotope_row: int, reaction_row: int, isotope_col: int, reaction_col: int, matrix: ndarray, energy_grid: List[float] | None = None, is_relative: bool | None = None) → None[source]

Add a covariance matrix to the collection.

Parameters:
  • isotope_row (int) – Row isotope ID

  • reaction_row (int) – Row reaction MT number

  • isotope_col (int) – Column isotope ID

  • reaction_col (int) – Column reaction MT number

  • matrix (np.ndarray) – Covariance matrix

  • energy_grid (list of float, optional) – Energy grid for this matrix. If not provided, validates against self.num_groups (multigroup mode).

  • is_relative (bool, optional) – Whether the matrix is relative (True) or absolute (False).

cholesky_decomposition(*, space: str = 'log', psd_method: str = 'auto', jitter_scale: float = 1e-10, max_jitter_ratio: float = 0.001, verbose: bool = True, logger=None) → ndarray[source]

Robust Cholesky factor L such that M ≈ L Lᵀ.

clean_cov(isotope: int) → CrossSectionCovariance[source]

Return a new CrossSectionCovariance containing only sub-matrices for isotope, always dropping reaction 1 and applying the mid/high-range rules:

4 → 51-91, 103 → 600-649, 104 → 650-699, 105 → 700-749, 106 → 750-799, 107 → 800

property clipped_correlation_matrix: ndarray

Return the correlation matrix clipped to [-1, 1] range.

Diagonal elements are forced to 1.0, undefined entries become NaN.

Returns:

Correlation matrix with values clipped to valid range [-1, 1]

Return type:

np.ndarray

copy() → CrossSectionCovariance[source]

Return a deep copy of this CrossSectionCovariance instance.

Returns:

Deep copy of the current covariance matrix object

Return type:

CrossSectionCovariance

property correlation_matrix: ndarray

Return the correlation matrix (unclipped).

Diagonal elements are forced to 1.0, undefined entries become NaN. No off-diagonal correction is applied.

Returns:

Correlation matrix with no clipping applied

Return type:

np.ndarray

property covariance_matrix: ndarray

Return the full covariance matrix of shape (N·G) × (N·G), where N = number of unique (iso,rxn) blocks and G = num_groups.

If ensure_psd() has been called, the cached PSD matrix is returned directly.

eigen_block_contributions(idx: int | None = None, which: str = 'min', top_n: int = 10, tol: float = 1e-12, symmetric: bool = True, relative: bool = False) → Dict[str, Any][source]

Measure how each (block_i, block_j) sub-matrix of the covariance matrix contributes to a chosen eigenvalue λ.

Parameters:
  • idx (Index of the eigenvalue to inspect (overrides which).)

  • which (If idx is None, choose ‘max’ or ‘min’ eigenvalue.)

  • top_n (Return only the top_n largest |contribution| entries.)

  • tol (Skip blocks with |contribution| ≤ tol.)

  • symmetric (If True, include only pairs with i ≤ j (avoids duplicates).)

  • relative (If True, report each block’s share of |λ|.)

Returns:

‘index’ : eigenvalue index used. ‘eigenvalue’ : eigenvalue λ. ‘contributions’: list of dicts:

{ ‘block’: (param_pairs[i], param_pairs[j]),

’contribution’: c_ij, ‘weight’: |c_ij| / |λ| # only if relative=True }

Return type:

A dict with

eigen_decomposition(*, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, logger=None) → Tuple[ndarray, ndarray][source]

Eigendecomposition with PSD correction.

energy_grid: List[float] | None = None
energy_unit: str = 'eV'
ensure_psd(*, preserve_diagonal: bool = True, max_iter: int = 1000, tol: float = 1e-10, eigval_floor: float = 0.0, verbose: bool = True, logger=None) → Tuple[CrossSectionCovariance, Dict[str, Any]][source]

Return a copy whose covariance_matrix is the nearest PSD matrix.

Uses Higham’s alternating-projection algorithm. The original sub-matrices are not modified; instead, the assembled PSD matrix is cached and returned by the covariance_matrix / log_covariance_matrix properties.

Parameters:
  • preserve_diagonal (bool) – If True, preserve the original variances (diagonal elements).

  • max_iter (int) – Maximum number of Higham iterations.

  • tol (float) – Convergence tolerance on relative Frobenius change.

  • eigval_floor (float) – Floor for eigenvalues in the PSD projection step.

  • verbose (bool) – Whether to print diagnostic information.

  • logger (optional) – Logger instance for file output.

Returns:

(psd_copy, info) where psd_copy has the PSD cache set and info is the diagnostic dict from nearest_psd_higham.

Return type:

Tuple[CrossSectionCovariance, dict]

filter_by_isotope(isotope: int) → CrossSectionCovariance[source]

Return a new CrossSectionCovariance containing only sub-matrices and cross-sections for the given isotope. All reactions for that isotope are retained.

Similar to clean_cov, but without dropping any reactions.

filter_by_isotopes(isotopes: Sequence[int]) → CrossSectionCovariance[source]

Return a new CrossSectionCovariance containing only matrices where BOTH row and column isotopes are in the specified list. Preserves cross-isotope blocks.

Parameters:

isotopes (Sequence[int]) – List of isotope ZAIDs to include

Returns:

Filtered CrossSectionCovariance containing only matrices involving the specified isotopes

Return type:

CrossSectionCovariance

filter_by_reactions(mts: Sequence[int], isotope: int | None = None) → CrossSectionCovariance[source]

Return a new CrossSectionCovariance containing only matrices whose row and column reactions are both in mts. When per-matrix energy_grids are available (e.g. ENDF MF33), the returned object has num_groups and energy_grid set from the diagonal block of the first requested MT so that all plotting methods work directly.

Parameters:
  • mts (sequence of int) – Reaction MT numbers to keep.

  • isotope (int, optional) – If given, also filter by this isotope.

fix_covariance(*, level: str = 'soft', high_val_thresh: float = 5.0, clamp_target: float = 1.0, accept_tol: float = -0.0001, clamp_max_iter: int = 10, max_steps: int = 40, verbose: bool = True, clamp_detail: bool = False, logger=None) → Tuple[CrossSectionCovariance, Dict[str, Any]][source]

Clean the covariance matrix until it is positive-(semi)definite.

Parameters:
  • level – ‘soft’ - clamp variances only ‘medium’ - clamp then drop the worst block pairs ‘hard’ - clamp then drop the worst reactions (all blocks)

  • high_val_thresh – Diagonal variances above this threshold are clamped.

  • clamp_target – Target variance assigned to clamped diagonals (off-diagonals rescaled by sqrt(target/|old|) to preserve correlations).

  • accept_tol – Minimum eigenvalue tolerated for acceptance.

  • clamp_max_iter – Maximum clamping passes before switching strategy.

  • max_steps – Maximum block-removal iterations (if used).

  • verbose – Forwarded to the removal routine.

  • clamp_detail – Dump every off-diagonal before/after pair on each clamp event. Very noisy — opt in only to debug a specific clamp.

  • logger – Optional logger instance for file output. If None, uses print().

classmethod from_boxer(file_path: str | Path, energy_unit: str = 'eV') → CrossSectionCovariance[source]

Create a CrossSectionCovariance instance from a BOXER card-image covariance file.

Parameters:
  • file_path (str or Path) – Path to the BOXER file

  • energy_unit (str, optional) – Energy unit for the energy grid: ‘eV’ (default) or ‘MeV’

Returns:

CrossSectionCovariance instance loaded from the file

Return type:

CrossSectionCovariance

See also

read_boxer

Underlying function that performs the parsing

classmethod from_covariance_section(section, nuclide: int = 0, mt: int | None = None, energy_unit: str = 'eV') → CrossSectionCovariance[source]

Create a CrossSectionCovariance from one GNDS §25.2 covariance section.

The model is treated as one more source format here, on the same footing as GENDF, COVFIL, BOXER and COVERX — which is what the model layer is for. The immediate reason it exists is MF35: its bands reach the model directly (decodeMF35MT) and never pass through this class, so before this there was no way to hand one to plot_covariance_heatmap. It is not MF35-specific: any section with a matrix and a row grid works, which includes MF31, MF33 and MF34.

One section, one matrix, on purpose. The bands of a single MF35 file have different orders (84, 641, 641, 641, 641 on ENDF/B-VIII.1 U-235) and all carry the same MT, so a whole suite loaded into one object would hold several matrices indistinguishable by the (nuclide, mt) key every selector here uses, and a plot would quietly draw the first. Loop over the sections and call this per band instead.

Parameters:
  • section – A CovarianceSection: anything exposing form.matrix, form.rowGrid and form.isRelative, with an optional rowData.ENDF_MFMT. Duck-typed rather than imported so that kika.cov keeps not depending on the model.

  • nuclide (int, optional) – ZAID to file the matrix under. The model identifies a covariance by its href rather than by a material header, so there is nothing to read this from and it is the caller’s to supply.

  • mt (int, optional) – Reaction number. Defaults to the MT half of rowData.ENDF_MFMT.

  • energy_unit (str, optional) – Unit of form.rowGrid. The model keeps ENDF’s native eV.

Returns:

A single-matrix instance, ready for plot_covariance_heatmap.

Return type:

CrossSectionCovariance

Examples

>>> suite, _ = decodeCovarianceSuite(endf)
>>> band = CrossSectionCovariance.from_covariance_section(
...     suite.covarianceSections[0], nuclide=92235)
>>> plot_covariance_heatmap(band, nuclide=92235, mt=18)
classmethod from_coverx(file_path: str | Path, ascending: bool = True, energy_unit: str = 'eV') → CrossSectionCovariance[source]

Create a CrossSectionCovariance instance from a COVERX covariance file (text or binary).

Parameters:
  • file_path (str or Path) – Path to the COVERX covariance file

  • ascending (bool, optional) – If True, energies ordered ascending (default True)

  • energy_unit (str, optional) – Energy unit for the energy grid: ‘eV’ (default) or ‘MeV’

Returns:

CrossSectionCovariance instance loaded from the file

Return type:

CrossSectionCovariance

See also

read_coverx

Underlying function that performs the parsing

classmethod from_covfil(file_path: str | Path, energy_unit: str = 'eV') → CrossSectionCovariance[source]

Create a CrossSectionCovariance instance from an NJOY-generated COVFIL/GENDF file.

Parameters:
  • file_path (str or Path) – Path to the COVFIL/GENDF covariance file

  • energy_unit (str, optional) – Energy unit for the energy grid: ‘eV’ (default) or ‘MeV’

Returns:

CrossSectionCovariance instance loaded from the file

Return type:

CrossSectionCovariance

Raises:

TypeError – If the file contains MF34 data instead of MF33

See also

read_covfil

Underlying function that performs the parsing

classmethod from_gendf(file_path: str | Path, energy_unit: str = 'eV') → CrossSectionCovariance[source]

Create a CrossSectionCovariance instance from an NJOY-generated GENDF covariance file.

This is a convenience class method that wraps the read_njoy_covmat function to provide a more object-oriented API consistent with other data classes.

Parameters:
  • file_path (str or Path) – Path to the GENDF covariance file

  • energy_unit (str, optional) – Energy unit for the energy grid: ‘eV’ (default) or ‘MeV’

Returns:

CrossSectionCovariance instance loaded from the file

Return type:

CrossSectionCovariance

Examples

>>> covmat = CrossSectionCovariance.from_gendf('path/to/file.gendf')
>>> print(f"Loaded {covmat.num_matrices} matrices")
>>> # Or specify MeV if the file uses MeV
>>> covmat_mev = CrossSectionCovariance.from_gendf('path/to/file.gendf', energy_unit='MeV')

See also

read_covfil

Underlying function that performs the parsing

get_cov_connections(nuclide: int | str, mt: int, *, depth: int = 1, require_nonzero: bool = True, tol: float = 0.0, max_neighbors: int | None = None) → Dict[str, Any][source]

Main method for your UI graph.

Returns a JSON-friendly payload with: - center node - nodes + edges within BFS depth - (if depth >= 2) a grouped mapping of second-level nodes per first-level neighbor,

ideal for your “mini nodes next to each neighbor”.

Parameters:
  • depth (int) – 1 -> show center + its neighbors 2 -> also include neighbors-of-neighbors

  • require_nonzero (bool) – True: only create edges if the covariance block has any |value| > tol False: edge exists if the block exists (even if all zeros)

  • tol (float) – Threshold for nonzero detection when require_nonzero=True

  • max_neighbors (Optional[int]) – If set, cap the number of neighbors expanded per node (safety for huge graphs)

get_uncertainty(zaid: int, mt: int, energy_mev: float | None = None) → float | ndarray[source]

Get relative uncertainty (standard deviation / mean) for a specific isotope and reaction.

The covariance matrices store relative covariances, so the square root of the diagonal elements directly gives relative uncertainties (as fractions).

Parameters:
  • zaid (int) – Isotope identifier (e.g., 26056 for Fe-56)

  • mt (int) – Reaction MT number

  • energy_mev (float, optional) – Specific energy in MeV. If provided, returns uncertainty at that energy. If None, returns array of uncertainties for all energy groups.

Returns:

Relative uncertainty (as fraction, e.g., 0.05 for 5%). If energy_mev is provided, returns a single float. If energy_mev is None, returns array of uncertainties for all groups.

Return type:

float or np.ndarray

Raises:

ValueError – If the specified (zaid, mt) pair is not found in the covariance data

property isotopes: Set[int]

Get the set of unique isotope IDs in the covariance matrices.

Returns:

Set of unique isotope IDs

Return type:

Set[int]

list_cov_nodes(*, require_nonzero: bool = False, tol: float = 0.0) → List[Dict[str, Any]][source]

Return all nodes (zaid, mt) known to the covariance store. If require_nonzero=True, nodes are restricted to those that participate in at least one non-zero block (or have a non-zero diagonal block).

property log_covariance_matrix: ndarray

Return the log-space covariance matrix.

Converts relative covariance to log-space using log1p transformation.

Returns:

Log-space covariance matrix

Return type:

np.ndarray

num_groups: int = 0
property num_matrices: int

Get the number of covariance matrices stored.

Returns:

Number of matrices

Return type:

int

plot_covariance_heatmap(nuclide: int | str, mt: int | Sequence[int] | Tuple[int, int], ax: Axes = None, *, matrix_type: str = 'corr', figsize: Tuple[float, float] = (6, 6), dpi: int = 300, font_family: str = 'serif', vmax: float = None, vmin: float = None, show_uncertainties: bool = True, scale: str = 'log', energy_range: Tuple[float, float] | None = None, title: str | None = 'default', **imshow_kwargs) → Figure[source]

Draw a covariance or correlation matrix heatmap for a specified isotope and MT reaction(s).

This method now uses the modern PlotBuilder-based implementation from kika.plotting.covariance for cleaner, more maintainable code.

Parameters:
  • nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)

  • mt (int, sequence of int, or tuple of (row_mt, col_mt)) – MT reaction number(s). Can be: - Single int: diagonal block for that MT - Sequence of ints: diagonal blocks for those MTs - Tuple of (row_mt, col_mt): off-diagonal block between row and column MT

  • ax (plt.Axes, optional) – Matplotlib axes to draw into (deprecated, kept for compatibility)

  • matrix_type (str, default "corr") – Type of matrix to plot: “corr”/”correlation” for correlation matrix, or “cov”/”covariance” for covariance matrix

  • figsize (tuple) – Figure size in inches (width, height)

  • dpi (int) – Dots per inch for figure resolution

  • font_family (str) – Font family for text elements

  • vmax (float, optional) – Color scale limits

  • vmin (float, optional) – Color scale limits

  • show_uncertainties (bool) – Whether to show uncertainty plots above the heatmap

  • scale (str, default "log") – Energy axis scale: “log”/”logarithmic” or “lin”/”linear”

  • energy_range (tuple of float, optional) – Energy range (min, max) for filtering. Values in eV.

  • title (str or None, default "default") – Plot title. If “default”, auto-generates from nuclide and MT. If a string, uses that as the title. If None, suppresses the title.

  • **imshow_kwargs – Additional arguments passed to imshow (deprecated)

Returns:

The matplotlib figure containing the heatmap and optional uncertainty plots

Return type:

plt.Figure

plot_multigroup_xs(nuclide: int | str | Sequence[int | str], mt: int | Sequence[int], ax: Axes = None, *, energy_range: Tuple[float, float] | None = None, show_uncertainties: bool = False, sigma: float = 1.0, style: str = 'default', figsize: Tuple[float, float] = (8, 5), dpi: int = 300, font_family: str = 'serif', legend_loc: str = 'best', xscale: str = 'log', yscale: str = 'linear', title: str | None = 'default', **step_kwargs) → Figure[source]

Plot multigroup cross sections with optional uncertainty bands.

This method now uses the modern PlotBuilder-based implementation from kika.plotting.covariance for cleaner, more maintainable code.

plot_uncertainties(nuclide: int | str | Sequence[int | str], mt: int | Sequence[int], *, energy_range: Tuple[float, float] | None = None, style: str = 'default', figsize: Tuple[float, float] = (8, 5), dpi: int = 300, font_family: str = 'serif', legend_loc: str = 'best', xscale: str = 'log', yscale: str = 'linear', title: str | None = 'default', **step_kwargs) → Figure[source]

Plot relative uncertainties for one or more (ZAID, MT) pairs.

This method now uses the modern PlotBuilder-based implementation from kika.plotting.covariance for cleaner, more maintainable code.

project_to_grid(target_bin_edges_ev: ndarray, *, xs_source: Any | None = None, target_mt: int | None = None, target_isotope: int | None = None) → ndarray[source]

Project a self-self covariance entry onto a target bin-edge grid.

Selects the matrix where reaction_row == reaction_col == target_mt (defaulting to the most common MT in the covariance) and projects every matching self-self contribution onto the target grid via kika.processing.multigroup (piecewise-constant overlap).

Parameters:
  • target_bin_edges_ev (np.ndarray) – Target bin edges in eV (length N+1, strictly increasing).

  • xs_source (MF3MT or CrossSection, optional) – Pointwise σ(E) used to convert any relative-form matrices to absolute. Required when at least one matched entry has is_relative=True; otherwise that entry is skipped with a warning.

  • target_mt (int, optional) – MT to look up. Defaults to the first reaction_rows value.

  • target_isotope (int, optional) – Isotope (ZA) to look up. Defaults to the first isotope_rows value.

Returns:

Absolute covariance (b²), shape (N, N), symmetric.

Return type:

np.ndarray

Notes

PSD enforcement is the caller’s responsibility. Use kika.cov.decomposition.cholesky_decomposition downstream when a positive-definite factor is needed.

property reactions: Set[int]

Get the set of unique reaction MT numbers in the covariance matrices.

Returns:

Set of unique reaction MT numbers

Return type:

Set[int]

reactions_by_isotope(isotope: int | None = None) → Dict[int, List[int]] | List[int][source]

Get a mapping of isotopes to their available reactions, or list of reactions for a specific isotope.

Parameters:

isotope (Optional[int]) – If provided, return reactions only for this isotope.

Returns:

Mapping from isotope IDs to sorted lists of MT numbers, or list of MT numbers for the specified isotope.

Return type:

Dict[int, List[int]] or List[int]

remove_matrix(isotope: int, reaction_pairs: List[Tuple[int, int]], exceptions: List[Tuple[int, int]] | None = None) → CrossSectionCovariance[source]

Return a new CrossSectionCovariance without the specified matrices for a given isotope, but always keep any pairs listed in exceptions. Also removes cross section entries for diagonal or wildcard reactions that are removed.

report_large_values(threshold: float = 1.0, top_n: int = 30, return_text: bool = False) → Tuple[str, dict] | None[source]

Scan each block of the relative covariance matrices and generate a detailed report of entries that exceed the specified threshold.

If return_text is True, returns a tuple: (report_text, summary_dict) where summary_dict contains: - zaid: the main isotope ZAID - name: human-readable symbol - count: number of entries > threshold - max_value: largest flagged value

sanitize_by_correlation(*, max_abs_corr: float = 1.0, zero_threshold: float = 1.5, report_tol: float = 1e-06, eigen_floor: float = 1e-12, project_psd: bool = True, psd_method: str = 'auto', verbose: bool = True)[source]

Clip out-of-range correlations, zero out huge outliers, inspect eigenvalues, and (optionally) project to PSD.

Parameters:
  • max_abs_corr – Any correlation with |ρ| > max_abs_corr (but ≤ zero_threshold) is clipped to ±max_abs_corr.

  • zero_threshold – Any |ρ| > zero_threshold is set to 0.

  • report_tol – Minimum |old–new| change to actually log.

  • eigen_floor – Floor for eigenvalues when projecting to PSD.

  • project_psd – Whether to do the PSD projection here.

  • verbose – Print detailed diagnostics.

svd_decomposition(*, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, full_matrices: bool = False, logger=None) → Tuple[ndarray, ndarray, ndarray][source]

SVD with PSD pre-processing.

to_boxer(file_path: str | Path, hlibid: str = '', hdescr: str = '', nvf: int = 10) → None[source]

Write this CrossSectionCovariance to a BOXER card-image (ASCII) file.

Parameters:
  • file_path (str or Path) – Output file path

  • hlibid (str, optional) – Library identifier (3 chars max)

  • hdescr (str, optional) – Description (32 chars max)

  • nvf (int, optional) – Value format code (7-14). Default 10 (1P8E10.3).

to_coverx(file_path: str | Path, fmt: str = 'binary', title: str = '') → None[source]

Write this CrossSectionCovariance to a COVERX covariance file (text or binary).

Parameters:
  • file_path (str or Path) – Output file path

  • fmt (str, optional) – 'binary' (default) or 'text'

  • title (str, optional) – File title / description

to_covfil(file_path: str | Path, tape_label: str = '', temperature: float = 0.0) → None[source]

Write this CrossSectionCovariance to an NJOY COVFIL/GENDF text file.

Parameters:
  • file_path (str or Path) – Output file path

  • tape_label (str, optional) – Label for the tape header line (max 66 chars)

  • temperature (float, optional) – Temperature in K for MF1 MT451 CONT record

to_dataframe() → DataFrame[source]

Convert the covariance matrix data to a pandas DataFrame.

Includes an extra row at the beginning to store the energy grid if available.

Returns:

DataFrame containing the covariance matrix data with columns: ISO_H, REAC_H, ISO_V, REAC_V, STD

Return type:

pd.DataFrame

to_heatmap_data(nuclide: int | str | Sequence[int | str], mt: int | Sequence[int] | Tuple[int, int], *, matrix_type: str = 'corr', scale: str = 'log', energy_range: Tuple[float, float] | None = None, **kwargs) → CovarianceHeatmapData[source]

Prepare covariance heatmap data for PlotBuilder rendering.

This method extracts the relevant matrix data, computes uncertainties, and packages everything into a CovarianceHeatmapData object that can be rendered by PlotBuilder.add_heatmap().

Parameters:
  • nuclide (int, str, or sequence of int/str) – Isotope identifier(s). Can be: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’) - List of ZAIDs or strings for multi-isotope heatmaps (e.g., [‘Fe54’, ‘Fe56’])

  • mt (int, sequence of int, or tuple of (row_mt, col_mt)) – MT reaction number(s). Can be: - Single int: diagonal block for that MT - Sequence of ints: diagonal blocks for those MTs - Tuple of (row_mt, col_mt): off-diagonal block between row and column MT

  • matrix_type (str, default 'corr') – Type of matrix: ‘corr’/’correlation’ for correlation matrix, or ‘cov’/’covariance’ for covariance matrix

  • scale (str, default 'log') – Energy axis scale: ‘log’/’logarithmic’ or ‘lin’/’linear’

  • energy_range (tuple of float, optional) – Energy window (emin, emax). Only bins overlapping the window are kept.

  • **kwargs – Additional parameters (reserved for future use)

Returns:

Heatmap data object ready for PlotBuilder.add_heatmap()

Return type:

CovarianceHeatmapData

Examples

>>> # Simple usage with PlotBuilder
>>> from kika.plotting import PlotBuilder
>>> heatmap_data = covmat.to_heatmap_data(nuclide=92235, mt=[2, 18, 102])
>>> fig = PlotBuilder(style='light').add_heatmap(heatmap_data)
>>> fig.show()
>>> # Can also use string symbols
>>> heatmap_data = covmat.to_heatmap_data(nuclide='U235', mt=2, matrix_type='cov')
>>> # Multi-isotope heatmap
>>> heatmap_data = covmat.to_heatmap_data(nuclide=['Fe54', 'Fe56'], mt=[2, 18])
to_plot_data(nuclide: int | str, mt: int, sigma: float = 1.0, label: str = None, **styling_kwargs)[source]

Create PlotData objects for multigroup cross sections with uncertainties.

This unified method extracts both nominal cross section data and uncertainty data. Both are returned as PlotData objects that can be plotted independently or combined.

Parameters:
  • nuclide (int or str) – Isotope identifier. Can be either: - Integer ZAID (e.g., 92235 for U-235) - Element-mass string (e.g., ‘U235’, ‘Fe56’)

  • mt (int) – Reaction MT number

  • sigma (float, optional) – Number of sigma levels for uncertainty bands (default: 1.0 for 1σ).

  • label (str, optional) – Custom label for the plot. If None, auto-generates from ZAID and MT.

  • **styling_kwargs – Additional styling kwargs (color, linestyle, linewidth, etc.)

Returns:

  • xs_data: Cross section data for plotting (or None if not available)

  • unc_data: Uncertainty data as percentages (or None if not available)

Return type:

tuple of (MultigroupCrossSectionPlotData, MultigroupUncertaintyPlotData)

Raises:

ValueError – If the specified (nuclide, mt) pair is not found in either cross sections or covariance data

Examples

>>> # Extract data - both integer ZAID and string notation work
>>> covmat = read_njoy_covmat('file.gendf')
>>> xs_data, unc_data = covmat.to_plot_data(nuclide=26056, mt=2)
>>> xs_data, unc_data = covmat.to_plot_data(nuclide='Fe56', mt=2)
>>>
>>> # Use with PlotBuilder:
>>> from kika.plotting import PlotBuilder
>>>
>>> # Option 1: Plot just cross sections
>>> fig1 = PlotBuilder().add_data(xs_data).build()
>>>
>>> # Option 2: Plot cross sections with uncertainty shading
>>> fig2 = PlotBuilder().add_data(xs_data, uncertainty=unc_data).build()
>>>
>>> # Option 3: Plot just uncertainties as a line
>>> fig3 = PlotBuilder().add_data(unc_data).build()
transfer_reactions(source: CrossSectionCovariance, reactions: Sequence[Tuple[int, int]], *, cross_correlation: str = 'both-present', grid_atol: float = 1e-06, grid_rtol: float = 1e-06, replace: bool = True) → TransferResult[source]

Copy selected reaction blocks from source into a copy of self.

self is the destination and is never mutated; a new CrossSectionCovariance is returned inside the TransferResult.

Parameters:
  • source (CrossSectionCovariance) – The covariance to take blocks from.

  • reactions (sequence of (isotope_zaid, mt)) – Reactions to transfer. For each, the diagonal self-block and the associated cross-section vector are copied, plus off-diagonal cross-correlation blocks according to cross_correlation.

  • cross_correlation ({'both-present', 'diagonal-only', 'always'}) –

    How to handle off-diagonal blocks of a transferred reaction:

    • 'both-present' (default): copy a cross block only when both partner reactions exist in the destination after the transfer; otherwise record it in cross_dropped.

    • 'diagonal-only': never copy cross blocks.

    • 'always': copy every cross block the reaction participates in, even if the partner reaction is absent (may leave dangling refs).

  • grid_atol (float) – Tolerances for the energy-grid equality check (after eV/MeV normalisation).

  • grid_rtol (float) – Tolerances for the energy-grid equality check (after eV/MeV normalisation).

  • replace (bool) – If True, a transferred reaction that already exists in the destination has its existing blocks/cross-section removed first.

Returns:

The merged covariance plus a report of what was transferred/dropped.

Return type:

TransferResult

Raises:

ValueError – If the group structures are incompatible, or cross_correlation is not a recognised mode.

verify_correlation(atol: float = 1e-12, rtol: float = 0.0001) → None[source]

Run basic checks on the correlation matrix and print any problems.

Checks: 1. Symmetry: ρ_ij == ρ_ji within atol. 2. Diagonal consistency: 1 if variance > 0, else 0. 3. Range: off‑diagonal in [‑1, 1].

energy_grids: List[List[float]]
isotope_rows: List[int]
reaction_rows: List[int]
isotope_cols: List[int]
reaction_cols: List[int]
matrices: List[ndarray]
is_relative: List[bool]
cross_sections: Dict[Tuple[int, int], ndarray]
metadata: Dict[str, Any]
kika.cov.CovMat

alias of CrossSectionCovariance

class kika.cov.TransferResult(covariance: ~kika.cov.cross_section_covariance.CrossSectionCovariance, diagonal_transferred: ~typing.List[~typing.Tuple[int, int]] = <factory>, cross_transferred: ~typing.List[~typing.Tuple[int, int, int, int]] = <factory>, cross_dropped: ~typing.List[~typing.Tuple[~typing.Tuple[int, int, int, int], str]] = <factory>, reactions_replaced: ~typing.List[~typing.Tuple[int, int]] = <factory>, cross_sections_transferred: ~typing.List[~typing.Tuple[int, int]] = <factory>, missing_in_source: ~typing.List[~typing.Tuple[int, int]] = <factory>)[source]

Bases: object

Outcome of CrossSectionCovariance.transfer_reactions().

covariance

The merged covariance (a new object; inputs are not mutated).

Type:

CrossSectionCovariance

diagonal_transferred

Self-blocks copied from the source.

Type:

list of (isotope, mt)

cross_transferred

Off-diagonal cross-correlation blocks copied from the source.

Type:

list of (iso_row, mt_row, iso_col, mt_col)

cross_dropped

Cross blocks that were not copied, with the reason.

Type:

list of ((iso_row, mt_row, iso_col, mt_col), reason)

reactions_replaced

Destination reactions whose existing blocks were removed before re-adding.

Type:

list of (isotope, mt)

cross_sections_transferred

Reactions whose associated cross-section vector was copied.

Type:

list of (isotope, mt)

missing_in_source

Requested reactions not found in the source.

Type:

list of (isotope, mt)

covariance: CrossSectionCovariance
diagonal_transferred: List[Tuple[int, int]]
cross_transferred: List[Tuple[int, int, int, int]]
cross_dropped: List[Tuple[Tuple[int, int, int, int], str]]
reactions_replaced: List[Tuple[int, int]]
cross_sections_transferred: List[Tuple[int, int]]
missing_in_source: List[Tuple[int, int]]
kika.cov.nearest_psd_higham(A: ndarray, *, preserve_diagonal: bool = True, max_iter: int | None = None, tol: float | None = None, eigval_floor: float = 0.0, verbose: bool = True, logger=None, log_every: int = 50) → Tuple[ndarray, Dict][source]

Find the nearest positive semi-definite matrix using Higham’s alternating projection algorithm with Dykstra’s correction.

When preserve_diagonal=True the algorithm iterates between projecting onto the PSD cone and restoring the original diagonal (variances), converging to the nearest PSD matrix that keeps all variances unchanged.

Reference: N.J. Higham, “Computing a nearest symmetric positive semidefinite matrix”, Linear Algebra and its Applications, 1988.

Parameters:
  • A (np.ndarray) – Input symmetric matrix (need not be PSD).

  • preserve_diagonal (bool) – If True, run the full Dykstra iteration that preserves the diagonal. If False, perform a single eigenvalue-clipping projection.

  • max_iter (int, optional) – Maximum number of alternating-projection iterations. None (default) auto-selects: 1000 for n ≤ 2000, 200 for n > 2000. Sparse multigroup covariances (n > 2000, ~95 % non-finite from below-threshold reactions) plateau by iter ~100 and the cholesky path then falls back to clip — running 1000 iterations just burns wall-time without improving the projection.

  • tol (float, optional) – Convergence tolerance on relative Frobenius change between iterations. None (default) auto-selects: 1e-10 for matrices ≤ 1000×1000, 1e-8 for larger ones — at n ≳ 1000, rel_change ≲ 1e-8 is already at float-precision noise from the O(n³) eigendecomposition, so a stricter tol just guarantees no convergence.

  • eigval_floor (float) – Floor for eigenvalues in the PSD projection step (0.0 for exact PSD, small positive for strict PD).

  • verbose (bool) – Whether to print diagnostic information.

  • logger (optional) – Logger instance for file output.

  • log_every (int) – Emit a progress line every N iterations (set to 0 to disable). Used to track convergence on long runs where each iteration is an O(n³) eigendecomposition.

Returns:

(X_psd, info) where info contains diagnostic metadata: - iterations, converged, frobenius_distance, relative_frobenius_error,

max_diagonal_change, eigenvalue_range_before, eigenvalue_range_after, n_negative_eigenvalues_before

Return type:

Tuple[np.ndarray, dict]

kika.cov.verify_psd_projection(original_matrix: ndarray, projected_matrix: ndarray, info: Dict, verbose: bool = True, logger=None) → Tuple[float, float, float][source]

Verify PSD projection quality by comparing original and projected matrices.

Reports Frobenius distance, maximum diagonal change, and maximum correlation change between the original and projected matrices.

Parameters:
  • original_matrix (np.ndarray) – Original (possibly non-PSD) covariance matrix.

  • projected_matrix (np.ndarray) – PSD-projected matrix.

  • info (dict) – Info dict returned by nearest_psd_higham().

  • verbose (bool) – Whether to log results.

  • logger (optional) – Logger instance for file output.

Returns:

(relative_frobenius_error_pct, max_diagonal_change_pct, max_correlation_change)

Return type:

Tuple[float, float, float]

Submodules

kika.cov.parse_covmat

exception kika.cov.parse_covmat.EmptyParsingError[source]

Bases: Exception

Raised when no data was extracted during parsing.

exception kika.cov.parse_covmat.InvalidDataFormatError[source]

Bases: Exception

Raised when the data format is invalid or corrupted.

kika.cov.parse_covmat.read_coverx(file_path: str, ascending: bool = True, energy_unit: str = 'eV') → CrossSectionCovariance[source]

Read a COVERX covariance file (text or binary) and return a CrossSectionCovariance object.

The format is auto-detected: if the first bytes are printable ASCII the file is treated as text; otherwise it is parsed as a Fortran unformatted binary COVERX file.

Parameters:
  • file_path (str) – Path to the COVERX covariance file

  • ascending (bool, optional) – If True, energies are reordered in ascending order (default True)

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'

Returns:

Parsed covariance data

Return type:

CrossSectionCovariance

Raises:
  • EmptyParsingError – If no valid covariance matrices were found in the file

  • InvalidDataFormatError – If the binary structure is corrupted

kika.cov.parse_covmat.write_coverx(data: CrossSectionCovariance, file_path: str, fmt: str = 'binary', title: str = '', endian: str = '>') → None[source]

Write a CrossSectionCovariance to a COVERX covariance file (text or binary).

Parameters:
  • data (CrossSectionCovariance) – Covariance data to write.

  • file_path (str) – Output file path.

  • fmt (str, optional) – 'binary' (default) or 'text'.

  • title (str, optional) – File title / description.

  • endian (str, optional) – Endianness for binary format: '>' big-endian (default).

kika.cov.parse_covmat.read_boxer(file_path: str, energy_unit: str = 'eV') → CrossSectionCovariance[source]

Read a BOXER card-image (ASCII) covariance file and return a CrossSectionCovariance.

BOXER is produced by NJOY’s COVR module. It stores energy boundaries, cross-section vectors, standard-deviation vectors, and covariance or correlation matrices in a compressed card-image format.

Parameters:
  • file_path (str) – Path to the BOXER file.

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'.

Returns:

Parsed covariance data.

Return type:

CrossSectionCovariance

Raises:

EmptyParsingError – If no valid covariance matrices were found.

kika.cov.parse_covmat.write_boxer(data: CrossSectionCovariance, file_path: str, hlibid: str = '', hdescr: str = '', nvf: int = 10) → None[source]

Write a CrossSectionCovariance to a BOXER card-image (ASCII) file.

Parameters:
  • data (CrossSectionCovariance) – Covariance data to write.

  • file_path (str) – Output file path.

  • hlibid (str, optional) – Library identifier (3 chars max). Default ''.

  • hdescr (str, optional) – Description (32 chars max). Default ''.

  • nvf (int, optional) – Value format code (7–14). Default 10 (1P8E10.3).

kika.cov.parse_covmat.read_covfil(file_path: str, energy_unit: str = 'eV') → CrossSectionCovariance | LegendreCovariance[source]

Parse an NJOY-generated COVFIL/GENDF covariance file.

Returns a CrossSectionCovariance for MF33 files (cross-section covariances) or an LegendreCovariance for MF34 files (angular-distribution covariances).

MF3 cross sections are stored in CrossSectionCovariance.cross_sections (MF33 case only).

Parameters:
  • file_path (str) – Path to the NJOY-generated covariance file.

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'.

Returns:

Parsed covariance data.

Return type:

CrossSectionCovariance or LegendreCovariance

Raises:

EmptyParsingError – If no valid covariance matrices were found.

kika.cov.parse_covmat.write_covfil(data: CrossSectionCovariance | LegendreCovariance, file_path: str, tape_label: str = '', temperature: float | None = None, awr: float | None = None) → None[source]

Write a CrossSectionCovariance or LegendreCovariance to an NJOY COVFIL/GENDF text file.

Parameters:
  • data (CrossSectionCovariance or LegendreCovariance) – Covariance data to write.

  • file_path (str) – Output file path.

  • tape_label (str, optional) – Label for the tape header line (max 66 chars). Default ''.

  • temperature (float, optional) – Temperature in K written into MF1 MT451 CONT record. If None (default), uses data.metadata['temperature'] (set by read_covfil()), falling back to 0.0.

  • awr (float, optional) – Atomic weight ratio written into MF1 MT451 HEAD and each MF33/MF34 section HEAD. If None (default), uses data.metadata['awr'] (set by read_covfil()), falling back to 0.0.

kika.cov.parse_covmat.read_scale_covmat(file_path: str, ascending: bool = True, energy_unit: str = 'eV') → CrossSectionCovariance

Read a COVERX covariance file (text or binary) and return a CrossSectionCovariance object.

The format is auto-detected: if the first bytes are printable ASCII the file is treated as text; otherwise it is parsed as a Fortran unformatted binary COVERX file.

Parameters:
  • file_path (str) – Path to the COVERX covariance file

  • ascending (bool, optional) – If True, energies are reordered in ascending order (default True)

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'

Returns:

Parsed covariance data

Return type:

CrossSectionCovariance

Raises:
  • EmptyParsingError – If no valid covariance matrices were found in the file

  • InvalidDataFormatError – If the binary structure is corrupted

kika.cov.parse_covmat.read_njoy_covmat(file_path: str, energy_unit: str = 'eV') → CrossSectionCovariance | LegendreCovariance

Parse an NJOY-generated COVFIL/GENDF covariance file.

Returns a CrossSectionCovariance for MF33 files (cross-section covariances) or an LegendreCovariance for MF34 files (angular-distribution covariances).

MF3 cross sections are stored in CrossSectionCovariance.cross_sections (MF33 case only).

Parameters:
  • file_path (str) – Path to the NJOY-generated covariance file.

  • energy_unit (str, optional) – Energy unit for the energy grid: 'eV' (default) or 'MeV'.

Returns:

Parsed covariance data.

Return type:

CrossSectionCovariance or LegendreCovariance

Raises:

EmptyParsingError – If no valid covariance matrices were found.

kika.cov.covmat

Backward-compatibility shim — real code lives in cross_section_covariance.py.

kika.cov.decomposition

Shared matrix decomposition methods for covariance matrix classes.

This module provides decomposition functionality that can be used by both CrossSectionCovariance and LegendreCovariance classes without code duplication.

class kika.cov.decomposition.CovarianceMatrixProtocol(*args, **kwargs)[source]

Bases: Protocol

Protocol defining the interface required for decomposition methods.

property covariance_matrix: ndarray

Return the linear-space covariance matrix.

property log_covariance_matrix: ndarray

Return the log-space covariance matrix.

kika.cov.decomposition.clip_projection(matrix: ndarray, *, floor: float = 0.0, symmetrise: bool = True, passthrough_when_clean: bool = False, eigvals: ndarray | None = None, eigvecs: ndarray | None = None, verbose: bool = True, logger=None, label: str = 'clip') → Tuple[ndarray, Dict][source]

V·max(Λ, floor)·Vᵀ — the PSD projection, written down once.

Four copies of these three lines were in the tree: two inside nearest_psd_higham(), one inside clip_and_rescale(), one inside svd_decomposition(), and a fifth in kika.cov.conditioning._clipped(). Consolidating them turned up something worth stating rather than quietly averaging away: they were not the same projection. They differ in three ways, each of which is load-bearing at the last bits, so each is a parameter here and every call site keeps exactly the arithmetic it had.

  • floor. Higham and clip_rescale clip to eigval_floor (a small positive number), the sampling paths clip to 0.0. A strictly positive floor makes the result positive definite rather than semi-definite, which is what a Cholesky downstream needs and what an SVD does not.

  • symmetrise. (V·Λ·Vᵀ + ·ᵀ)/2 is not a no-op: the product is mathematically symmetric and numerically is not, and Higham’s inner loop deliberately skips the fold-up because Dykstra’s correction is taken on the raw projection.

  • passthrough_when_clean. svd_decomposition leaves a matrix with no negative eigenvalue untouched rather than rebuilding it from its own eigendecomposition. That is what makes psd_method="clip" a true no-op on an already-PSD matrix, and it is what kika.cov.conditioning.apply_plan() has to reproduce for a conditioned matrix drawn with psd_method="none" to be bit-identical to the same matrix drawn with psd_method="clip".

A @ np.diag(w) and A * w[None, :] are bit-identical for finite A — the gemm adds exact zeros — so the two spellings the copies used are not a fourth difference, and the faster one is used here.

Parameters:
  • eigvals – A decomposition of matrix already in hand, reused rather than recomputed. Callers that need the eigendecomposition for their own reasons — psd_method="auto", which resolves on the spectrum, and Higham’s loop, which counts SVD fallbacks — pass it in.

  • eigvecs – A decomposition of matrix already in hand, reused rather than recomputed. Callers that need the eigendecomposition for their own reasons — psd_method="auto", which resolves on the spectrum, and Higham’s loop, which counts SVD fallbacks — pass it in.

Returns:

info carries n_negative, min_eigenvalue and clipped (False when passthrough_when_clean returned the input unchanged).

Return type:

(matrix, info)

kika.cov.decomposition.cap_variance_congruence(cov_mat: ndarray, max_variance: float, *, param_pairs=None, num_groups: int = 0, bins=None, verbose: bool = True, logger=None, label: str = '') → Tuple[ndarray, Dict][source]

Cap diagonal variances via congruence transform, preserving correlations and PSD.

For each diagonal entry σ²_i > max_variance, compute a scale factor s_i = sqrt(max_variance / σ²_i) and apply the one-shot congruence transform Σ_capped = diag(s) @ Σ @ diag(s).

Parameters:
  • cov_mat (np.ndarray) – Covariance matrix (modified in-place is NOT done; a copy is returned).

  • max_variance (float) – Maximum allowed diagonal variance.

  • param_pairs (list of (zaid, mt) tuples, optional) – For logging which parameters were capped.

  • num_groups (int) – Number of energy groups per reaction (for index→group mapping).

  • bins (array-like, optional) – Energy bin edges (for logging energy ranges).

  • verbose (logging controls.)

  • logger (logging controls.)

  • label (logging controls.)

Returns:

  • cov_capped (np.ndarray) – Capped covariance matrix.

  • info (dict) – n_capped, capped_entries list, max_original_variance.

kika.cov.decomposition.flag_threshold_bins(cov_mat: ndarray, mt_thresholds: Dict[int, float], param_pairs: Sequence[Tuple[int, int]], num_groups: int, bins: Sequence[float], *, space: str = 'log', min_groups_for_median: int = 3, verbose: bool = True, logger=None, label: str = '') → Tuple[List[int], List[float], List[Dict]][source]

Identify threshold-spanning bins from per-MT reaction thresholds (e.g., extracted from ACE σ(E)) and compute a per-bin replacement variance.

For each (zaid, mt) entry in param_pairs whose MT appears in mt_thresholds, locate the unique group g such that bins[g] <= E_thresh < bins[g+1]. That bin straddles the reaction threshold and is the canonical NJOY artifact location: ⟨σ⟩ is pulled down by the below-threshold tail, blowing up the relative uncertainty.

The replacement target for that bin’s diagonal variance is the median of the diagonal over the same MT’s other groups (finite, positive). Returns indices and targets only — actual rescaling is performed by rescale_threshold_bins_congruence.

Parameters:
  • cov_mat ((n,n) np.ndarray) – Covariance matrix (linear or log space; matching space).

  • mt_thresholds (dict) – {mt: E_threshold} in the same energy units as bins (MeV here).

  • param_pairs (list of (zaid, mt) tuples) – Maps row/col blocks to (isotope, reaction).

  • num_groups (int) – Energy groups per (zaid, mt) block.

  • bins (array-like) – Group boundaries, length num_groups + 1.

  • space ({"log", "linear"}) – Only used in the log lines (interpretation of σ²).

  • min_groups_for_median (int) – Skip a flagged bin when fewer than this many same-MT neighbors are usable; the bin remains for the global cap to handle.

Returns:

  • flagged_indices (list of int) – Flat covariance indices for bins flagged as threshold-spanning.

  • targets (list of float) – Target variance per flagged index (one-to-one with flagged_indices).

  • detection_log (list of dict) – Per-flagged-bin detail (zaid, mt, group, energy_lo, energy_hi, E_thresh, current_variance, target_variance, n_groups_used).

kika.cov.decomposition.rescale_threshold_bins_congruence(cov_mat: ndarray, flagged_indices: Sequence[int], targets: Sequence[float], *, param_pairs=None, num_groups: int = 0, bins=None, verbose: bool = True, logger=None, label: str = '') → Tuple[ndarray, Dict][source]

Rescale specific diagonal entries to per-index target variances via a congruence transform. One-sided: never inflates a variance.

For each flagged index i with target t_i:
s_i = sqrt(t_i / σ²_i) if σ²_i > t_i

1.0 otherwise

Then Σ' = diag(s) @ Σ @ diag(s). PSD-preserving, correlation-preserving.

This complements cap_variance_congruence (which applies a single global cap): use this for targeted rescaling of bins identified as artifacts (e.g. NJOY threshold-spanning bins flagged by flag_threshold_bins).

Parameters:
  • cov_mat ((n,n) np.ndarray) – Covariance matrix. A copy is returned; input is not modified.

  • flagged_indices (sequence of int) – Flat covariance indices to (potentially) rescale.

  • targets (sequence of float) – Target variance for each flagged index, one-to-one with flagged_indices.

  • param_pairs (optional) – For per-entry logging (zaid/mt/group/energy ranges).

  • num_groups (optional) – For per-entry logging (zaid/mt/group/energy ranges).

  • bins (optional) – For per-entry logging (zaid/mt/group/energy ranges).

  • verbose (logging controls.)

  • logger (logging controls.)

  • label (logging controls.)

Returns:

  • cov_rescaled (np.ndarray) – Rescaled covariance.

  • info (dict) – n_flagged, n_actually_rescaled, rescaled_entries (list of dicts with index, zaid, mt, group, energy_lo, energy_hi, original_variance, target_variance, scale_factor).

kika.cov.decomposition.flag_outlier_variance_bins(cov_mat: ndarray, param_pairs: Sequence[Tuple[int, int]], num_groups: int, bins: Sequence[float], *, outlier_factor: float = 1000.0, min_groups_for_median: int = 4, skip_indices: Sequence[int] | None = None, space: str = 'log', verbose: bool = True, logger=None, label: str = '') → Tuple[List[int], List[float], List[Dict]][source]

Flag diagonal entries σ²_i > outlier_factor × per-MT median(σ²) as statistical outliers. Target = per-MT median.

Catches evaluator-written placeholder/filler variances that aren’t near a reaction threshold (so flag_threshold_bins won’t catch them) and would otherwise saturate the global variance cap. Concrete failure mode: JEFF-4.0 Mn-55 MT=856 (lumped n,α ladder) carries σ²_rel ~ 10⁹ in bins above ~22 MeV where σ̄ collapses; the data is parsed as already-relative so the σ̄-floor in the abs→rel conversion never sees it.

Same return signature as flag_threshold_bins so rescale_threshold_bins_congruence consumes the output unchanged.

outlier_factor is intentionally large (default 1000) so only physically-impossible outliers are caught — bins at 5–10× the median represent legitimate heterogeneity in measurement quality and are left alone.

Parameters:
  • cov_mat ((n,n) np.ndarray)

  • param_pairs (list of (zaid, mt) tuples)

  • num_groups (int)

  • bins (array-like, length num_groups + 1)

  • outlier_factor (float) – Bins with σ² > outlier_factor × per-MT median are flagged.

  • min_groups_for_median (int) – Minimum same-MT bins (excluding the flagged one) required to compute the median target. If fewer, the bin is not flagged.

  • skip_indices (sequence of int, optional) – Flat indices to exclude from outlier consideration entirely (typically those already rescaled by flag_threshold_bins).

  • space (same as flag_threshold_bins.)

  • verbose (same as flag_threshold_bins.)

  • logger (same as flag_threshold_bins.)

  • label (same as flag_threshold_bins.)

kika.cov.decomposition.nearest_psd_higham(A: ndarray, *, preserve_diagonal: bool = True, max_iter: int | None = None, tol: float | None = None, eigval_floor: float = 0.0, verbose: bool = True, logger=None, log_every: int = 50) → Tuple[ndarray, Dict][source]

Find the nearest positive semi-definite matrix using Higham’s alternating projection algorithm with Dykstra’s correction.

When preserve_diagonal=True the algorithm iterates between projecting onto the PSD cone and restoring the original diagonal (variances), converging to the nearest PSD matrix that keeps all variances unchanged.

Reference: N.J. Higham, “Computing a nearest symmetric positive semidefinite matrix”, Linear Algebra and its Applications, 1988.

Parameters:
  • A (np.ndarray) – Input symmetric matrix (need not be PSD).

  • preserve_diagonal (bool) – If True, run the full Dykstra iteration that preserves the diagonal. If False, perform a single eigenvalue-clipping projection.

  • max_iter (int, optional) – Maximum number of alternating-projection iterations. None (default) auto-selects: 1000 for n ≤ 2000, 200 for n > 2000. Sparse multigroup covariances (n > 2000, ~95 % non-finite from below-threshold reactions) plateau by iter ~100 and the cholesky path then falls back to clip — running 1000 iterations just burns wall-time without improving the projection.

  • tol (float, optional) – Convergence tolerance on relative Frobenius change between iterations. None (default) auto-selects: 1e-10 for matrices ≤ 1000×1000, 1e-8 for larger ones — at n ≳ 1000, rel_change ≲ 1e-8 is already at float-precision noise from the O(n³) eigendecomposition, so a stricter tol just guarantees no convergence.

  • eigval_floor (float) – Floor for eigenvalues in the PSD projection step (0.0 for exact PSD, small positive for strict PD).

  • verbose (bool) – Whether to print diagnostic information.

  • logger (optional) – Logger instance for file output.

  • log_every (int) – Emit a progress line every N iterations (set to 0 to disable). Used to track convergence on long runs where each iteration is an O(n³) eigendecomposition.

Returns:

(X_psd, info) where info contains diagnostic metadata: - iterations, converged, frobenius_distance, relative_frobenius_error,

max_diagonal_change, eigenvalue_range_before, eigenvalue_range_after, n_negative_eigenvalues_before

Return type:

Tuple[np.ndarray, dict]

kika.cov.decomposition.clip_and_rescale(M: ndarray, *, eigval_floor: float = 0.0, verbose: bool = False, logger=None) → Tuple[ndarray, Dict[str, Any]][source]

Clip negative eigenvalues, then put the stated diagonal back.

The gap this closes. clip rebuilds V·clip(Λ,0)·Vᵀ, which preserves the eigenvectors — the reason it is the right projection for these matrices, since the near-null direction carries a sum rule — but not the diagonal. Every clipped negative eigenvalue adds variance, spread over the components in proportion to its eigenvector, so a component the evaluation gives almost no variance comes out of the projection with some. Downstream that is not cosmetic: PFNS divides each drawn delta by its group probability, and the lowest groups of a fission spectrum hold ~1e-17 of the total, so a negligible absolute gain becomes a large perturbation ratio. The standing workaround is a 5σ clamp in generate_pfns_samples.

Higham is the third option and it is not the answer either, though the reason first written here was wrong. “Did not finish four 122×122 bands in two minutes” was contention on a loaded box: re-measured quiet, one band converges in ~1900 iterations and ~13 s. Three numbers rule it out instead.

  • It preserves the diagonal in absolute norm — the post-loop eigenvalue clip below runs without restoring it, leaving max_diagonal_change at 1.4e-12 against a largest stated variance of 7.4e-5. Negligible, and the code says so. But the same 1.4e-12 is 0.55% of the smallest stated variances, and on a covariance whose diagonal spans decades that is the sense that matters. clip_rescale is exact in both senses.

  • It degrades C·1 by ~40x (4.2e-6 → 1.7e-4) where clip improves it.

  • It costs ~10⁴x clip.

Middling on both axes for four orders of magnitude more time, so clip stays on the PFNS path. kika.cov.conditioning.predict_psd_repairs() measures all three on any given matrix, which is how these numbers were got.

The fix is one line of algebra. With d = sqrt(diag(C₀)/diag(C_clip)), the matrix diag(d)·C_clip·diag(d) has exactly diag(C₀) on its diagonal, is a congruence transform of a PSD matrix and therefore still PSD, and costs O(n²) against Higham’s iterations. It does not preserve the eigenvectors exactly — nothing that changes the diagonal can — but it rescales rather than rotates, so a component with zero stated variance gets an identically zero row and column, which is stronger than what clip gives it.

Measured, and it does not do what it was proposed for. On the four Cf-252 MF35 bands the diagonal comes back exactly (max error 1.6e-8 → 1.4e-20) and the result stays PSD (λ_min ≈ -5e-20). But the sum rule does not survive it: the row-sum residual max|Σ_j C_ij| / max|C| goes from 4.2e-6 to 2.2e-3, some 500x worse, on every band.

That is structural rather than a tolerance to tune. C·1 ≈ 0 says the all-ones vector is a near-null eigenvector, and clipping keeps it null precisely because it preserves eigenvectors. The congruence gives (D C D)·1 = D C (D 1), which is zero only when d is constant — so restoring a non-uniform diagonal and preserving the sum rule are, for these matrices, the same choice made two ways.

The 5σ clamp in generate_pfns_samples therefore stays: the rescale roughly halves the draws that reach it (172→113, 220→121, 274→127, 277→124 over the four bands at 64 samples) and removes none of them. Use this where the marginals matter and no sum rule does — MF33 and MF34 — and not on a normalised spectrum.

Not reachable from psd_method="auto": this is a different numerical answer, and the pipelines already running on clip must not change underneath because a new option was added. Ask for it by name.

Returns:

info carries n_negative, min_eigenvalue, max_diagonal_error_before and max_diagonal_error_after, so a caller can assert the restoration actually happened.

Return type:

(matrix, info)

kika.cov.decomposition.cholesky_decomposition(cov_obj: CovarianceMatrixProtocol = None, *, space: str = 'log', psd_method: str = 'auto', jitter_scale: float = 1e-10, max_jitter_ratio: float = 0.001, verbose: bool = True, logger=None, matrix: ndarray = None) → ndarray[source]

Robust Cholesky factor L such that M ≈ L L^T.

Parameters:
  • cov_obj (CovarianceMatrixProtocol, optional) – Object containing covariance matrix data.

  • space (str) – “linear” or “log” space for decomposition

  • psd_method (str) –

    PSD enforcement method when the matrix is not already PD: - “auto” (default): one eigendecomposition; if |λ_min|/λ_max is

    below the module threshold use clip, otherwise Higham.

    • ”higham”: iterative diagonal-preserving projection.

    • ”clip”: zero negative eigenvalues, reconstruct, then Cholesky.

    • ”none”: add diagonal jitter until Cholesky succeeds.

  • jitter_scale (float) – Base jitter scale for PSD correction

  • max_jitter_ratio (float) – Maximum jitter relative to matrix norm

  • verbose (bool) – Whether to log progress

  • logger (optional) – Logger instance for output

  • matrix (np.ndarray, optional) – If provided, decompose this matrix directly instead of extracting from cov_obj. Useful when the matrix has been pre-processed (e.g. variance-capped).

Returns:

Lower triangular Cholesky factor L

Return type:

np.ndarray

kika.cov.decomposition.eigen_decomposition(cov_obj: CovarianceMatrixProtocol = None, *, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, logger=None, matrix: ndarray = None) → Tuple[ndarray, ndarray][source]

Eigendecomposition with PSD correction.

Parameters:
  • cov_obj (CovarianceMatrixProtocol, optional) – Object containing covariance matrix data

  • space (str) – “linear” or “log” space for decomposition

  • clip_negatives (bool) – Deprecated. Use psd_method instead. Kept for backward compatibility.

  • psd_method (str or None) – PSD correction method: “auto” (default), “higham”, “clip”, or “none”. If None, resolved from clip_negatives: True → “auto”, False → “none”. “auto” picks clip when |λ_min|/λ_max is below the module threshold, otherwise Higham.

  • verbose (bool) – Whether to log progress

  • logger (optional) – Logger instance for output

  • matrix (np.ndarray, optional) – If provided, decompose this matrix directly instead of extracting from cov_obj.

Returns:

Eigenvalues and eigenvectors

Return type:

Tuple[np.ndarray, np.ndarray]

kika.cov.decomposition.svd_decomposition(cov_obj: CovarianceMatrixProtocol = None, *, space: str = 'log', clip_negatives: bool = True, psd_method: str | None = None, verbose: bool = True, full_matrices: bool = False, logger=None, matrix: ndarray = None) → Tuple[ndarray, ndarray, ndarray][source]

SVD with PSD pre-processing.

Parameters:
  • cov_obj (CovarianceMatrixProtocol, optional) – Object containing covariance matrix data

  • space (str) – “linear” or “log” space for decomposition

  • clip_negatives (bool) – Deprecated. Use psd_method instead. Kept for backward compatibility.

  • psd_method (str or None) – PSD correction method: “auto” (default), “higham”, “clip”, or “none”. If None, resolved from clip_negatives: True → “auto”, False → “none”. “auto” picks clip when |λ_min|/λ_max is below the module threshold, otherwise Higham.

  • verbose (bool) – Whether to log progress

  • full_matrices (bool) – Whether to return full-sized U and V matrices

  • logger (optional) – Logger instance for output

  • matrix (np.ndarray, optional) – If provided, decompose this matrix directly instead of extracting from cov_obj.

Returns:

U, singular values, V^T matrices

Return type:

Tuple[np.ndarray, np.ndarray, np.ndarray]

kika.cov.decomposition.compute_correlation(cov_obj: CovarianceMatrixProtocol, *, clip: bool = False, force_diagonal: bool = True) → ndarray[source]

Compute correlation matrix from covariance matrix.

Parameters:
  • cov_obj (CovarianceMatrixProtocol) – Object containing covariance matrix data

  • clip (bool) – Whether to clip correlations to [-1, 1] range

  • force_diagonal (bool) – Whether to force diagonal elements to 1.0

Returns:

Correlation matrix with optional clipping and diagonal forcing

Return type:

np.ndarray

kika.cov.decomposition.verify_cholesky_decomposition(original_matrix: ndarray, L: ndarray, space: str, verbose: bool = True, logger=None) → Tuple[float, float, float][source]

Verify Cholesky decomposition quality by reconstructing L @ L.T.

Parameters:
  • original_matrix (np.ndarray) – Original covariance matrix

  • L (np.ndarray) – Cholesky factor (lower triangular)

  • space (str) – Space (“linear” or “log”) for logging

  • verbose (bool) – Whether to log results

  • logger (optional) – Logger instance for output

Returns:

Relative Frobenius error (%), max diagonal error (%), max off-diagonal error (%)

Return type:

Tuple[float, float, float]

kika.cov.decomposition.verify_eigen_decomposition(original_matrix: ndarray, eigvals: ndarray, eigvecs: ndarray, space: str, verbose: bool = True, logger=None) → Tuple[float, float, float][source]

Verify eigendecomposition quality by reconstructing V @ Λ @ V.T.

Parameters:
  • original_matrix (np.ndarray) – Original covariance matrix

  • eigvals (np.ndarray) – Eigenvalues

  • eigvecs (np.ndarray) – Eigenvectors

  • space (str) – Space (“linear” or “log”) for logging

  • verbose (bool) – Whether to log results

  • logger (optional) – Logger instance for output

Returns:

Relative Frobenius error (%), max diagonal error (%), max off-diagonal error (%)

Return type:

Tuple[float, float, float]

kika.cov.decomposition.verify_svd_decomposition(original_matrix: ndarray, U: ndarray, S: ndarray, Vt: ndarray, space: str, verbose: bool = True, logger=None) → Tuple[float, float, float][source]

Verify SVD quality by reconstructing U @ Σ @ V.T.

Parameters:
  • original_matrix (np.ndarray) – Original covariance matrix

  • U (np.ndarray) – Left singular vectors

  • S (np.ndarray) – Singular values

  • Vt (np.ndarray) – Right singular vectors (transposed)

  • space (str) – Space (“linear” or “log”) for logging

  • verbose (bool) – Whether to log results

  • logger (optional) – Logger instance for output

Returns:

Relative Frobenius error (%), max diagonal error (%), max off-diagonal error (%)

Return type:

Tuple[float, float, float]

kika.cov.decomposition.verify_pca_decomposition(original_matrix: ndarray, eigvals: ndarray, eigvecs: ndarray, k: int, space: str, verbose: bool = True, logger=None) → Tuple[float, float, float][source]

Verify PCA decomposition quality by reconstructing the truncated approximation.

Parameters:
  • original_matrix (np.ndarray) – Original covariance matrix

  • eigvals (np.ndarray) – All eigenvalues (sorted descending)

  • eigvecs (np.ndarray) – All eigenvectors (sorted by descending eigenvalue)

  • k (int) – Number of components kept in truncation

  • space (str) – Space (“linear” or “log”) for logging

  • verbose (bool) – Whether to log results

  • logger (optional) – Logger instance for output

Returns:

Relative Frobenius error (%), max diagonal error (%), max off-diagonal error (%)

Return type:

Tuple[float, float, float]

kika.cov.decomposition.verify_psd_projection(original_matrix: ndarray, projected_matrix: ndarray, info: Dict, verbose: bool = True, logger=None) → Tuple[float, float, float][source]

Verify PSD projection quality by comparing original and projected matrices.

Reports Frobenius distance, maximum diagonal change, and maximum correlation change between the original and projected matrices.

Parameters:
  • original_matrix (np.ndarray) – Original (possibly non-PSD) covariance matrix.

  • projected_matrix (np.ndarray) – PSD-projected matrix.

  • info (dict) – Info dict returned by nearest_psd_higham().

  • verbose (bool) – Whether to log results.

  • logger (optional) – Logger instance for file output.

Returns:

(relative_frobenius_error_pct, max_diagonal_change_pct, max_correlation_change)

Return type:

Tuple[float, float, float]