Skip to content

modeling: 13 of 19 intrinsic functions are missing from the top-level discopt namespace #1233

Description

@jkitchin

Summary

python/discopt/modeling/core.py defines 19 symbolic intrinsic functions
(lines 1505–1738), but only 6 are re-exported from the top-level discopt
namespace. The other 13 are reachable only as
from discopt.modeling.core import <name>.

Measured at runtime on fix/lp-std-form-consolidation (worktree wt-stdform):

top-level : exp log sqrt sin cos tan
core-only : abs_ acos acosh asin asinh atan atanh cosh log10 log1p log2 sinh tanh

So from discopt import sqrt works and from discopt import tanh raises
AttributeError / ImportError.

Why this is a hard block rather than a papercut

Expression.__array_ufunc__ = None (modeling/core.py:619) means a user cannot
fall back on NumPy:

>>> np.tanh(x)
TypeError: operand 'Variable' does not support ufuncs (__array_ufunc__=None)

That opt-out is the right call on its own terms — it stops NumPy from silently
returning an object array of expressions — but combined with the missing export
it leaves no discoverable way to write tanh(x) in a model. The user has to know
that discopt.modeling.core exists and that the function lives there.

tanh in particular is a common activation, and discopt.nn's
FullSpaceFormulation / ReducedSpaceFormulation handle smooth activations, so
a model that embeds a tanh network is a natural thing to write by hand.

For comparison, Pyomo exports its full intrinsic set (sqrt, log, log10,
exp, the trig and hyperbolic families, ceil, floor) from pyomo.environ.

Reproduction

import numpy as np
from discopt import Model

m = Model()
x = m.continuous("x", lb=0.1, ub=2.0)

from discopt import tanh              # AttributeError / ImportError
np.tanh(x)                            # TypeError: does not support ufuncs
from discopt.modeling.core import tanh
tanh(x)                               # works -> FunctionCall

Suggested fix

Extend the re-export block in python/discopt/__init__.py (currently around
lines 181–193, which lists cos, exp, log, sin, sqrt, tan) to cover
the remaining 13: abs_, acos, acosh, asin, asinh, atan, atanh,
cosh, log10, log1p, log2, sinh, tanh.

Two things to decide while doing it:

  1. abs_ naming. The trailing underscore avoids shadowing the builtin. Worth
    confirming that is still the intent before promoting it to the top level,
    since from discopt import * would then introduce abs_ alongside abs.
  2. Lazy-import cost. __init__.py has an explicit note about import cost and
    a deferred-import list; the intrinsics are plain functions in
    modeling/core.py, which is already imported for Model, so this should add
    nothing measurable — but the added names should go in the same block as the
    existing six rather than triggering a new module import.

Test

A regression test asserting every public intrinsic defined in
modeling/core.py is reachable from the top-level namespace, so the two lists
cannot drift apart again. Enumerating them by introspection rather than by a
hardcoded name list is what keeps a future addition from repeating this.

Verification of the claims above

Three runtime checks executed (CHECKS_EXECUTED 3): np.tanh(x) raises
TypeError; discopt.tanh raises AttributeError; discopt.modeling.core.tanh(x)
returns a FunctionCall.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions