Skip to content

Commit cfe6460

Browse files
authored
Merge branch 'Pyomo:main' into parmest_obj_regularization
2 parents 77c2832 + fa15cc7 commit cfe6460

File tree

84 files changed

+2550
-1835
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+2550
-1835
lines changed

.codecov.yml

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
codecov:
22
notify:
33
# GHA: 5, Jenkins: 11
4-
# Accurate as of July 3, 2024
5-
# Potential to change when Python versions change
6-
after_n_builds: 16
7-
wait_for_ci: true
4+
# Accurate as of April 1, 2025
5+
# Potential to change when Python versions change. We will allow
6+
# codecov to start reporting when 80% of the reports are in. This
7+
# will hopefully help avoid an issue where codecov is not tracking
8+
# PR branches (see https://github.com/codecov/feedback/issues/692).
9+
after_n_builds: 13
10+
wait_for_ci: false
811
require_ci_to_pass: false
12+
comment:
13+
after_n_builds: 13
914
coverage:
10-
range:
11-
- 50.0
12-
- 100.0
15+
range: 70..98
1316
status:
1417
patch:
1518
default:

.github/workflows/test_branches.yml

+9-23
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,14 @@ env:
2626
CACHE_VER: v221013.1
2727
NEOS_EMAIL: [email protected]
2828
SRC_REF: ${{ github.head_ref || github.ref }}
29+
PYOMO_WORKFLOW: branch
2930

3031
jobs:
3132
lint:
3233
name: lint/style-and-typos
34+
if: |
35+
${{ ! ( contains(github.event.pull_request.title, '[WIP]') ||
36+
github.event.pull_request.draft ) }}
3337
runs-on: ubuntu-latest
3438
steps:
3539
- name: Checkout Pyomo source
@@ -48,6 +52,7 @@ jobs:
4852
with:
4953
config: ./.github/workflows/typos.toml
5054
- name: URL Checker
55+
if: env.PYOMO_WORKFLOW == 'branch'
5156
uses: urlstechie/[email protected]
5257
with:
5358
# A comma-separated list of file types to cover in the URL checks
@@ -805,7 +810,8 @@ jobs:
805810
cover:
806811
name: process-coverage-${{ matrix.TARGET }}
807812
needs: build
808-
if: ${{ false }} # turn off for branches
813+
# run even if a build job fails, but not if canceled (except for branches)
814+
if: ${{ false }}
809815
runs-on: ${{ matrix.os }}
810816
timeout-minutes: 10
811817
strategy:
@@ -876,26 +882,6 @@ jobs:
876882
$PYTHON_EXE -c "from pyomo.dataportal.parse_datacmds import \
877883
parse_data_commands; parse_data_commands(data='')"
878884
879-
- name: Update codecov uploader
880-
run: |
881-
set +e
882-
CODECOV="${GITHUB_WORKSPACE}/codecov.sh"
883-
echo "CODECOV=$CODECOV" >> $GITHUB_ENV
884-
for i in `seq 3`; do
885-
echo "Downloading current codecov script (attempt ${i})"
886-
curl -L https://codecov.io/bash -o $CODECOV
887-
if test $? == 0; then
888-
break
889-
fi
890-
DELAY=$(( RANDOM % 30 + 30))
891-
echo "Pausing $DELAY seconds before re-attempting download"
892-
sleep $DELAY
893-
done
894-
if test ! -e $CODECOV; then
895-
echo "Failed to download codecov.sh"
896-
exit 1
897-
fi
898-
899885
- name: Combine coverage reports
900886
if: github.repository_owner == 'Pyomo' || github.ref != 'refs/heads/main'
901887
run: |
@@ -934,7 +920,7 @@ jobs:
934920
935921
- name: Upload codecov reports
936922
if: github.repository_owner == 'Pyomo' || github.ref != 'refs/heads/main'
937-
uses: codecov/codecov-action@v4
923+
uses: codecov/codecov-action@v5
938924
with:
939925
files: coverage.xml
940926
token: ${{ secrets.PYOMO_CODECOV_TOKEN }}
@@ -946,7 +932,7 @@ jobs:
946932
if: |
947933
hashFiles('coverage-other.xml') != '' &&
948934
(github.repository_owner == 'Pyomo' || github.ref != 'refs/heads/main')
949-
uses: codecov/codecov-action@v4
935+
uses: codecov/codecov-action@v5
950936
with:
951937
files: coverage-other.xml
952938
token: ${{ secrets.PYOMO_CODECOV_TOKEN }}

