Explainer Guide
This page focuses on the common public API shared by the three explainers and highlights the few places where backend behavior differs.
Constructor pattern
All public explainers follow the same constructor shape.
explainer = SomeExplainer(model, mapper=mapper)
The two required inputs are:
a fitted supported tree ensemble,
the mapper returned by
ocean.feature.parse_features().
Two optional constructor arguments are especially useful when you compare backends:
isolation=some_isolation_forestis supported by the MIP and CP explainers and adds an isolation-forest plausibility constraint.isolation_threshold=...can be passed together withisolation=...on MIP and CP.0.5reproduces the historical cutoff, values closer to1are weaker, and smaller values are stricter.hard_voting=Trueis supported by the MaxSAT explainer forRandomForestClassifierand switches the score comparison from soft class proportions to per-tree votes.
Calling explain
Every explainer exposes an explain method with the same core arguments.
xQuery instance as a one-dimensional numpy array in the processed feature space.
yTarget class to enforce in the counterfactual.
normDistance norm. The MIP backend supports
1and2. The CP backend supports integer norms with1as the default. The MaxSAT backend supports1.max_timeSolver time limit in seconds.
num_workersParallel worker count when the backend exposes it.
random_seedSolver seed for more repeatable runs. The MaxSAT backend currently accepts this argument for API compatibility but does not use it.
verboseWhether to print solver logs.
Backend-specific behavior
Topic |
MIP |
CP |
MaxSAT |
|---|---|---|---|
Norm support |
|
Integer |
|
Anytime callback |
Yes |
Yes |
No public callback list |
Automatic cleanup after solve |
Yes by default |
Yes |
Yes |
Isolation forest support |
Yes |
Yes |
No |
Hard-voting random forest mode |
No |
No |
Yes |
Repeated solves
All three explainers default to clean_up=True inside explain. That
means query-specific objectives and target-class constraints are removed
automatically after each solve unless you opt out.
Call cleanup() manually only when you deliberately run explain(...,
clean_up=False) and want to clear the previous query state yourself before
reusing the same explainer instance.
One backend detail matters when you keep old explanations around. The CP and
MaxSAT explanation objects read values back from the current shared backend
solver state, so they behave like live views rather than frozen snapshots. If
you want to preserve one result before another CP or MaxSAT solve, materialize
it immediately with explanation.to_numpy().copy() or
explanation.to_series().copy().
Inspecting the result
Once a counterfactual is found, these access patterns are typically the most useful.
explanation.xgives the processed numerical vector.explanation.to_series()keeps the processed column names.explanation.valuedecodes one-hot groups into original category labels.explainer.get_objective_value()returns the backend objective value for the last solve.explainer.get_distance()returns the post-processed distance between the query and the decoded counterfactual using the norm from the lastexplain(...)call.repr(explanation)is usually the best display form for notebooks and logs.
Handling infeasibility and time limits
All explainers return None when no counterfactual is found under the given
constraints. Depending on the backend, the solver may also warn when:
the target class is infeasible,
a feasible solution was found but optimality could not be certified in time,
the solver terminated for a backend-specific reason.
When that happens, increase max_time, simplify the ensemble, or choose a
query whose target class is realistically reachable under the learned model.
For MIP and CP with isolation=..., also check whether
isolation_threshold is too strict for the target region.