Source code for qca.engines.csqca
"""Crisp-set QCA engine."""
from __future__ import annotations
from collections.abc import Sequence
import pandas as pd
from qca.engines._helpers import (
normalize_engine_condition_types,
resolve_engine_conditions,
)
from qca.engines.base import QCAEngineBase
[docs]
class CSQCA(QCAEngineBase):
"""First-class csQCA engine for crisp binary set conditions."""
qca_type = "csQCA"
def __init__(
self,
data: pd.DataFrame,
outcome: str,
conditions: Sequence[str] | None = None,
case_id: str | None = None,
condition_types: dict[str, str] | None = None,
set_conditions: Sequence[str] | None = None,
) -> None:
resolved_conditions = resolve_engine_conditions(
engine_name="CSQCA",
conditions=conditions,
legacy_conditions=set_conditions,
legacy_name="set_conditions",
)
if condition_types is None:
normalized_types = {condition: "crisp" for condition in resolved_conditions}
else:
normalized_types = normalize_engine_condition_types(
engine_name="CSQCA",
condition_types=condition_types,
allowed_types={"crisp"},
)
super().__init__(
data=data,
case_id=case_id,
outcome=outcome,
conditions=resolved_conditions,
condition_types=normalized_types,
)
def _validate(self) -> None:
super()._validate()
self._validate_crisp_schema()
self._validate_crisp_columns([*self.conditions, self.outcome])
def _validate_crisp_schema(self) -> None:
non_crisp = [spec.name for spec in self.condition_specs if spec.kind != "crisp"]
if non_crisp:
raise ValueError(
f"CSQCA requires crisp condition specs. Non-crisp: {non_crisp}"
)
if self.multivalue_conditions:
raise ValueError(
"CSQCA does not support multi-value conditions. "
f"Got {self.multivalue_conditions}."
)
def _validate_crisp_columns(self, columns: Sequence[str]) -> None:
for column in columns:
try:
values = (
pd.to_numeric(self.data[column], errors="raise")
.dropna()
.astype(float)
)
except (TypeError, ValueError) as exc:
raise ValueError(
f"CSQCA requires crisp/binary values in {column!r}."
) from exc
if not values.isin([0.0, 1.0]).all():
raise ValueError(f"CSQCA requires crisp/binary values in {column!r}.")
def __repr__(self) -> str:
return (
f"CSQCA("
f"n_cases={self.n_cases}, "
f"conditions={self.conditions}, "
f"outcome={self.outcome!r}"
f")"
)
__all__ = ["CSQCA"]