Pyomo / pyomo

An object-oriented algebraic modeling language in Python for structured optimization problems.
https://www.pyomo.org
Other
1.97k stars 507 forks source link

Limitation with arguments to ExternalFunctions #1090

Closed andrewlee94 closed 3 years ago

andrewlee94 commented 5 years ago

I have a case where I am using ExternalFunctions as part of my model. The ExternalFunction takes three arguments:

As the first argument is a constant, internal option I would like to provide it as a pure Python variable rather than a Pyomo component. However, if I provide the first argument as a Python int then I get the following error:

Traceback (most recent call last):
  File "dummy_frame.py", line 44, in <module>
    results = solver.solve(m, tee=True)
  File "/home/alee/pyomo/pyomo/pyomo/opt/base/solvers.py", line 573, in solve
    self._presolve(*args, **kwds)
  File "/home/alee/pyomo/pyomo/pyomo/opt/solver/shellcmd.py", line 198, in _presolve
    OptSolver._presolve(self, *args, **kwds)
  File "/home/alee/pyomo/pyomo/pyomo/opt/base/solvers.py", line 673, in _presolve
    **kwds)
  File "/home/alee/pyomo/pyomo/pyomo/opt/base/solvers.py", line 744, in _convert_problem
    **kwds)
  File "/home/alee/pyomo/pyomo/pyomo/opt/base/convert.py", line 105, in convert_problem
    problem_files, symbol_map = converter.apply(*tmp, **tmpkw)
  File "/home/alee/pyomo/pyomo/pyomo/solvers/plugins/converter/model.py", line 191, in apply
    io_options=io_options)
  File "/home/alee/pyomo/pyomo/pyomo/core/base/block.py", line 1716, in write
    io_options)
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 378, in __call__
    include_all_variable_bounds=include_all_variable_bounds)
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 1531, in _print_model_NL
    wrapped_repn.repn.nonlinear_expr)
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 529, in _print_nonlinear_terms_NL
    self._print_nonlinear_terms_NL(exp.arg(1))
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 528, in _print_nonlinear_terms_NL
    self._print_nonlinear_terms_NL(exp.arg(0))
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 503, in _print_nonlinear_terms_NL
    self._print_nonlinear_terms_NL(vargs[0])
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 529, in _print_nonlinear_terms_NL
    self._print_nonlinear_terms_NL(exp.arg(1))
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 589, in _print_nonlinear_terms_NL
    self._print_nonlinear_terms_NL(exp.arg(0))
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 528, in _print_nonlinear_terms_NL
    self._print_nonlinear_terms_NL(exp.arg(0))
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 503, in _print_nonlinear_terms_NL
    self._print_nonlinear_terms_NL(vargs[0])
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 529, in _print_nonlinear_terms_NL
    self._print_nonlinear_terms_NL(exp.arg(1))
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 635, in _print_nonlinear_terms_NL
    self._print_nonlinear_terms_NL(exp.expr)
  File "/home/alee/pyomo/pyomo/pyomo/repn/plugins/ampl/ampl_.py", line 569, in _print_nonlinear_terms_NL
    elif arg.is_fixed():
AttributeError: 'int' object has no attribute 'is_fixed'

The reason is that line 569 in ampl_.py presumes that the argument is a Pyomo component, and thus has the is_fixed attribute:

                for arg in exp.args:
                    print(arg)
                    if isinstance(arg, basestring):
                        OUTPUT.write(string_arg_str % (len(arg), arg))
                    elif arg.is_fixed():
                        self._print_nonlinear_terms_NL(arg())
                    else:
                        self._print_nonlinear_terms_NL(arg)

Obviously, this precludes using anything other than a Param or Var for these arguments. However, I would prefer to avoid creating a Pyomo component just for this purpose. Would it be possible to add support for non-Pyomo components are arguments to ExternalFunctions?

qtothec commented 5 years ago

I would like to echo this sentiment. It does not appear to be isolated to just this case. Just running the simple code below triggers the same error for me:

    m = ConcreteModel()
    m.x = Var(bounds=(1, 2))
    def myfun(x):
        return x
    m.fun = ExternalFunction(function=myfun)
    m.obj = Objective(expr=m.fun(m.x))
    m.write("test.nl")
Traceback (most recent call last):
  File "~/pyoexternal.py", line 143, in <module>
    run()
  File "~/pyoexternal.py", line 81, in run
    m.write("test.nl")
  File "~/anaconda3/envs/pyotest/lib/python3.7/site-packages/pyomo/core/base/block.py", line 1716, in write
    io_options)
  File "~/anaconda3/envs/pyotest/lib/python3.7/site-packages/pyomo/repn/plugins/ampl/ampl_.py", line 378, in __call__
    include_all_variable_bounds=include_all_variable_bounds)
  File "~/anaconda3/envs/pyotest/lib/python3.7/site-packages/pyomo/repn/plugins/ampl/ampl_.py", line 1587, in _print_model_NL
    wrapped_repn.repn.nonlinear_expr)
  File "~/anaconda3/envs/pyotest/lib/python3.7/site-packages/pyomo/repn/plugins/ampl/ampl_.py", line 568, in _print_nonlinear_terms_NL
    elif arg.is_fixed():
AttributeError: 'int' object has no attribute 'is_fixed'

From what I can see, arg[0] is an integer counter, and arg[1] is a _VarData instance in this case.

mariogen commented 5 years ago

I have the same problem. A simple python external function does not work.