Source code for synthpriv.core.base
"""Base contracts for synthetic data generators.
All synthpriv generators implement ``BaseSynthesizer``.
The minimal interface is ``fit`` + ``sample``, so callers
(pipeline, CLI, REST) are agnostic to the concrete algorithm.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any
import pandas as pd
from synthpriv.utils import check_fitted
[docs]
class BaseSynthesizer(ABC):
"""Common interface for any synthetic data generator.
Parameters
----------
metadata:
Optional metadata (e.g. SDV ``SingleTableMetadata`` or a dict).
If ``None``, the generator infers it in ``fit``.
"""
name: str = "base"
description: str = ""
def __init__(self, metadata=None, **kwargs): # noqa: B027
self.metadata = metadata
self._model = None
self._fitted = False
@property
def fitted(self) -> bool:
"""True if ``fit`` completed successfully."""
return self._fitted
@property
def model(self):
"""Trained underlying model (implementation-specific)."""
return self._model
[docs]
@abstractmethod
def fit(self, data: pd.DataFrame) -> "BaseSynthesizer":
"""Train the generator on the real data."""
[docs]
@abstractmethod
def sample(self, num_rows: int = 1000, **kwargs) -> pd.DataFrame:
"""Generate ``num_rows`` synthetic records."""
[docs]
def fit_and_sample(self, data: pd.DataFrame, num_rows: int = 1000, **kwargs) -> pd.DataFrame:
"""Shortcut: ``fit`` + ``sample`` in a single step."""
self.fit(data)
return self.sample(num_rows=num_rows, **kwargs)
[docs]
def get_params(self) -> dict[str, Any]:
"""Reproducible configuration parameters (for save/resample)."""
return {}
# ------------------------------------------------------------------
# persistence
# ------------------------------------------------------------------
[docs]
def save(self, path: str | Path) -> Path:
"""Persist the trained generator to ``path`` (implementation-defined format)."""
raise NotImplementedError(f"{self.__class__.__name__} does not implement save()")
[docs]
@classmethod
def load(cls, path: str | Path) -> "BaseSynthesizer":
"""Rebuild a trained generator from ``path``."""
raise NotImplementedError(f"{cls.__name__} does not implement load()")
def __repr__(self) -> str: # pragma: no cover - debugging aid
return f"<{self.__class__.__name__} name={self.name!r} fitted={self.fitted}>"