{
  "$id": "https://schema.rtemis.org/hyperparameters/linadforest/v1/schema.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "rtemis LINADForestHyperparameters",
  "description": "Bagged ensemble of Linear Additive Trees. See `setup_LINADForest`.",
  "type": "object",
  "properties": {
    "n_trees": {
      "description": "Number of trees in the forest.",
      "oneOf": [
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true
      }
    },
    "mtry_split": {
      "description": "Features sampled at each split search. NULL scans every feature the tree holds. Cannot exceed the number of features in the training data.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true,
        "data_bound": "n_features"
      }
    },
    "mtry_tree": {
      "description": "Features available to a whole tree, splits and node models alike. NULL gives every tree every feature. Cannot exceed the number of features in the training data.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true,
        "data_bound": "n_features"
      }
    },
    "max_leaves": {
      "description": "Largest number of terminal nodes to grow. Plays the role the number of trees plays in gradient boosting.",
      "oneOf": [
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true
      }
    },
    "patience": {
      "description": "Expansions without validation improvement before growth stops, as gradient boosting stops on rounds. Needs a validation set; without one the tree grows to max_leaves. Stopping only bounds growth -- the size is still the one that minimizes validation loss over the curve reached. Defaults to growing to max_leaves.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true
      }
    },
    "force_max_leaves": {
      "description": "Keep every leaf grown instead of selecting a tree size on the validation set.",
      "type": "boolean",
      "x-rtemis": {
        "type": "boolean"
      }
    },
    "smooth_validation_curve": {
      "description": "Smooth the validation curve before reading its minimum. Steadier when the validation set is small. Applies only when force_max_leaves is FALSE.",
      "type": ["boolean", "null"],
      "x-rtemis": {
        "type": "boolean",
        "applies_when": {
          "force_max_leaves": false
        }
      }
    },
    "min_cases_split": {
      "description": "Fewest cases a node may hold and still be considered for splitting.",
      "oneOf": [
        {
          "type": "integer",
          "minimum": 2
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 2
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true
      }
    },
    "min_cases_leaf": {
      "description": "Fewest cases a split must leave on each side.",
      "oneOf": [
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true
      }
    },
    "min_cases_node_model": {
      "description": "Fewest cases needed to fit a linear model at a node. Below it the node inherits its parent unchanged. Applies only when node_model is forward, ridge or elasticnet.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true,
        "applies_when": {
          "node_model": ["forward", "ridge", "elasticnet"]
        }
      }
    },
    "node_model": {
      "description": "Model fitted at each node. constant is the intercept-only model every decision tree node carries; the others add a linear model on top of it, regularized by lambda.",
      "type": "string",
      "enum": ["forward", "ridge", "elasticnet", "constant"],
      "x-rtemis": {
        "type": "string"
      }
    },
    "nvmax": {
      "description": "Most terms forward selection may add beside the intercept. A ceiling: the search stops earlier when forward_stop says a term does not pay for itself. Capped at the width of the design. Defaults to 3 where it applies. Applies only when node_model is forward.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true,
        "applies_when": {
          "node_model": "forward"
        }
      }
    },
    "forward_stop": {
      "description": "Cost a term must earn to be added: the Bayesian or Akaike criterion, or none to always add nvmax terms. Shrinkage alone cannot stop the search, since every added term reduces the residual sum of squares. Defaults to bic where it applies. Applies only when node_model is forward.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "string",
          "enum": ["bic", "aic", "none"]
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "string",
                "enum": ["bic", "aic", "none"]
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "string",
        "tunable": true,
        "applies_when": {
          "node_model": "forward"
        }
      }
    },
    "lambda": {
      "description": "L2 penalty on the node models, on a standardized design so one value means the same at every node. Under forward selection it penalizes the fit and the search alike, so a feature that only looks good unpenalized does not win. Defaults to 0.05 where it applies. Applies only when node_model is forward, ridge or elasticnet.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "number",
          "minimum": 0
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "number",
                "minimum": 0
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "number",
        "tunable": true,
        "applies_when": {
          "node_model": ["forward", "ridge", "elasticnet"]
        }
      }
    },
    "alpha": {
      "description": "Elastic-net mixing: 0 is ridge, 1 is lasso. Applies only when node_model is elasticnet.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "number",
        "tunable": true,
        "applies_when": {
          "node_model": "elasticnet"
        }
      }
    },
    "learning_rate": {
      "description": "Shrinkage applied to every functional update.",
      "oneOf": [
        {
          "type": "number",
          "maximum": 1,
          "exclusiveMinimum": 0
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "number",
                "maximum": 1,
                "exclusiveMinimum": 0
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "number",
        "tunable": true
      }
    },
    "root_model": {
      "description": "Model fitted at the root. NULL uses node_model.",
      "type": ["string", "null"],
      "enum": [
        "forward",
        "ridge",
        "elasticnet",
        "constant",
        null
      ],
      "x-rtemis": {
        "type": "string"
      }
    },
    "root_nvmax": {
      "description": "nvmax for the root model. NULL uses nvmax. Ignored unless the root model is forward selection.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "integer",
          "minimum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true
      }
    },
    "root_lambda": {
      "description": "lambda for the root model. NULL uses lambda. Ignored unless the root model is penalized.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "number",
          "minimum": 0
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "number",
                "minimum": 0
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "number",
        "tunable": true
      }
    },
    "root_alpha": {
      "description": "alpha for the root model. NULL uses alpha. Ignored unless the root model is an elastic net.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "number",
        "tunable": true
      }
    },
    "root_learning_rate": {
      "description": "Shrinkage applied to the root model's slopes. 0 fits no root model, so the first step is a split; 1 fits it in full.",
      "oneOf": [
        {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "number",
        "tunable": true
      }
    },
    "node_test": {
      "description": "Cost a node's slopes must earn over the constant alone, so that a node carries coefficients only where the data supports them and a plain constant otherwise. The constant is nested in the linear model, so on the node's own cases the slopes always fit better and no comparison is possible without a cost; aic charges 2 per nonzero slope and bic log(n). Forward selection has the same criterion per term in forward_stop, which subsumes this one, so this is the shrinking models' equivalent. Defaults to none where it applies. Applies only when node_model is ridge or elasticnet.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "string",
          "enum": ["none", "aic", "bic"]
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "string",
                "enum": ["none", "aic", "bic"]
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "string",
        "tunable": true,
        "applies_when": {
          "node_model": ["ridge", "elasticnet"]
        }
      }
    },
    "split_criterion": {
      "description": "What the stump search scores a candidate side by: the level its fit explains, or its level and its slope in the split variable. linear finds a change of slope in the split variable, such as a parabola's vertex, which a level criterion cannot see; it can also chase the slope of the wrong variable where the change belongs to another. Defaults to mean where it applies. Applies only when split_search is stump.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "string",
          "enum": ["mean", "linear"]
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "string",
                "enum": ["mean", "linear"]
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "string",
        "tunable": true,
        "applies_when": {
          "split_search": "stump"
        }
      }
    },
    "split_search": {
      "description": "How a split is chosen. stump takes the best squared-error split of the gradient; exhaustive scores each candidate by the loss after fitting both child models.",
      "type": "string",
      "enum": ["stump", "exhaustive"],
      "x-rtemis": {
        "type": "string"
      }
    },
    "split_binning": {
      "description": "Discretize each numeric feature into this many equal-frequency bins and consider only bin boundaries as splits. Applies to both split searches. NULL considers every distinct value.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "integer",
          "minimum": 2
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 2
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true
      }
    },
    "split_bin_type": {
      "description": "How split_binning places bin edges: frequency spaces them evenly through the cases, width evenly through the feature's range.",
      "oneOf": [
        {
          "type": "string",
          "enum": ["frequency", "width"]
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "string",
                "enum": ["frequency", "width"]
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "string",
        "tunable": true
      }
    },
    "n_cuts": {
      "description": "Number of cut points tried per feature by the exhaustive search. split_bin_type decides how they are spaced. Applies only when split_search is exhaustive.",
      "oneOf": [
        {
          "type": "null"
        },
        {
          "type": "integer",
          "minimum": 2
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "integer",
                "minimum": 2
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "integer",
        "tunable": true,
        "applies_when": {
          "split_search": "exhaustive"
        }
      }
    },
    "split_features": {
      "description": "Features that may define a split. NULL imposes no constraint, so every feature may. Independent of the linear roles: a feature can split without entering any node model, or the reverse. Values must name training features.",
      "type": ["array", "null"],
      "items": {
        "type": "string"
      },
      "minItems": 1,
      "x-rtemis": {
        "type": "string",
        "container": "array",
        "data_bound": "feature_names"
      }
    },
    "linear_features": {
      "description": "Features that get a slope in the node models. NULL imposes no constraint, so every feature does. A tree with no slopes at all is node_model = constant. Values must name training features.",
      "type": ["array", "null"],
      "items": {
        "type": "string"
      },
      "minItems": 1,
      "x-rtemis": {
        "type": "string",
        "container": "array",
        "data_bound": "feature_names"
      }
    },
    "global_features": {
      "description": "Linear features whose slope is shared by every leaf: the root estimates it and no node below may change it. Must be a subset of linear_features where that is set. NULL imposes no constraint, so no coefficient is pinned and every linear feature may adapt. Values must name training features.",
      "type": ["array", "null"],
      "items": {
        "type": "string"
      },
      "minItems": 1,
      "x-rtemis": {
        "type": "string",
        "container": "array",
        "data_bound": "feature_names"
      }
    },
    "gamma": {
      "description": "Weight retained by a case in the branch it does not belong to. 0 is a hard partition; larger values share influence and reduce variance.",
      "oneOf": [
        {
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "number",
        "tunable": true
      }
    },
    "line_search": {
      "description": "Scope of the Newton step for each update: one per expansion, one per child, or none.",
      "oneOf": [
        {
          "type": "string",
          "enum": ["expansion", "child", "none"]
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "string",
                "enum": ["expansion", "child", "none"]
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "string",
        "tunable": true
      }
    },
    "line_search_max": {
      "description": "Largest absolute step the line search may take.",
      "type": "number",
      "exclusiveMinimum": 0,
      "x-rtemis": {
        "type": "number"
      }
    },
    "constant_rule": {
      "description": "Rule for a node's constant: the manuscript's closed forms, or the weighted mean of the residual. Identical for regression; they differ only for classification.",
      "oneOf": [
        {
          "type": "string",
          "enum": ["closed_form", "least_squares"]
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "string",
                "enum": ["closed_form", "least_squares"]
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "string",
        "tunable": true
      }
    },
    "ifw": {
      "description": "Inverse Frequency Weighting in classification.",
      "oneOf": [
        {
          "type": "boolean"
        },
        {
          "type": "object",
          "properties": {
            "candidates": {
              "description": "Values to tune over; the tuner selects one.",
              "type": "array",
              "items": {
                "type": "boolean"
              },
              "minItems": 2
            }
          },
          "required": ["candidates"],
          "additionalProperties": false
        }
      ],
      "x-rtemis": {
        "type": "boolean",
        "tunable": true
      }
    }
  },
  "additionalProperties": false
}
