Problem modeling

Optimistic semantics

Construct the lower problem with blvpy.LowerProblem, then pass it to blvpy.BilevelProblem. The same lower decision-variable objects may appear in the upper objective and constraints. BLVPY preserves those objects, so the upper problem can choose the most favorable member of a nonsingleton lower solution set.

x = cp.Variable(name="x")
y = cp.Variable(name="y")

lower = bp.LowerProblem(
    cp.Minimize(0.0 * y),
    [y >= x, y <= 1.0],
    parameters=[x],
)
problem = bp.BilevelProblem(
    cp.Minimize(cp.square(x) + cp.square(y - 1.0)),
    lower,
    upper_constraints=[x >= 0.0, x <= 1.0],
)

Every variable in LowerProblem.parameters is an upper variable that BLVPY holds fixed inside the lower problem. BLVPY clones the lower expression tree and replaces each listed variable with a generated CVXPY parameter of matching shape and domain. Unlisted variables remain the original lower variables.

Structural requirements

The complete model must satisfy all of the following:

  • The upper problem is a real-valued continuous optimization problem with a scalar cp.Minimize or cp.Maximize objective.

  • The assembled upper objective and upper constraints are compliant with CVXPY’s DNLP rules.

  • The lower problem is a real-valued continuous convex optimization problem: either cp.Minimize with a convex objective expression or cp.Maximize with a concave objective expression.

  • The lower problem contains at least one canonical optimization variable; constant-only lower problems are not supported.

  • The lower problem is DCP and DPP with respect to every linked upper variable.

  • Every unlinked CVXPY parameter already has a finite value.

  • CVXPY produces only zero, nonnegative, second-order, exponential, and 3D power-cone blocks when canonicalization is requested with a linear conic objective.

This includes linear programs, quadratic programs that CVXPY converts exactly to the accepted conic form, second-order cone programs, and models whose exact cp.power(..., approx=False) or cp.pnorm(..., approx=False) graphs use 3D power cones. Exact exponential-family atoms and scalar, vector, or matrix cp.ExpCone constraints are also accepted.

Call blvpy.BilevelProblem.validate() to obtain a specific exception for an unsupported model. See Troubleshooting for the exception categories.

BLVPY also audits every nonlinear node in the lower source expression tree. See Supported atoms for the complete allowlist, exactness conditions, and unsupported atom families.

Objective senses and canonicalization

BLVPY preserves the objective objects supplied by the model. In particular, objective returns the original cp.Minimize or cp.Maximize object.

Internally, a lower cp.Maximize(f) objective is changed to the equivalent cp.Minimize(-f) objective immediately before conic canonicalization. The canonical vectors and offsets, KKT conditions, and residuals therefore always use a minimization convention. For lower maximization, the canonical objective \(c(x)^T u+d(x)\) equals the negative of the modeled lower objective \(f(x,y)\). This sign convention also applies to advanced canonical inspection.

Parameters and fixed data

The word parameters in LowerProblem refers to upper CVXPY variables. Ordinary CVXPY parameters may still appear as fixed lower data, but they must have values when the model is first canonicalized. BLVPY freezes those values into the canonical family. Change them only by constructing and canonicalizing a new BilevelProblem.

Linked variables may be scalar, vector, or matrix valued. Their relevant CVXPY domain attributes and native bounds= data are copied to the generated parameters and enforced by the lifted model.

Variable values and bounds

Variable names are optional; CVXPY creates names automatically. Explicit names are recommended because they make initialization and validation messages more useful.

A pre-solve .value is only an initialization hint. In ordinary deterministic solving, BLVPY uses an existing value, otherwise a point derived from finite bounds, otherwise zero. It then applies variable-attribute projection and a best-effort projection onto DCP upper constraints.

The dynamically assigned variable.sample_bounds attribute is sampling-only metadata used by explicit best_of searches. See Best-of search for local solutions.