.github/workflows/test_pr_and_main.yml

+26-25
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,18 @@ env:
3434
CACHE_VER: v221013.1
3535
NEOS_EMAIL: [email protected]
3636
SRC_REF: ${{ github.head_ref || github.ref }}
37+
PYOMO_WORKFLOW: |
38+
${{ ( contains(github.event.pull_request.title, '[WIP]') ||
39+
github.event.pull_request.draft ) && 'draft_pr'
40+
|| github.event.pull_request.number && 'pr' || 'main' }}
3741
3842
jobs:
3943
lint:
4044
name: lint/style-and-typos
41-
runs-on: ubuntu-latest
4245
if: |
43-
contains(github.event.pull_request.title, '[WIP]') != true && !github.event.pull_request.draft
46+
${{ ! ( contains(github.event.pull_request.title, '[WIP]') ||
47+
github.event.pull_request.draft ) }}
48+
runs-on: ubuntu-latest
4449
steps:
4550
- name: Checkout Pyomo source
4651
uses: actions/checkout@v4
@@ -57,6 +62,21 @@ jobs:
5762
uses: crate-ci/typos@master
5863
with:
5964
config: ./.github/workflows/typos.toml
65+
- name: URL Checker
66+
if: env.PYOMO_WORKFLOW == 'branch'
67+
uses: urlstechie/[email protected]
68+
with:
69+
# A comma-separated list of file types to cover in the URL checks
70+
file_types: .md,.rst,.py
71+
# Choose whether to include file with no URLs in the prints.
72+
print_all: false
73+
# More verbose summary at the end of a run
74+
verbose: true
75+
# How many times to retry a failed request (defaults to 1)
76+
retry_count: 3
77+
# Exclude Jenkins because it's behind a firewall; ignore RTD because
78+
# a magically-generated string is triggering a failure
79+
exclude_urls: https://pyomo-jenkins.sandia.gov/,https://pyomo.readthedocs.io/en/%s/errors.html
6080

6181

6282
build:
@@ -843,7 +863,8 @@ jobs:
843863
cover:
844864
name: process-coverage-${{ matrix.TARGET }}
845865
needs: build
846-
if: success() || failure() # run even if a build job fails, but not if cancelled
866+
# run even if a build job fails, but not if canceled (except for branches)
867+
if: success() || failure()
847868
runs-on: ${{ matrix.os }}
848869
timeout-minutes: 10
849870
strategy:
@@ -914,26 +935,6 @@ jobs:
914935
$PYTHON_EXE -c "from pyomo.dataportal.parse_datacmds import \
915936
parse_data_commands; parse_data_commands(data='')"
916937
917-
- name: Update codecov uploader
918-
run: |
919-
set +e
920-
CODECOV="${GITHUB_WORKSPACE}/codecov.sh"
921-
echo "CODECOV=$CODECOV" >> $GITHUB_ENV
922-
for i in `seq 3`; do
923-
echo "Downloading current codecov script (attempt ${i})"
924-
curl -L https://codecov.io/bash -o $CODECOV
925-
if test $? == 0; then
926-
break
927-
fi
928-
DELAY=$(( RANDOM % 30 + 30))
929-
echo "Pausing $DELAY seconds before re-attempting download"
930-
sleep $DELAY
931-
done
932-
if test ! -e $CODECOV; then
933-
echo "Failed to download codecov.sh"
934-
exit 1
935-
fi
936-
937938
- name: Combine coverage reports
938939
if: github.repository_owner == 'Pyomo' || github.ref != 'refs/heads/main'
939940
run: |
@@ -972,7 +973,7 @@ jobs:
972973
973974
- name: Upload codecov reports
974975
if: github.repository_owner == 'Pyomo' || github.ref != 'refs/heads/main'
975-
uses: codecov/codecov-action@v4
976+
uses: codecov/codecov-action@v5
976977
with:
977978
files: coverage.xml
978979
token: ${{ secrets.PYOMO_CODECOV_TOKEN }}
@@ -984,7 +985,7 @@ jobs:
984985
if: |
985986
hashFiles('coverage-other.xml') != '' &&
986987
(github.repository_owner == 'Pyomo' || github.ref != 'refs/heads/main')
987-
uses: codecov/codecov-action@v4
988+
uses: codecov/codecov-action@v5
988989
with:
989990
files: coverage-other.xml
990991
token: ${{ secrets.PYOMO_CODECOV_TOKEN }}

doc/OnlineDocs/explanation/solvers/gdpopt.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Three algorithms are available in GDPopt:
2020

