> ## Documentation Index
> Fetch the complete documentation index at: https://docs.outerproduct.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reasoning Methods

> Reasoning models each come with methods for reasoning across the structured data stack.

Below, we describe two core methods of the `ReasoningModel` object, drivers and scenarios.

## Drivers

Every inference from a `ReasoningModel` is enriched with the drivers behind the output.  These drivers are available in a `Reasoning` object.

```python theme={null}
scores = ws.table("customers").filter(op.col("signup_year") == 2025)
predictions, reasoning = model.predict_and_explain(scores)

# predictions: numpy.ndarray of shape (n_samples,)
# reasoning:   Reasoning object with .feature_names, .drivers
```

The `Reasoning` object contains:

| Attribute                 | Type        | Description                                                   |
| ------------------------- | ----------- | ------------------------------------------------------------- |
| `reasoning.feature_names` | `list[str]` | Ordered list of input names                                   |
| `reasoning.drivers`       | `ndarray`   | Features that drive the prediction for each individual sample |

```python theme={null}
reasoning.feature_names    # ["age", "income", "credit_score", ...]
reasoning.drivers     # ndarray of same shape as input data
```

## Scenarios

A `ReasoningModel` provides verifiable reasoning via scenarios.  Scenarios are minimal interventions on input data that change model outputs to a specific target value.  As a result, scenarios provide verifiable reasoning for models.

`model.scenario()` is available on any `ReasoningModel` and returns a structured `ScenarioResult` containing one `QueryResult` per input row, each with the candidate scenarios. The required `target_value` is the score the search drives each prediction toward — in label units for regression, or the positive-class probability in `(0, 1)` for binary classification (e.g. `0.5` flips a row to the other side of the decision boundary).

```python theme={null}
queries_df = pd.DataFrame([
    {"age": 25, "income": 50000, "credit_score": 620},
    {"age": 40, "income": 85000, "credit_score": 710},
])
queries = ws.register_pandas(queries_df, table_name="queries")

result = model.scenario(queries, target_value=0.5)

for q in result.queries:
    print(f"baseline={q.baseline_prediction:.2f}")
    for scenario in q.scenarios:
        # Each scenario is a compact diff: {changed_feature: new_value}.
        print(f"  Candidate ({len(scenario)} changes):")
        for feature, new_value in scenario.items():
            print(f"    {feature}: {q.query[feature]} → {new_value}")
```

## Sourcing scenarios

The number of scenarios is determined by the reasoning effort.  Control the effort with `n_trials` and `max_steps`:

```python theme={null}
result = model.scenario(
    queries,
    target_value=0.5,
    n_trials=200,   # number of scenario sourcing trials per query row
    max_steps=20,   # reasoning effort per trial
)
```
