Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
b62a948
feat: Add point cloud explainer
HamedDaneshvar Aug 21, 2026
6419871
fix(explainer): isolate legacy random state to resolve NPY002 and pre…
HamedDaneshvar Aug 22, 2026
89cb2d2
feat(plots): add visualization functions for point cloud data and exp…
HamedDaneshvar Aug 22, 2026
ec2f2cd
feat(metrics): Add stability and jaccard score for point cloud
HamedDaneshvar Aug 22, 2026
17e465d
fix(metrics): fix support clustering mode for noisy ponit cloud expla…
HamedDaneshvar Aug 22, 2026
55c526f
feat(explainer): support huggingface point cloud models using custom …
HamedDaneshvar Sep 12, 2026
4c0ce6b
feat(explainer): add mask mode for cosine distance type and fix compu…
HamedDaneshvar Sep 12, 2026
5cc3d7d
Merge branch 'main' of https://github.com/HamedDaneshvar/xwhy
HamedDaneshvar Sep 12, 2026
d288851
test(point cloud): Add unit tests for point cloud explainer code
HamedDaneshvar Sep 13, 2026
afa7267
refactor: remove extra and unnessecary files
HamedDaneshvar Sep 13, 2026
0b291a7
feat(surrogate): Add some common parameter for surrogate to all expla…
HamedDaneshvar Sep 14, 2026
6e63edd
feat(providers): Add retry mechanism parameters for providers into ex…
HamedDaneshvar Sep 14, 2026
bb03145
refactor(explainers): Add some parameters for images explainers
HamedDaneshvar Sep 14, 2026
c2ed5fe
refactor(explainers): Add sanitize distance and normalization method …
HamedDaneshvar Sep 14, 2026
cce39bf
refactor: refactor most common config parameters into parent config
HamedDaneshvar Sep 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/xwhy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
ImageGenerationAndEditingExplainer,
)
from xwhy.explainers.llm import LLMExplainer
from xwhy.explainers.pointcloud import PointCloudExplainer
from xwhy.explainers.point_cloud import PointCloudExplainer
from xwhy.explainers.tabular import TabularExplainer
from xwhy.explainers.text import TextExplainer

Expand Down
1 change: 0 additions & 1 deletion src/xwhy/adapters/__init__.py

This file was deleted.

15 changes: 0 additions & 15 deletions src/xwhy/adapters/base.py

This file was deleted.

2 changes: 0 additions & 2 deletions src/xwhy/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@
)
from xwhy.core.exceptions import XWhyError
from xwhy.core.explainer import BaseExplainer
from xwhy.core.pipeline import ExplanationPipeline
from xwhy.core.result import BaseXWhyResult

__all__ = [
"BaseExplainer",
"BaseXWhyResult",
"ExplainerConfig",
"ExplanationPipeline",
"ImageClassificationConfig",
"LLMConfig",
"TabularConfig",
Expand Down
70 changes: 44 additions & 26 deletions src/xwhy/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@
class ExplainerConfig(BaseModel):
"""Explainer config."""

pass
seed: int = 42
epsilon: float = Field(default=0.01, ge=0.0)
kernel_width: float = Field(default=0.5, gt=0.0)
ridge_alpha: float = Field(default=1.0, ge=0.0)

num_perturbations: int = Field(default=50, gt=0)
surrogate_type: SurrogateType | str = SurrogateType.LIME
use_best_surrogate: bool = True


class LLMConfig(ExplainerConfig):
Expand All @@ -35,11 +42,11 @@ class LLMConfig(ExplainerConfig):
model_name: str = "gpt-3.5-turbo-instruct"
max_tokens: int = Field(default=200, gt=0)
temperature: float = Field(default=0.0, ge=0.0, le=2.0)
seed: int = 42
num_perturbations: int = Field(default=64, gt=0)
max_retries: int = Field(default=7, ge=0)
delay: float | None = Field(default=None, ge=0.0)
normalization_method: Literal["linear", "inverse"] = "linear"
embedding_type: EmbeddingType | str = EmbeddingType.WORD2VEC
surrogate_type: SurrogateType | str = SurrogateType.LIME
use_best_surrogate: bool = True
sanitize_distances: bool = False


class ImageClassificationConfig(ExplainerConfig):
Expand All @@ -64,20 +71,19 @@ class ImageClassificationConfig(ExplainerConfig):
custom_preprocess: Callable[..., Any] | None = None
categories: Any = None

class_of_interest: int = 1

use_segmentation_model: bool = True
segmentation_type: SegmentationType | str = SegmentationType.DEEPLABV3_RESNET101
device: str = "cpu" # or "cuda"

seed: int = 42

kernel_size: int = Field(default=4, ge=1)
max_dist: int = Field(default=200, gt=0)
ratio: float = Field(default=0.2, gt=0.0, le=1.0)
num_perturb: int = Field(default=150, gt=0)

keep_probability: float = Field(default=0.5, gt=0.0, le=1.0)

distance_type: DistanceType | str = DistanceType.WASSERSTEIN
surrogate_type: SurrogateType | str = SurrogateType.LIME
use_best_surrogate: bool = True

num_top_features: int = Field(default=4, gt=0)
num_top_predictions: int = Field(default=5, gt=0)
Expand All @@ -94,16 +100,10 @@ class TabularConfig(ExplainerConfig):
)