2121
Usage and implementation details for GDPopt can be found in the PSE 2018 paper
2222
(`Chen et al., 2018`_), or via its
23-
`preprint <http://egon.cheme.cmu.edu/Papers/Chen_Pyomo_GDP_PSE2018.pdf>`_.
23+
`preprint <https://egon.cheme.cmu.edu/Papers/Chen_Pyomo_GDP_PSE2018.pdf>`_.
2424

2525
Credit for prototyping and development can be found in the ``GDPopt`` class documentation, below.
2626

doc/OnlineDocs/explanation/solvers/mindtpy.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ The following algorithms are currently available in MindtPy:
1717

1818
Usage and early implementation details for MindtPy can be found in the PSE 2018 paper Bernal et al.,
1919
(`ref <https://doi.org/10.1016/B978-0-444-64241-7.50144-0>`_,
20-
`preprint <http://egon.cheme.cmu.edu/Papers/Bernal_Chen_MindtPy_PSE2018Paper.pdf>`_).
20+
`preprint <https://egon.cheme.cmu.edu/Papers/Bernal_Chen_MindtPy_PSE2018Paper.pdf>`_).
2121
This solver implementation has been developed by `David Bernal <https://github.com/bernalde>`_
2222
and `Zedong Peng <https://github.com/ZedongPeng>`_ as part of research efforts at the `Bernal Research Group
23-
<https://bernalde.github.io/>`_ and the `Grossmann Research Group <http://egon.cheme.cmu.edu/>`_
23+
<https://bernalde.github.io/>`_ and the `Grossmann Research Group <https://egon.cheme.cmu.edu/>`_
2424
at Purdue University and Carnegie Mellon University.
2525

2626
.. _Duran & Grossmann, 1986: https://dx.doi.org/10.1007/BF02592064

doc/OnlineDocs/explanation/solvers/persistent.rst

+40-40
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,16 @@ Using Persistent Solvers
2424
The first step in using a persistent solver is to create a Pyomo model
2525
as usual.
2626

27-
>>> import pyomo.environ as pe
28-
>>> m = pe.ConcreteModel()
29-
>>> m.x = pe.Var()
30-
>>> m.y = pe.Var()
31-
>>> m.obj = pe.Objective(expr=m.x**2 + m.y**2)
32-
>>> m.c = pe.Constraint(expr=m.y >= -2*m.x + 5)
27+
>>> import pyomo.environ as pyo
28+
>>> m = pyo.ConcreteModel()
29+
>>> m.x = pyo.Var()
30+
>>> m.y = pyo.Var()
31+
>>> m.obj = pyo.Objective(expr=m.x**2 + m.y**2)
32+
>>> m.c = pyo.Constraint(expr=m.y >= -2*m.x + 5)
3333

3434
You can create an instance of a persistent solver through the SolverFactory.
3535

36-
>>> opt = pe.SolverFactory('gurobi_persistent') # doctest: +SKIP
36+
>>> opt = pyo.SolverFactory('gurobi_persistent') # doctest: +SKIP
3737

3838
This returns an instance of :py:class:`GurobiPersistent`. Now we need
3939
to tell the solver about our model.
@@ -48,7 +48,7 @@ variables and constraints. We can now solve the model.
4848
We can also add or remove variables, constraints, blocks, and
4949
objectives. For example,
5050

51-
>>> m.c2 = pe.Constraint(expr=m.y >= m.x) # doctest: +SKIP
51+
>>> m.c2 = pyo.Constraint(expr=m.y >= m.x) # doctest: +SKIP
5252
>>> opt.add_constraint(m.c2) # doctest: +SKIP
5353

5454
This tells the solver to add one new constraint but otherwise leave
@@ -69,29 +69,29 @@ code will run without error, but the solver will have an extra
6969
constraint. The solver will have both y >= -2*x + 5 and y <= x, which
7070
is not what was intended!
7171

