E-SE0005 — Decorated function references an unbound variable

Error Type

Syntax Error

Phase

defining a problem with the decorator API

Python exception

ValueError (builtin)

Message

Invalid decorated function: did you reference the function's own name `{name}`, or a variable defined only inside another decorated function?

A decorated function cannot use variables that are still unbound when the decorator runs.

Possible fix: remove the reference, or bind the variable globally before the function definition.

({name} is the name of the decorated function)

Cause

When @problem.update or @jijmodeling.Problem.define processes a function, it resolves the variables the function captures from its enclosing scopes. This error is raised when one of those captured variables has no value yet at the moment the decorator runs.

One common trigger is mentioning the function’s own name inside its body: the name is only bound after the decorator returns, so it is still empty while the decorator is processing the function.

def build_model():
    @jm.Problem.define("My Problem")
    def myprob(problem: jm.DecoratedProblem):
        _ = myprob  # `myprob` is not bound yet when the decorator runs

Another typical trigger is referencing a variable that is defined only inside another Problem.define or problem.update function block, rather than globally: sibling decorated functions never run as ordinary functions, so an assignment inside one of them never gives the shared variable a value.

def build_models():
    @jm.Problem.define("First")
    def first(problem: jm.DecoratedProblem):
        w = problem.Float(ndim=1)

    @jm.Problem.define("Second")
    def second(problem: jm.DecoratedProblem):
        _ = w  # `w` only exists inside `first`; it is unbound here

Any other enclosing-scope variable that is assigned only after the function definition triggers the same error, which is why the message is phrased as a question.

Fix

Remove the reference to the function’s own name from the body; inside the function, work through the problem parameter, and use the object returned by the decorator only after the definition.

def build_model():
    @jm.Problem.define("My Problem")
    def myprob(problem: jm.DecoratedProblem):
        w = problem.Float(ndim=1)
        x = problem.BinaryVar(shape=(w.len_at(0),))
        problem += jm.sum(w * x)  # build via `problem`, not `myprob`

    return myprob  # the returned Problem is available here

If the offending variable lives in another decorated function, bind it globally or lookup it using jijmodeling.Problem.placeholders(), jijmodeling.Problem.decision_vars(), or jijmodeling.Problem.named_exprs(). For any other variable, move its assignment above the decorated function definition (e.g. to module level) so it already has a value when the decorator runs.