mode: Literal["classification", "regression"] = "classification"
num_perturbations: int = Field(default=500, gt=0)
kernel_width: float = Field(default=0.2, gt=0.0)
num_distribution_samples: int = Field(default=100, gt=0)
local_noise: float = Field(default=0.05, ge=0.0)
perturbation_noise: float = Field(default=0.4, ge=0.0)
epsilon: float = Field(default=0.01, gt=0.0)
distance_type: DistanceType | str = DistanceType.WASSERSTEIN
surrogate_type: SurrogateType | str = SurrogateType.LIME
use_best_surrogate: bool = True
seed: int = 42
device: str = "cpu"
validate_normalization: bool = True

Expand All @@ -123,14 +123,15 @@ class ImageGenerationAndEditingConfig(ExplainerConfig):
provider_type: ProviderType | str | None = Field(default=ProviderType.OPENAI)
engine_type: Literal["provider", "custom", "pipeline"] = "provider"
model_name: str = "dall-e-3"
max_retries: int = Field(default=7, ge=0)
delay: float | None = Field(default=None, ge=0.0)

# Custom Model Injection
custom_model: Any = None
custom_generate_fn: Callable[..., Any] | None = None

# Core Shared Generation Parameters
temperature: float = Field(default=0.0, ge=0.0, le=2.0)
seed: int = 42

# Explainer Components
use_image_embedding_model: bool = False
Expand All @@ -143,15 +144,11 @@ class ImageGenerationAndEditingConfig(ExplainerConfig):
# Core Explainability Settings
output_dir: str = "outputs"
device: str = "cpu" # or "cuda"
num_perturbations: int = Field(default=64, gt=0)
normalization_method: Literal["linear", "inverse"] = "linear"
distance_type: DistanceType | str = DistanceType.WASSERSTEIN
surrogate_type: SurrogateType | str = SurrogateType.LIME
use_best_surrogate: bool = True

# Surrogate & Perturbation Fine-tuning Parameters
normalization_mode: Literal["linear", "inverse"] = "linear"
kernel_width: float = Field(default=0.25, gt=0.0)
ridge_alpha: float = Field(default=1.0, ge=0.0)


class TextConfig(ExplainerConfig):
Expand All @@ -167,8 +164,29 @@ class TextConfig(ExplainerConfig):

model: Any = None
predict_fn: Callable[..., Any] | None = None
seed: int = 42
num_perturbations: int = Field(default=64, gt=0)
embedding_type: EmbeddingType | str = EmbeddingType.WORD2VEC
surrogate_type: SurrogateType | str = SurrogateType.LIME
use_best_surrogate: bool = True
sanitize_distances: bool = True


class PointCloudConfig(ExplainerConfig):
"""Configuration for the Point Cloud explainer."""

model_config = ConfigDict(
frozen=True,
extra="forbid",
validate_assignment=True,
str_strip_whitespace=True,
)

custom_model: Any | None = None
custom_predict_fn: Callable[..., Any] | None = None

num_clusters: int = Field(default=8, gt=0)
num_top_features: int = Field(default=4, gt=0)
removal_probability: float = Field(default=0.3, ge=0.0, le=1.0)
max_iters: int = Field(default=50, gt=0)
device: str = "cpu"

clustering_mode: Literal["kmeans", "precomputed"] = "kmeans"
distance_type: DistanceType | str = DistanceType.WASSERSTEIN
distance_mode: Literal["mask", "spatial", "latent"] = "mask"
4 changes: 0 additions & 4 deletions src/xwhy/core/contracts.py

This file was deleted.

18 changes: 0 additions & 18 deletions src/xwhy/core/pipeline.py

This file was deleted.

