Skip to content

AdditiveDGP

A GAM-style target: a weighted sum of univariate functions, one per informative feature, with no interactions at all.

y = Σᵢ wᵢ · fᵢ(X[:, i]) + noise

The absence of interactions is the point. If a method does well here but poorly on PolynomialDGP, you have located its weakness precisely.

complexity selects which function library the fᵢ are drawn from, not how many there are — the informative count is fixed at max(2, p // 2), and noise is fixed at σ = 0.5:

  • "low" and "medium" draw from smooth functions: sin(πx), √|x|, , x.
  • "high" draws from wiggly ones: sin(4πx), √|x|·sin(πx), sign(x).

"high" is harder because higher-frequency components need more samples to pin down, and sign(x) is not differentiable at zero — not because there is more signal to find.

from synthbench import BenchPipeline, AdditiveDGP

dgp = AdditiveDGP(complexity="medium", task_type="regression", random_state=0)
result = BenchPipeline(dgp).run(n_samples=500, n_features=10, random_state=42)

print(result.X.shape)  # (500, 10)
print(sum(result.metadata["signal_feature_importances"].values()))  # 1.0

Unlike most of the DGPs here, importances are empirical rather than structural: Var(wᵢ · fᵢ(X[:, i])), measured on the generated X and normalised across components. That means they tell you how much each component actually moves the target, so a feature whose function happens to be nearly flat over the sampled range will score low even though the formula uses it. Features outside the informative set still get exactly 0.0.

n_classes > 2 produces ordinal classes here

This DGP is built on a fixed term basis, so it has one signal and no honest way to emit a separate score per class. Above two classes that signal is cut into ordered bins instead, which means only neighbouring classes are confusable. That is a fine model of a genuinely ordinal target — a severity grade, a credit rating — and a poor general multiclass benchmark, because a method that internally regresses a single score will score better than it deserves. metadata["label_mechanism"] reads "ordinal", so a result always says which kind it is. For unordered classes use LinearDGP, SparseDGP, TreeDGP, or RandomNeuralDGP.

Full parameter list: AdditiveDGP in the API reference.