72-
>>> m = pe.ConcreteModel() # doctest: +SKIP
73-
>>> m.x = pe.Var() # doctest: +SKIP
74-
>>> m.y = pe.Var() # doctest: +SKIP
75-
>>> m.c = pe.Constraint(expr=m.y >= -2*m.x + 5) # doctest: +SKIP
76-
>>> opt = pe.SolverFactory('gurobi_persistent') # doctest: +SKIP
72+
>>> m = pyo.ConcreteModel() # doctest: +SKIP
73+
>>> m.x = pyo.Var() # doctest: +SKIP
74+
>>> m.y = pyo.Var() # doctest: +SKIP
75+
>>> m.c = pyo.Constraint(expr=m.y >= -2*m.x + 5) # doctest: +SKIP
76+
>>> opt = pyo.SolverFactory('gurobi_persistent') # doctest: +SKIP
7777
>>> opt.set_instance(m) # doctest: +SKIP
7878
>>> # WRONG:
7979
>>> del m.c # doctest: +SKIP
80-
>>> m.c = pe.Constraint(expr=m.y <= m.x) # doctest: +SKIP
80+
>>> m.c = pyo.Constraint(expr=m.y <= m.x) # doctest: +SKIP
8181
>>> opt.add_constraint(m.c) # doctest: +SKIP
8282

8383
The correct way to do this is:
8484

85-
>>> m = pe.ConcreteModel() # doctest: +SKIP
86-
>>> m.x = pe.Var() # doctest: +SKIP
87-
>>> m.y = pe.Var() # doctest: +SKIP
88-
>>> m.c = pe.Constraint(expr=m.y >= -2*m.x + 5) # doctest: +SKIP
89-
>>> opt = pe.SolverFactory('gurobi_persistent') # doctest: +SKIP
85+
>>> m = pyo.ConcreteModel() # doctest: +SKIP
86+
>>> m.x = pyo.Var() # doctest: +SKIP
87+
>>> m.y = pyo.Var() # doctest: +SKIP
88+
>>> m.c = pyo.Constraint(expr=m.y >= -2*m.x + 5) # doctest: +SKIP
89+
>>> opt = pyo.SolverFactory('gurobi_persistent') # doctest: +SKIP
9090
>>> opt.set_instance(m) # doctest: +SKIP
9191
>>> # Correct:
9292
>>> opt.remove_constraint(m.c) # doctest: +SKIP
9393
>>> del m.c # doctest: +SKIP
94-
>>> m.c = pe.Constraint(expr=m.y <= m.x) # doctest: +SKIP
94+
>>> m.c = pyo.Constraint(expr=m.y <= m.x) # doctest: +SKIP
9595
>>> opt.add_constraint(m.c) # doctest: +SKIP
9696

9797
.. warning:: Components removed from a pyomo model must be removed
@@ -100,14 +100,14 @@ The correct way to do this is:
100100
Additionally, unexpected behavior may result if a component is
101101
modified before being removed.
102102

103-
>>> m = pe.ConcreteModel() # doctest: +SKIP
104-
>>> m.b = pe.Block() # doctest: +SKIP
105-
>>> m.b.x = pe.Var() # doctest: +SKIP
106-
>>> m.b.y = pe.Var() # doctest: +SKIP
107-
>>> m.b.c = pe.Constraint(expr=m.b.y >= -2*m.b.x + 5) # doctest: +SKIP
108-
>>> opt = pe.SolverFactory('gurobi_persistent') # doctest: +SKIP
103+
>>> m = pyo.ConcreteModel() # doctest: +SKIP
104+
>>> m.b = pyo.Block() # doctest: +SKIP
105+
>>> m.b.x = pyo.Var() # doctest: +SKIP
106+
>>> m.b.y = pyo.Var() # doctest: +SKIP
107+
>>> m.b.c = pyo.Constraint(expr=m.b.y >= -2*m.b.x + 5) # doctest: +SKIP
108+
>>> opt = pyo.SolverFactory('gurobi_persistent') # doctest: +SKIP
109109
>>> opt.set_instance(m) # doctest: +SKIP
110-
>>> m.b.c2 = pe.Constraint(expr=m.b.y <= m.b.x) # doctest: +SKIP
110+
>>> m.b.c2 = pyo.Constraint(expr=m.b.y <= m.b.x) # doctest: +SKIP
111111
>>> # ERROR: The constraint referenced by m.b.c2 does not
112112
>>> # exist in the solver model.
113113
>>> opt.remove_block(m.b) # doctest: +SKIP
@@ -117,12 +117,12 @@ the solver instance, modify it with Pyomo, and then add it back to the
117117
solver instance. The only exception is with variables. Variables may
118118
be modified and then updated with with solver:
119119