26 changes: 26 additions & 0 deletions src/xwhy/core/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,29 @@ def data(self) -> np.ndarray:
if self.instance is not None
else np.array([])
)


@dataclass
class PointCloudXWhyResult(BaseXWhyResult):
"""Container for point cloud explanation results.

Attributes:
important_clusters: Array of important cluster indices.
sample_points: Original point cloud input tensor/array.
cluster_labels: Cluster labels assigned to each point.

"""

important_clusters: np.ndarray = field(default_factory=lambda: np.zeros(0))
sample_points: np.ndarray = field(default_factory=lambda: np.zeros(0))
cluster_labels: np.ndarray = field(default_factory=lambda: np.zeros(0))

@property
def feature_names(self) -> Sequence[str]:
"""Sequence of cluster names corresponding to features."""
return [f"Cluster {i}" for i in range(len(self.coefficients))]

@property
def data(self) -> np.ndarray:
"""The underlying point cloud points array."""
return self.sample_points
128 changes: 128 additions & 0 deletions src/xwhy/core/states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""Run time states."""

from __future__ import annotations

from collections.abc import Callable, Sequence
from typing import Any

import numpy as np
import torch

from xwhy.core.types import BaseImageGenerationAndEditing
from xwhy.models.classification.base import BaseClassification
from xwhy.models.embeddings.base import BaseEmbedding
from xwhy.models.point_cloud.base import BasePointCloudModel
from xwhy.models.segmentation.base import BaseSegmentation
from xwhy.perturbation.image import ImagePerturbation
from xwhy.perturbation.point_cloud import PointCloudPerturbation
from xwhy.perturbation.text import TextPerturbation
from xwhy.providers.base import BaseProvider


class LLMState:
"""Runtime state for the LLM explainer."""

def __init__(self) -> None:
"""Initialize the runtime state.

This object stores runtime resources that are created during the
explainer lifecycle. Unlike the configuration, these values are
mutable and are populated as models and providers are initialized.
"""
self.provider: BaseProvider | None = None
self.perturbator: TextPerturbation | None = None
self.embedding_model: BaseEmbedding | None = None


class ImageClassificationState:
"""Runtime state for the Image Classification explainer."""

def __init__(self, device_: torch.device) -> None:
"""Initialize the runtime state.

This object stores runtime resources that are created during the
explainer lifecycle. Unlike the configuration, these values are
mutable and are populated as models are loaded.

Args:
device_: Torch device used to load and run all models.

"""
self.device = device_
self.perturbator: ImagePerturbation | None = None

self.classification_model: BaseClassification | None = None
self.transform_fn: Callable[..., Any] | None = None

self.segmentation_model: BaseSegmentation | None = None

self.embedding_model: BaseEmbedding | None = None


class TabularState:
"""Runtime state for the Tabular explainer."""

def __init__(self) -> None:
"""Initialize the runtime state.

This object stores the loaded predictive model to prevent redundant
initializations across multiple explanation requests.
"""
self.model: Any | None = None


class ImageGenerationAndEditingState:
"""Runtime state for the Image Generation and Editing explainer."""

def __init__(self, device_: torch.device) -> None:
"""Initialize the runtime state.

This object stores runtime resources that are created during the
explainer lifecycle. Unlike the configuration, these values are
mutable and are populated as models and providers are initialized.

Args:
device_: Torch device used to load and run all models.

"""
self.device = device_

# Unified Generation/Editing Resource
self.engine: BaseImageGenerationAndEditing | None = None

# Explainability Resources
self.text_perturbator: TextPerturbation | None = None
self.segmentation_model: BaseSegmentation | None = None
self.image_embedding_model: BaseEmbedding | None = None
self.text_embedding_model: BaseEmbedding | None = None


class TextState:
"""Runtime state for the Text explainer."""

def __init__(self) -> None:
"""Initialize the runtime state.

This object stores runtime resources created during the explainer
lifecycle, including models, prediction callables, perturbators, and
embeddings.
"""
self.model: Any = None
self.predict_fn: Callable[[Sequence[str]], np.ndarray] | None = None
self.perturbator: TextPerturbation | None = None
self.embedding_model: BaseEmbedding | None = None


class PointCloudState:
"""Runtime state for the Point Cloud explainer."""

def __init__(self, device_: torch.device) -> None:
"""Initialize the runtime state.

Args:
device_: Torch device used for running point cloud models.

"""
self.device = device_
self.model: BasePointCloudModel | None = None
self.perturbation: PointCloudPerturbation | None = None
Loading
Loading