Marginal DRO

class dro.src.linear_model.marginal_dro.MarginalCVaRDRO(input_dim, model_type='svm', fit_intercept=True, solver='MOSEK', kernel='linear', alpha=1.0, L=10.0, p=2)

Bases: BaseLinearDRO

Marginal-X Conditional Value-at-Risk (Conditional-CVaR) Distributionally Robust Optimization (DRO) model that only allow likelihood ratio changes in X.

This model minimizes a robust loss function for both regression and classification tasks under a CVaR constraint only for the marginal distribution of X.

Reference: [1] <https://pubsonline.informs.org/doi/10.1287/opre.2022.2363> [2] The specific model follows Equation (27) in: https://arxiv.org/pdf/2007.13982.pdf with parameters L and p.

Parameters:
  • input_dim (int) – Dimension of input features. Must be ≥ 1.

  • model_type (str) –

    Base model architecture. Supported:

    • 'svm': Hinge loss (classification)

    • 'logistic': Logistic loss (classification)

    • 'ols': Least squares (regression)

    • 'lad': Least absolute deviation (regression)

  • fit_intercept (bool) – Whether to learn intercept term \(b\). Set False for pre-centered data. Defaults to True.

  • solver (str) – Convex optimization solver. Defaults to ‘MOSEK’.

  • kernel (str) – the kernel type to be used in the optimization model, default = ‘linear’

  • alpha (float) – CVaR risk level controlling tail expectation. Must satisfy 0 < α ≤ 1. Defaults to 1.0.

  • L (float) – Wasserstein radius scaling factor. Larger values increase distributional robustness. Must satisfy L ≥ 0. Defaults to 10.0.

  • p (int) – Order of Wasserstein distance. Supported values: 1 (Earth Mover’s Distance) or 2 (Quadratic Wasserstein). Defaults to 2.

Raises:

ValueError

  • If input_dim < 1

  • If alpha ∉ (0, 1]

  • If L < 0

  • If p < 1

Example:
>>> model = MarginalCVaRDRO(
...     input_dim=5, 
...     model_type='lad',
...     alpha=0.95,
...     L=5.0,
...     p=1
... )
>>> model.L  # 5.0
>>> model.p  # 1
update(config)

Update Marginal CVaR-DRO model configuration parameters.

Dynamically adjusts the robustness parameters for marginal distribution shifts. Preserves existing solutions until next fit() call.

Parameters:

config (Dict[str, Any]) –

Configuration dictionary with optional keys:

  • control_name (List[int]):

    Indices of features to protect against marginal shifts. Constraints:

    • All indices must satisfy \(0 \leq \text{index} < \text{input_dim}\)

    • Empty list disables marginal robustness

  • L (float):

    Wasserstein radius scaling factor. Must satisfy \(L > 0\) Larger values increase conservativeness

  • p (int):

    Wasserstein metric order. Must satisfy \(p \geq 1\) Supported values: 1 (EMD), 2 (Quadratic)

  • alpha (float):

    CVaR risk level. Must satisfy \(0 < \alpha \leq 1\) \(\alpha \to 0\) focuses on average loss, \(\alpha = 1\) is worst-case

Raises:

MarginalCVaRDROError

  • If control_name contains invalid indices

  • If L is non-positive

  • If p < 1

  • If alpha ∉ (0, 1]

  • If config contains unsupported keys

Return type:

None

Example:
>>> model = MarginalCVaRDRO(input_dim=5, control_name=[1,3])
>>> model.update({
...     "L": 15.0,      
...     "alpha": 0.95  
... })
>>> model.L  # 15.0
fit(X, y)

Solve the Marginal CVaR-DRO problem via convex optimization.

Constructs and solves the following distributionally robust optimization problem:

Parameters:
  • X (numpy.ndarray) – Training feature matrix of shape (n_samples, n_features). Must satisfy n_features == self.input_dim.

  • y (numpy.ndarray) –

    Target vector of shape (n_samples,). Format requirements depend on model_type:

    • Classification (svm/logistic):

      • Binary labels in {-1, +1}

      • No missing values allowed

    • Regression** (ols/lad):

      • Continuous real values

      • May contain NaN

Returns:

Solution dictionary containing:

  • theta: Weight vector of shape (n_features,)

  • b: Intercept term (exists if fit_intercept=True)

  • B: Marginal robustness dual matrix of shape (n_samples, n_samples)

  • threshold: CVaR threshold value

Return type:

Dict[str, Any]

Raises:

MarginalCVaRDROError

  • If X.shape[1] != self.input_dim

  • If X.shape[0] != y.shape[0]

  • If optimization fails (problem infeasible/solver error)

  • If control_name indices exceed feature dimensions

Example:
>>> model = MarginalCVaRDRO(input_dim=3, control_name=[0,2], L=5.0)
>>> X = np.random.randn(100, 3)
>>> y = np.sign(np.random.randn(100)) 
>>> sol = model.fit(X, y)
>>> print(sol["theta"].shape)  # (3,)
>>> print(sol["B"].shape)      # (2, 2)