120-
>>> m = pe.ConcreteModel() # doctest: +SKIP
121-
>>> m.x = pe.Var() # doctest: +SKIP
122-
>>> m.y = pe.Var() # doctest: +SKIP
123-
>>> m.obj = pe.Objective(expr=m.x**2 + m.y**2) # doctest: +SKIP
124-
>>> m.c = pe.Constraint(expr=m.y >= -2*m.x + 5) # doctest: +SKIP
125-
>>> opt = pe.SolverFactory('gurobi_persistent') # doctest: +SKIP
120+
>>> m = pyo.ConcreteModel() # doctest: +SKIP
121+
>>> m.x = pyo.Var() # doctest: +SKIP
122+
>>> m.y = pyo.Var() # doctest: +SKIP
123+
>>> m.obj = pyo.Objective(expr=m.x**2 + m.y**2) # doctest: +SKIP
124+
>>> m.c = pyo.Constraint(expr=m.y >= -2*m.x + 5) # doctest: +SKIP
125+
>>> opt = pyo.SolverFactory('gurobi_persistent') # doctest: +SKIP
126126
>>> opt.set_instance(m) # doctest: +SKIP
127127
>>> m.x.setlb(1.0) # doctest: +SKIP
128128
>>> opt.update_var(m.x) # doctest: +SKIP
@@ -160,13 +160,13 @@ Persistent Solver Performance
160160
In order to get the best performance out of the persistent solvers, use the
161161
"save_results" flag:
162162

163-
>>> import pyomo.environ as pe
164-
>>> m = pe.ConcreteModel()
165-
>>> m.x = pe.Var()
166-
>>> m.y = pe.Var()
167-
>>> m.obj = pe.Objective(expr=m.x**2 + m.y**2)
168-
>>> m.c = pe.Constraint(expr=m.y >= -2*m.x + 5)
169-
>>> opt = pe.SolverFactory('gurobi_persistent') # doctest: +SKIP
163+
>>> import pyomo.environ as pyo
164+
>>> m = pyo.ConcreteModel()
165+
>>> m.x = pyo.Var()
166+
>>> m.y = pyo.Var()
167+
>>> m.obj = pyo.Objective(expr=m.x**2 + m.y**2)
168+
>>> m.c = pyo.Constraint(expr=m.y >= -2*m.x + 5)
169+
>>> opt = pyo.SolverFactory('gurobi_persistent') # doctest: +SKIP
170170
>>> opt.set_instance(m) # doctest: +SKIP
171171
>>> results = opt.solve(save_results=False) # doctest: +SKIP
172172

doc/OnlineDocs/explanation/solvers/pynumero/tutorial.nlp_interfaces.rst

+7-7
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Relevant imports
1010
.. doctest::
1111
:skipif: not numpy_available or not scipy_available or not asl_available
1212

13-
>>> import pyomo.environ as pe
13+
>>> import pyomo.environ as pyo
1414
>>> from pyomo.contrib.pynumero.interfaces.pyomo_nlp import PyomoNLP
1515
>>> import numpy as np
1616

@@ -19,12 +19,12 @@ Create a Pyomo model
1919
.. doctest::
2020
:skipif: not numpy_available or not scipy_available or not asl_available
2121

22-
>>> m = pe.ConcreteModel()
23-
>>> m.x = pe.Var(bounds=(-5, None))
24-
>>> m.y = pe.Var(initialize=2.5)
25-
>>> m.obj = pe.Objective(expr=m.x**2 + m.y**2)
26-
>>> m.c1 = pe.Constraint(expr=m.y == (m.x - 1)**2)
27-
>>> m.c2 = pe.Constraint(expr=m.y >= pe.exp(m.x))
22+
>>> m = pyo.ConcreteModel()
23+
>>> m.x = pyo.Var(bounds=(-5, None))
24+
>>> m.y = pyo.Var(initialize=2.5)
25+
>>> m.obj = pyo.Objective(expr=m.x**2 + m.y**2)
26+
>>> m.c1 = pyo.Constraint(expr=m.y == (m.x - 1)**2)
27+
>>> m.c2 = pyo.Constraint(expr=m.y >= pyo.exp(m.x))
2828

2929
Create a :py:class:`pyomo.contrib.pynumero.interfaces.pyomo_nlp.PyomoNLP` instance
3030

examples/gdp/small_lit/ex1_Lee.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
Taken from Example 1 of the paper "New Algorithms for Nonlinear Generalized Disjunctive Programming" by Lee and Grossmann
1414
1515
This GDP problem has one disjunction containing three terms, and exactly one of them must be true. The literature can be found here:
16-
http://egon.cheme.cmu.edu/Papers/LeeNewAlgo.pdf
16+
https://egon.cheme.cmu.edu/Papers/LeeNewAlgo.pdf
1717
1818
"""
1919

0 commit comments

Comments
 (0)