Every Model.fit(data) call in rscopulas returns a FitResult[ModelT] dataclass. The result holds both the fitted model object and a FitDiagnostics instance that lets you evaluate fit quality and check convergence before using the model.

FitResult

FitResult is a generic, frozen dataclass parameterized by the model type.

@dataclass(frozen=True, slots=True)
class FitResult(Generic[ModelT]):
    model: ModelT
    diagnostics: FitDiagnostics
model
ModelT

The fitted copula model. The concrete type matches the class you called fit on—for example, GaussianCopula.fit(data) produces a FitResult[GaussianCopula], so result.model is a GaussianCopula.

Response
diagnostics
FitDiagnostics

Fit quality metrics and convergence status. See FitDiagnostics below.

Response

FitDiagnostics

FitDiagnostics is a frozen dataclass that reports numerical summaries of the fitting process.

@dataclass(frozen=True, slots=True)
class FitDiagnostics:
    loglik: float
    aic: float
    bic: float
    converged: bool
    n_iter: int
loglik
float

Log-likelihood evaluated at the fitted parameters. Higher values (closer to zero) indicate a better fit to the data.

Response
aic
float

Akaike Information Criterion: aic = 2k - 2 * loglik, where k is the number of free parameters. Lower AIC indicates a better balance of fit quality and model complexity.

Response
bic
float

Bayesian Information Criterion: bic = k * log(n) - 2 * loglik, where n is the number of observations. BIC penalizes complexity more heavily than AIC for large datasets.

Response
converged
bool

True when the optimizer reached its convergence criterion before max_iter. A False value does not mean the result is unusable, but you should inspect the parameter estimates and log-likelihood before relying on the model.

Response
n_iter
int

Number of optimizer iterations completed. Useful for diagnosing slow or non-converging fits.

Response

Model comparison with AIC and BIC

When you fit multiple families to the same dataset, use AIC or BIC to select the best model. Both criteria prefer the model with the lower value.

import numpy as np
from rscopulas import GaussianCopula, StudentTCopula, ClaytonCopula

rng = np.random.default_rng(0)
data = rng.uniform(0.01, 0.99, size=(500, 2))

results = {
    "gaussian": GaussianCopula.fit(data),
    "student_t": StudentTCopula.fit(data),
    "clayton": ClaytonCopula.fit(data),
}

best = min(results, key=lambda name: results[name].diagnostics.aic)
print("Best family by AIC:", best)

for name, result in results.items():
    d = result.diagnostics
    print(f"{name}: loglik={d.loglik:.2f}, AIC={d.aic:.2f}, BIC={d.bic:.2f}")

Accessing all fields

import numpy as np
from rscopulas import GaussianCopula

rng = np.random.default_rng(42)
data = rng.uniform(0.01, 0.99, size=(400, 3))

result = GaussianCopula.fit(data)

# Fitted model
model = result.model
print("correlation:\n", model.correlation)

# Diagnostics
d = result.diagnostics
print("log-likelihood:", d.loglik)
print("AIC:", d.aic)
print("BIC:", d.bic)
print("converged:", d.converged)
print("iterations:", d.n_iter)

# Downstream use
log_densities = model.log_pdf(data)
new_samples = model.sample(100, seed=0)

Error handling

Fitting can raise several exceptions. You should always catch at least InvalidInputError and ModelFitError in production code.

import numpy as np
from rscopulas import GaussianCopula, InvalidInputError, ModelFitError

data = np.array([[0.1, 0.2], [0.5, 0.6], [0.9, 0.8]], dtype=np.float64)

try:
    result = GaussianCopula.fit(data)
    if not result.diagnostics.converged:
        print(f"Warning: did not converge after {result.diagnostics.n_iter} iterations")
    print("AIC:", result.diagnostics.aic)
except InvalidInputError as e:
    print("Invalid input:", e)
except ModelFitError as e:
    print("Fit failed:", e)

Error types

ExceptionWhen raised
RscopulasErrorBase class for all library exceptions. Catch this to handle any rscopulas error.
InvalidInputErrorInput data fails validation: wrong dtype, wrong shape, values outside (0, 1), or an invalid parameter value.
ModelFitErrorThe optimization algorithm fails to find a valid solution, e.g. a singular correlation matrix.
NumericalErrorA numerical operation (log, inversion, etc.) produces an invalid result such as NaN or Inf.
BackendErrorAn internal error in the Rust backend that does not fall into the categories above.
InternalErrorAn internal Rust panic was caught at the Python boundary. This indicates a bug in rscopulas and should be reported upstream.