-
Notifications
You must be signed in to change notification settings - Fork 560
Add external function library #3648
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
bc29a64
427ae10
93a95cf
4a146b0
fa0a9ca
533ddb1
1ce0c10
967a111
9c6c6b6
79d0d55
4d115c8
d76ea88
f4fb50a
a74351c
a0f92fa
16be337
a5f3770
5b86b0a
5d63a10
4db46dc
e2302d0
dcc87f2
a7f5333
1c3c6f2
bf8ad36
5e4cb24
0eac381
1d45a86
f2547c3
857b48b
66161c2
f8e4857
fc0a021
f2476b2
a6be015
ee7d13f
936ec3c
ca1b43a
ac3a579
bf21a22
58b6451
4a7ced2
4e58993
a84f9e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| External Functions | ||
| ================== | ||
|
|
||
| Pyomo provides a set of AMPL user-defined functions that commonly occur but cannot be easily written as Pyomo expressions. | ||
|
|
||
| Using These External Functions | ||
| ------------------------------ | ||
|
|
||
| Build | ||
| ~~~~~ | ||
|
|
||
| You must build the Pyomo extensions to use these functions. Run ``pyomo build-extensions`` in the terminal and make sure the ``external_functions`` build status is "ok." | ||
|
|
||
| Example | ||
| ~~~~~~~ | ||
|
|
||
| .. doctest:: | ||
|
|
||
| >>> import pyomo.environ as pyo | ||
| >>> from pyomo.common.fileutils import find_library | ||
| >>> flib = find_library("external_functions") | ||
| >>> m = pyo.ConcreteModel(name = 'ExternalFunctions') | ||
| >>> m.sinc = pyo.ExternalFunction(library=flib, function="sinc") | ||
| >>> m.x = pyo.Var() | ||
| >>> m.z = pyo.Var() | ||
| >>> m.constraint = pyo.Constraint(expr = m.z == m.sinc(m.x)) | ||
|
|
||
| Functions | ||
| --------- | ||
|
|
||
| sinc(x) | ||
| ~~~~~~~ | ||
|
|
||
| This function is defined as: | ||
|
|
||
| .. math:: | ||
|
|
||
| \text{sinc}(x) = \begin{cases} | ||
| \sin(x) / x & \text{if } x \neq 0 \\ | ||
| 1 & \text{if } x = 0 | ||
| \end{cases} | ||
|
|
||
| In this implementation, the region :math:`-0.1 < x < 0.1` is replaced by a Taylor series with enough terms that the function should be at least :math:`C^2` smooth. The difference between the function and the Tayor series is near the limits of machine precision, about :math:`1 \times 10^{-16}` for the function value, :math:`1 \times 10^{-16}` for the first derivative, and :math:`1 \times 10^{-14}` for the second derivative. | ||
|
|
||
| These figures show the sinc(x) function, the Taylor series and where the Taylor series is used. | ||
|
|
||
| .. image:: figs/sinc_f.png | ||
|
|
||
| .. image:: figs/sinc_fx.png | ||
|
|
||
| .. image:: figs/sinc_fxx.png | ||
|
|
||
|
|
||
| sgnsqr(x) | ||
| ~~~~~~~~~ | ||
|
|
||
| This function is defined as: | ||
|
|
||
| .. math:: | ||
|
|
||
| \text{sgnsqr}(x) = \text{sgn}(x)x^2 | ||
|
|
||
| This function is only $C^1$ smooth because at 0 the second derivative is undefined and the jumps from -2 to 2. | ||
|
|
||
| sgnsqr_c4(x) | ||
| ~~~~~~~~~~~~ | ||
|
|
||
|
|
||
| This function is defined as: | ||
|
|
||
| .. math:: | ||
|
|
||
| \text{sgnsqr}(x) = \begin{cases} | ||
eslickj marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| \text{sgn}(x)x^2 & \text{if } |x| \ge 0.1 \\ | ||
| \sum_{i=0}^{11} c_i x^i & \text{if } |x| < 0.1 | ||
| \end{cases} | ||
|
|
||
| This function is $C^4$ smooth. The region :math:`-0.1 < x < 0.1` is replaced by an 11th order polynomial approximates that :math:`\text{sgn}(x)x^2`. The approximate function has a well behaved derivatives at :math:`x=0`. If you need to use this function with very small numbers and high accuracy is important, you can scale the argument up (e.g. :math:`\text{sgnsqr\_c4}(sx)/s^2`). | ||
eslickj marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| These figures show the sgnsqr(x) function compared to the smooth approximation sgnsqr_c4(x). | ||
|
|
||
| .. image:: figs/sgnsqr_f.png | ||
|
|
||
| .. image:: figs/sgnsqr_fx.png | ||
|
|
||
| .. image:: figs/sgnsqr_fxx.png | ||
|
|
||
|
|
||
| sgnsqrt_c4(x) | ||
| ~~~~~~~~~~~~ | ||
|
|
||
| This function is a signed square root approximation defined as: | ||
|
|
||
| .. math:: | ||
|
|
||
| \text{sgnsqr}(x) = \begin{cases} | ||
eslickj marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| \text{sgn}(x)|x|^{0.5} & \text{if } |x| \ge 0.1 \\ | ||
| \sum_{i=0}^{11} c_i x^i & \text{if } |x| < 0.1 | ||
| \end{cases} | ||
|
|
||
| This function is $C^4$ smooth. The region :math:`-0.1 < x < 0.1` is replaced by an 11th order polynomial approximates that :math:`\text{sgn}(x)x^2`. The approximate function has a well behaved derivatives at :math:`x=0`. If you need to use this function with very small numbers and high accuracy is important, you can scale the argument up (e.g. :math:`\text{sgnsqr\_c4}(sx)/s^2`). | ||
|
|
||
| These figures show the signed square root function compared to the smooth approximation sgnsqr_c4(x). | ||
eslickj marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .. image:: figs/sgnsqrt_c4_err.png | ||
|
|
||
| .. image:: figs/sgnsqrt_c4_f.png | ||
|
|
||
| .. image:: figs/sgnsqrt_c4_fx.png | ||
|
|
||
| .. image:: figs/sgnsqrt_c4_fxx.png | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,7 +8,7 @@ Modeling Utilities | |
| latex_printer | ||
| preprocessing | ||
| scaling | ||
|
|
||
| external_functions/index | ||
|
|
||
|
|
||
| .. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # ___________________________________________________________________________ | ||
| # | ||
| # Pyomo: Python Optimization Modeling Objects | ||
| # Copyright (c) 2008-2025 | ||
| # National Technology and Engineering Solutions of Sandia, LLC | ||
| # Under the terms of Contract DE-NA0003525 with National Technology and | ||
| # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain | ||
| # rights in this software. | ||
| # This software is distributed under the 3-clause BSD License. | ||
| # ___________________________________________________________________________ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # ___________________________________________________________________________ | ||
| # | ||
| # Pyomo: Python Optimization Modeling Objects | ||
| # Copyright (c) 2008-2025 | ||
| # National Technology and Engineering Solutions of Sandia, LLC | ||
| # Under the terms of Contract DE-NA0003525 with National Technology and | ||
| # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain | ||
| # rights in this software. | ||
| # This software is distributed under the 3-clause BSD License. | ||
| # ___________________________________________________________________________ | ||
|
|
||
| import sys | ||
| from pyomo.common.cmake_builder import build_cmake_project | ||
|
|
||
|
|
||
| def build_external_functions(user_args=[], parallel=None): | ||
| return build_cmake_project( | ||
| targets=["src"], | ||
| package_name="external_functions", | ||
| description="Useful external function", | ||
eslickj marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| user_args=["-DBUILD_AMPLASL_IF_NEEDED=ON"] + user_args, | ||
| parallel=parallel, | ||
| ) | ||
|
|
||
|
|
||
| class ExternalFunctionBuilder(object): | ||
| def __call__(self, parallel): | ||
| return build_external_functions(parallel=parallel) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| build_external_functions(sys.argv[1:]) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # ___________________________________________________________________________ | ||
| # | ||
| # Pyomo: Python Optimization Modeling Objects | ||
| # Copyright (c) 2008-2025 | ||
| # National Technology and Engineering Solutions of Sandia, LLC | ||
| # Under the terms of Contract DE-NA0003525 with National Technology and | ||
| # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain | ||
| # rights in this software. | ||
| # This software is distributed under the 3-clause BSD License. | ||
| # ___________________________________________________________________________ | ||
|
|
||
| from pyomo.common.extensions import ExtensionBuilderFactory | ||
| from pyomo.contrib.external_functions.build import ExternalFunctionBuilder | ||
|
|
||
|
|
||
| def load(): | ||
| ExtensionBuilderFactory.register("external_functions")(ExternalFunctionBuilder) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| # ___________________________________________________________________________ | ||
| # | ||
| # Pyomo: Python Optimization Modeling Objects | ||
| # Copyright (c) 2008-2025 | ||
| # National Technology and Engineering Solutions of Sandia, LLC | ||
| # Under the terms of Contract DE-NA0003525 with National Technology and | ||
| # Engineering Solutions of Sandia, LLC, the U.S. Government retains certain | ||
| # rights in this software. | ||
| # This software is distributed under the 3-clause BSD License. | ||
| # ___________________________________________________________________________ | ||
|
|
||
| cmake_minimum_required(VERSION 3.0...3.31) | ||
| # This was developed against CMake 3.0, and appears to comply with 3.5 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CMake 3.5 is mentioned in the comment but the upper limit on the version in the previous line is 3.31. Is that a typo? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really sure. Original source of this is the Pynumero cmake file and this is the same. I'm not sure what to do here. As a whole, I guess, it implies this should work with cmake 3.5, but we aren't taking any chances. The easiest thing would be to delete the comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It’s not a typo. 3.5 changed a bunch of things, and cmake started complaining loudly if you don't explicitly state you are compatible. Basically, newer versions (through 3.31 - which was current when this change was made) only promise compatibility back to the syntax / libraries / behavior provided in 3.5. Also, “min…max” is not the correct way to read that. It is closer to “min…guaranteed compatible through”. So, what we are saying is that this needs at least cmake 3, and should work with any cmake that is compatible back to version 3.31 (so cmake, if you are newer than 3.31, please behave like 3.31). |
||
|
|
||
| PROJECT( external_functions ) | ||
|
|
||
| INCLUDE( | ||
| "${CMAKE_CURRENT_SOURCE_DIR}/../../ampl_function_demo/src/FindASL.cmake") | ||
|
|
||
| # Targets in this project | ||
| OPTION(BUILD_EXTERNAL_FCN_LIBRARY | ||
| "Build the ASL external function example library" ON) | ||
|
|
||
| IF( BUILD_EXTERNAL_FCN_LIBRARY ) | ||
| ADD_LIBRARY( external_functions SHARED "functions.cpp" ) | ||
| TARGET_LINK_LIBRARIES( external_functions | ||
| PUBLIC ${ASL_LIBRARY} ${CMAKE_DL_LIBS}) | ||
| TARGET_INCLUDE_DIRECTORIES( external_functions | ||
| PUBLIC ${ASL_INCLUDE_DIR} | ||
| INTERFACE . ) | ||
| # If you need a CPP directive defined when building the library (e.g., | ||
| # for managing __declspec(dllimport) under Windows) uncomment the | ||
| # following: | ||
| #TARGET_COMPILE_DEFINITIONS( external_functions PRIVATE BUILDING_ASL_DEMO ) | ||
| #SET_TARGET_PROPERTIES( external_functions PROPERTIES ENABLE_EXPORTS 1 ) | ||
| INSTALL( TARGETS external_functions LIBRARY DESTINATION lib | ||
| RUNTIME DESTINATION lib ) | ||
| IF( BUILD_AMPLASL ) | ||
| # If we are building AMPL/asl (from FindASL), it is possible that we | ||
| # are linking against it, so we will add the appropriate dependency | ||
| add_dependencies(external_functions ampl_asl) | ||
| ENDIF() | ||
| ENDIF() | ||
Uh oh!
There was an error while loading. Please reload this page.