Experimental ResidOpt Backend¶
updatesupport can optionally call
residopt as an endpoint compiler for
some repeated robust-optimization subproblems. This is an experimental
integration, not a default dependency.
Install the integration with:
pip install "updatesupport[residopt]"
# or
uv add "updatesupport[residopt]"
The published residopt 0.1.x package currently requires Python 3.13 or newer.
The first supported path is an L2 hidden-composition stress test:
import updatesupport as us
grouped = us.from_dataframe(
rows,
public=["region"],
hidden=["region", "channel", "tenure_band"],
target="metric",
weight="weight",
q=us.q_l2_budget(0.05),
)
report = us.residopt_l2_support_interval(grouped)
print(report.to_markdown())
For repeated endpoint evaluations, build a reusable compiler once:
compiler = us.ResidOptL2EndpointCompiler.from_grouped(grouped)
primary = compiler.interval()
channel_direction = {
state: 1.0 if state[-1] == "paid" else 0.0
for state in grouped.problem.states
}
channel = compiler.interval(direction=channel_direction)
print(compiler.compiled_template_count)
print(compiler.support_solve_count)
The compiler caches the public-incidence nullspace and a parameterized residopt SOCP template. The first interval builds the template; later intervals update the projected direction parameter and solve the same compiled problem.
Claim Screening¶
Claim audits can opt into a screen-then-certify path:
claim = us.claim(
"reported lift remains positive",
public=["segment"],
hidden=["segment", "channel", "tenure_band"],
target="lift",
weight="users",
q=us.q_l2_budget(0.05),
decision=us.threshold_decision(">=", 0.0),
screening_backend="residopt",
)
audit = claim.audit(rows)
print(audit.screening.as_dict())
When the conservative residopt interval already proves the decision rule or
ambiguity limit, the audit returns without running the exact endpoint solve. If
the screen is unavailable or inconclusive, the audit falls back to the ordinary
exact primary solver and records the screening attempt in audit.screening.
This mode is intentionally conservative: it can certify passes early, but it does not use a wide conservative interval to fail a claim without the exact fallback.
Check availability directly:
import updatesupport as us
print(us.residopt_available().as_dict())
Cached Refinement Screening¶
Refinement and frontier workflows evaluate many candidate public representations. The experimental refinement-screening context keeps that workflow explicit:
context = us.ResidOptRefinementScreenContext(
rows,
public=["region"],
hidden=["region", "channel", "tenure_band"],
target="metric",
weight="weight",
q=us.q_l2_budget(0.05),
)
screen = context.screen(
candidate_refinements=["channel", "tenure_band"],
ambiguity_limit=0.01,
exact_fallback=True,
)
print(screen.to_markdown())
For each candidate representation, the context:
compiles or reuses a
GroupedProblem;compiles or reuses one
ResidOptL2EndpointCompiler;evaluates the conservative residopt interval;
skips the exact CVXPY endpoint if the conservative interval is already within the supplied ambiguity limit;
otherwise runs the exact
updatesupportendpoint whenexact_fallback=True.
The report separates these outcomes:
screen.certified_count
screen.exact_solve_count
screen.exact_solve_avoided_count
screen.to_tables()["candidates"]
Use us.residopt_refinement_screen(...) for a one-shot helper, or keep a
ResidOptRefinementScreenContext alive when running several related screens
over the same dataset.
The standard one-column refinement API can also opt into the same screen:
rows = us.recommend_refinements(
data,
public=["region"],
hidden=["region", "channel", "tenure_band"],
target="metric",
weight="weight",
candidate_refinements=["channel", "tenure_band"],
q=us.q_l2_budget(0.05),
screening_backend="residopt",
ambiguity_limit=0.01,
)
for row in rows:
print(row.column, row.after_ambiguity, row.after_ambiguity_bound_type)
When a candidate is certified by the conservative screen, its returned
after_ambiguity is marked as a conservative_upper_bound. When the screen is
inconclusive and exact fallback runs, the returned value is marked exact.
public_descent_report(...) exposes the same refinement-screening option
through refinement_screening_backend="residopt" and includes the screening
summary in Markdown and structured exports.
The public-representation frontier search can opt into conservative screening as well:
frontier = us.public_representation_frontier(
data,
base_public=["region"],
hidden=["region", "channel", "tenure_band"],
target="metric",
weight="weight",
candidate_refinements=["channel", "tenure_band"],
q_presets=[us.q_l2_budget(0.05)],
ambiguity_limit=0.01,
screening_backend="residopt",
)
print(frontier.screening.as_dict())
This path uses one cached residopt context per frontier stress-test scenario. If
a candidate representation is certified by the conservative screen, the scenario
ambiguity is marked as a conservative_upper_bound; if the screen is
inconclusive and exact fallback is enabled, the scenario ambiguity is marked
exact.
The same frontier-screening path is available through the certificate and claim front doors:
certificate = us.certify_public_representation(
data,
base_public=["region"],
hidden=["region", "channel", "tenure_band"],
target="metric",
candidate_refinements=["channel", "tenure_band"],
q_presets=[us.q_l2_budget(0.05)],
ambiguity_limit=0.01,
screening_backend="residopt",
)
claim = us.claim(
"reported metric is stable",
public=["region"],
hidden=["region", "channel", "tenure_band"],
target="metric",
q_presets=[us.q_l2_budget(0.05)],
candidate_refinements=["channel", "tenure_band"],
ambiguity_limit=0.01,
refinement_screening_backend="residopt",
)
audit = claim.audit(data)
Certificate and claim Markdown reports summarize how many frontier endpoints were certified by conservative screening, how many exact fallbacks ran, and how many exact endpoint solves were avoided.
What It Compiles¶
For a fixed public law and observed hidden distribution q0, the adapter writes
hidden-distribution shifts as
q = q0 + delta
and enforces public-law equality by projecting delta into the nullspace of the
public-incidence matrix. For an L2 radius, the endpoint support calculation is:
sup <direction, delta>
subject to A delta = 0
||delta||_2 <= radius
After the nullspace projection this becomes an ellipsoidal support-function
problem. residopt compiles that support atom to an SOCP certificate.
Certificate Semantics¶
The current adapter preserves public-law equality and the L2 radius, but it drops hidden-cell nonnegativity. That means:
the
residoptcertificate is exact for the compiled ellipsoidal support atom;the returned interval is conservative for the original simplex-constrained
updatesupportendpoint;if a claim survives this conservative interval, it also survives the tighter exact endpoint under the same L2 radius;
if the interval is too wide, use the standard CVXPY backend to solve the exact simplex-constrained problem.
This distinction is visible in the report:
report.exact_for_updatesupport_q
report.conservative_for_updatesupport_q
report.to_tables()["certificates"]
Why This Matters¶
The most promising use is repeated endpoint evaluation: refinement ranking,
frontier search, claim repair, and other workflows that evaluate many candidate
public representations. residopt gives updatesupport a place to route
subproblems through compiled support-function certificates, oracle decisions,
and timing metadata without making the core package depend on a separate
compiler. Use ResidOptL2EndpointCompiler when many target directions share the
same fixed public representation and L2 radius.
Current Limits¶
This first slice supports fixed linear and uncertain-linear point-estimate targets. Nonlinear targets, ratio targets, procedure targets, nonnegativity-exact certificates, and exact-support certificates with hidden-cell nonnegativity are future integration points.