From f8d6f2d7b9f170b564aaa075f47e644361562d6c Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 25 Feb 2025 17:42:28 -0500 Subject: [PATCH 01/12] Add GDP_LDSDA_Solver to GDPopt documentation for the autosummary --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index 953799f0555..33fc3723577 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -204,4 +204,5 @@ GDPopt implementation and optional arguments ~pyomo.contrib.gdpopt.gloa.GDP_GLOA_Solver ~pyomo.contrib.gdpopt.ric.GDP_RIC_Solver ~pyomo.contrib.gdpopt.branch_and_bound.GDP_LBB_Solver + ~pyomo.contrib.gdpopt.ldsda.GDP_LDSDA_Solver From bb5054d7887d2580b2f1f74595198ff6ebce8666 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 25 Feb 2025 17:50:01 -0500 Subject: [PATCH 02/12] Add explanation for Logic-based Discrete-Steepest Descent AlgorithmBriefly in GDPopt documentation --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index 33fc3723577..06f32bfab98 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -189,6 +189,11 @@ To use the GDPopt-LBB solver, define your Pyomo GDP model as usual: >>> print([value(m.y1.indicator_var), value(m.y2.indicator_var)]) [True, False] +Logic-based Discrete-Steepest Descent Algorithm (LD-SDA) +-------------------------------------------------------- + +The GDPopt-LDSDA solver exploits the ordered Boolean variables in the disjunctions to solve the GDP model. + GDPopt implementation and optional arguments -------------------------------------------- From e5856eb18a37dd76f24a438449cec0276476ab58 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 27 Feb 2025 16:52:51 -0500 Subject: [PATCH 03/12] Added the Pyomo GDP model for LDSDA in the documentation --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index 06f32bfab98..e4dca9ffc6b 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -194,6 +194,56 @@ Logic-based Discrete-Steepest Descent Algorithm (LD-SDA) The GDPopt-LDSDA solver exploits the ordered Boolean variables in the disjunctions to solve the GDP model. +To use the GDPopt-LDSDA solver, define your Pyomo GDP model as usual: + +.. doctest:: + :skipif: not baron_available + + Required imports + >>> from pyomo.environ import * + >>> from pyomo.gdp import Disjunct, Disjunction + + Create a simple model + >>> m = ConcreteModel() + + Define sets + >>> I = [1, 2, 3, 4, 5] + >>> J = [1, 2, 3, 4, 5] + + Define variables + >>> m.a = Var(bounds=(-0.1, 0.3)) + >>> m.b = Var(bounds=(-0.9, -0.5)) + + Define disjuncts for Y1 + >>> m.Y1_disjuncts = Disjunct(I) + >>> for i in I: + ... m.Y1_disjuncts[i].y1_constraint = Constraint(expr=m.a == -0.1 + 0.1 * (i - 1)) + + Define disjuncts for Y2 + >>> m.Y2_disjuncts = Disjunct(J) + >>> for j in J: + ... m.Y2_disjuncts[j].y2_constraint = Constraint(expr=m.b == -0.9 + 0.1 * (j - 1)) + + Define disjunctions + >>> m.y1_disjunction = Disjunction(I, expr=[m.Y1_disjuncts[i] for i in I]) + >>> m.y2_disjunction = Disjunction(J, expr=[m.Y2_disjuncts[j] for j in J]) + + Logical constraints to enforce exactly one selection + >>> m.Y1_limit = LogicalConstraint(expr=exactly(1, [m.Y1_disjuncts[i].indicator_var for i in I])) + >>> m.Y2_limit = LogicalConstraint(expr=exactly(1, [m.Y2_disjuncts[j].indicator_var for j in J])) + + Define objective function + >>> m.obj = Objective( + ... expr=4 * m.a**2 - 2.1 * m.a**4 + (1 / 3) * m.a**6 + m.a * m.b - 4 * m.b**2 + 4 * m.b**4, + ... sense=minimize + ... ) + + Invoke the GDPopt-LDSDA solver + >>> results = SolverFactory('gdpopt.ldsda').solve(m, + ... starting_point=[1,1], + ... logical_constraint_list=[m.Y1_limit, m.Y2_limit], + ... direction_norm='Linf', + ... ) GDPopt implementation and optional arguments -------------------------------------------- From 3f7406fdd22ab58784616b8bfe6bd2527b39032e Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 27 Feb 2025 16:54:22 -0500 Subject: [PATCH 04/12] Added the documentation for the L2 and Linf direction norm and other input for the LDSDA. --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index e4dca9ffc6b..aae32b3fcf6 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -193,6 +193,8 @@ Logic-based Discrete-Steepest Descent Algorithm (LD-SDA) -------------------------------------------------------- The GDPopt-LDSDA solver exploits the ordered Boolean variables in the disjunctions to solve the GDP model. +It requires an **exclusive OR (XOR) logical constraint** to ensure that exactly one disjunct is active in each disjunction. +The solver also requires a **starting point** for the discrete variables and allows users to choose between two **direction norms**, `'L2'` and `'Linf'`, to guide the search process. To use the GDPopt-LDSDA solver, define your Pyomo GDP model as usual: From 2247e1872caa9b21d4167ebdc049a39dec79240f Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 25 Mar 2025 15:20:41 -0400 Subject: [PATCH 05/12] Update GDPopt documentation with corrected variable bounds and disjunction definitions. --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index aae32b3fcf6..786604bdd2b 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -213,13 +213,13 @@ To use the GDPopt-LDSDA solver, define your Pyomo GDP model as usual: >>> J = [1, 2, 3, 4, 5] Define variables - >>> m.a = Var(bounds=(-0.1, 0.3)) + >>> m.a = Var(bounds=(-0.3, 0.2)) >>> m.b = Var(bounds=(-0.9, -0.5)) Define disjuncts for Y1 >>> m.Y1_disjuncts = Disjunct(I) >>> for i in I: - ... m.Y1_disjuncts[i].y1_constraint = Constraint(expr=m.a == -0.1 + 0.1 * (i - 1)) + ... m.Y1_disjuncts[i].y1_constraint = Constraint(expr=m.a == -0.3 + 0.1 * (i - 1)) Define disjuncts for Y2 >>> m.Y2_disjuncts = Disjunct(J) @@ -227,8 +227,8 @@ To use the GDPopt-LDSDA solver, define your Pyomo GDP model as usual: ... m.Y2_disjuncts[j].y2_constraint = Constraint(expr=m.b == -0.9 + 0.1 * (j - 1)) Define disjunctions - >>> m.y1_disjunction = Disjunction(I, expr=[m.Y1_disjuncts[i] for i in I]) - >>> m.y2_disjunction = Disjunction(J, expr=[m.Y2_disjuncts[j] for j in J]) + >>> m.y1_disjunction = Disjunction(expr=[m.Y1_disjuncts[i] for i in I]) + >>> m.y2_disjunction = Disjunction(expr=[m.Y2_disjuncts[j] for j in J]) Logical constraints to enforce exactly one selection >>> m.Y1_limit = LogicalConstraint(expr=exactly(1, [m.Y1_disjuncts[i].indicator_var for i in I])) From 84780d4139ed9f3ef6dcbf62606127fc1bcdc324 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 26 Mar 2025 18:16:09 -0400 Subject: [PATCH 06/12] Add reference to Ovalle et al. (2025) in GDPopt documentation --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index 786604bdd2b..bd29bac86cc 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -28,6 +28,7 @@ Credit for prototyping and development can be found in the ``GDPopt`` class docu .. _Lee & Grossmann, 2001: https://doi.org/10.1016/S0098-1354(01)00732-3 .. _Lee & Grossmann, 2000: https://doi.org/10.1016/S0098-1354(00)00581-0 .. _Chen et al., 2018: https://doi.org/10.1016/B978-0-444-64241-7.50143-9 +.. _Ovalle et al., 2025: https://doi.org/10.1016/j.compchemeng.2024.108993 GDPopt can be used to solve a Pyomo.GDP concrete model in two ways. The simplest is to instantiate the generic GDPopt solver and specify the desired algorithm as an argument to the ``solve`` method: From 6629d56c365169de1b5527cf02973ec4e1db747c Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 26 Mar 2025 18:16:58 -0400 Subject: [PATCH 07/12] Add LD-SDA algorithm to GDPopt documentation --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index bd29bac86cc..31544e2108b 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -12,11 +12,12 @@ The main advantage of these techniques is their ability to solve subproblems in a reduced space, including nonlinear constraints only for ``True`` logical blocks. As a result, GDPopt is most effective for nonlinear GDP models. -Three algorithms are available in GDPopt: +Four algorithms are available in GDPopt: 1. Logic-based outer approximation (LOA) [`Turkay & Grossmann, 1996`_] 2. Global logic-based outer approximation (GLOA) [`Lee & Grossmann, 2001`_] 3. Logic-based branch-and-bound (LBB) [`Lee & Grossmann, 2001`_] +4. Logic-based discrete steepest descent algorithm (LD-SDA) [`Ovalle et al., 2025`_] Usage and implementation details for GDPopt can be found in the PSE 2018 paper (`Chen et al., 2018`_), or via its From 72182850eb6370d50d3f3c380a925d63e75000cc Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Thu, 27 Mar 2025 13:00:39 -0400 Subject: [PATCH 08/12] Add note on LogicalConstraint requirement for GDPopt-LDSDA solver. --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index 31544e2108b..5f0310125e2 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -198,6 +198,10 @@ The GDPopt-LDSDA solver exploits the ordered Boolean variables in the disjunctio It requires an **exclusive OR (XOR) logical constraint** to ensure that exactly one disjunct is active in each disjunction. The solver also requires a **starting point** for the discrete variables and allows users to choose between two **direction norms**, `'L2'` and `'Linf'`, to guide the search process. +.. note:: + + The current implementation of the GDPopt-LDSDA requires an explicit LogicalConstraint to enforce the exclusive OR condition for each disjunction. + To use the GDPopt-LDSDA solver, define your Pyomo GDP model as usual: .. doctest:: From 06790233d6bd99ad8853b59cfb8c3b1f22654267 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Tue, 1 Apr 2025 23:44:12 -0400 Subject: [PATCH 09/12] Changed the old standard of import * into pyo for the LDSDA documentation --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index 5f0310125e2..2e92f3ec893 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -208,46 +208,46 @@ To use the GDPopt-LDSDA solver, define your Pyomo GDP model as usual: :skipif: not baron_available Required imports - >>> from pyomo.environ import * + >>> import pyomo.environ as pyo >>> from pyomo.gdp import Disjunct, Disjunction Create a simple model - >>> m = ConcreteModel() + >>> m = pyo.ConcreteModel() Define sets >>> I = [1, 2, 3, 4, 5] >>> J = [1, 2, 3, 4, 5] Define variables - >>> m.a = Var(bounds=(-0.3, 0.2)) - >>> m.b = Var(bounds=(-0.9, -0.5)) + >>> m.a = pyo.Var(bounds=(-0.3, 0.2)) + >>> m.b = pyo.Var(bounds=(-0.9, -0.5)) Define disjuncts for Y1 >>> m.Y1_disjuncts = Disjunct(I) >>> for i in I: - ... m.Y1_disjuncts[i].y1_constraint = Constraint(expr=m.a == -0.3 + 0.1 * (i - 1)) + ... m.Y1_disjuncts[i].y1_constraint = pyo.Constraint(expr=m.a == -0.3 + 0.1 * (i - 1)) Define disjuncts for Y2 >>> m.Y2_disjuncts = Disjunct(J) >>> for j in J: - ... m.Y2_disjuncts[j].y2_constraint = Constraint(expr=m.b == -0.9 + 0.1 * (j - 1)) + ... m.Y2_disjuncts[j].y2_constraint = pyo.Constraint(expr=m.b == -0.9 + 0.1 * (j - 1)) Define disjunctions >>> m.y1_disjunction = Disjunction(expr=[m.Y1_disjuncts[i] for i in I]) >>> m.y2_disjunction = Disjunction(expr=[m.Y2_disjuncts[j] for j in J]) Logical constraints to enforce exactly one selection - >>> m.Y1_limit = LogicalConstraint(expr=exactly(1, [m.Y1_disjuncts[i].indicator_var for i in I])) - >>> m.Y2_limit = LogicalConstraint(expr=exactly(1, [m.Y2_disjuncts[j].indicator_var for j in J])) + >>> m.Y1_limit = pyo.LogicalConstraint(expr=exactly(1, [m.Y1_disjuncts[i].indicator_var for i in I])) + >>> m.Y2_limit = pyo.LogicalConstraint(expr=exactly(1, [m.Y2_disjuncts[j].indicator_var for j in J])) Define objective function - >>> m.obj = Objective( + >>> m.obj = pyo.Objective( ... expr=4 * m.a**2 - 2.1 * m.a**4 + (1 / 3) * m.a**6 + m.a * m.b - 4 * m.b**2 + 4 * m.b**4, - ... sense=minimize + ... sense=pyo.minimize ... ) Invoke the GDPopt-LDSDA solver - >>> results = SolverFactory('gdpopt.ldsda').solve(m, + >>> results = pyo.SolverFactory('gdpopt.ldsda').solve(m, ... starting_point=[1,1], ... logical_constraint_list=[m.Y1_limit, m.Y2_limit], ... direction_norm='Linf', From 99d6efb67885ade20dd82d9d2cd8ae6d0206033c Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 2 Apr 2025 03:19:43 -0400 Subject: [PATCH 10/12] Fix GDPopt-LDSDA documentation by adding pyo. --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index 2377f81f855..4f505357564 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -237,8 +237,8 @@ To use the GDPopt-LDSDA solver, define your Pyomo GDP model as usual: >>> m.y2_disjunction = Disjunction(expr=[m.Y2_disjuncts[j] for j in J]) Logical constraints to enforce exactly one selection - >>> m.Y1_limit = pyo.LogicalConstraint(expr=exactly(1, [m.Y1_disjuncts[i].indicator_var for i in I])) - >>> m.Y2_limit = pyo.LogicalConstraint(expr=exactly(1, [m.Y2_disjuncts[j].indicator_var for j in J])) + >>> m.Y1_limit = pyo.LogicalConstraint(expr=pyo.exactly(1, [m.Y1_disjuncts[i].indicator_var for i in I])) + >>> m.Y2_limit = pyo.LogicalConstraint(expr=pyo.exactly(1, [m.Y2_disjuncts[j].indicator_var for j in J])) Define objective function >>> m.obj = pyo.Objective( From 00ebb7327667313c9422e2aab4cbcb29883b5389 Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 2 Apr 2025 03:28:43 -0400 Subject: [PATCH 11/12] Renewed the documentation of the LBB example code. --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index 4f505357564..7414be4421f 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -160,25 +160,25 @@ To use the GDPopt-LBB solver, define your Pyomo GDP model as usual: :skipif: not baron_available Required imports - >>> from pyomo.environ import * + >>> import pyomo.environ as pyo >>> from pyomo.gdp import Disjunct, Disjunction Create a simple model - >>> m = ConcreteModel() - >>> m.x1 = Var(bounds = (0,8)) - >>> m.x2 = Var(bounds = (0,8)) - >>> m.obj = Objective(expr=m.x1 + m.x2, sense=minimize) + >>> m = pyo.ConcreteModel() + >>> m.x1 = pyo.Var(bounds = (0,8)) + >>> m.x2 = pyo.Var(bounds = (0,8)) + >>> m.obj = pyo.Objective(expr=m.x1 + m.x2, sense=pyo.minimize) >>> m.y1 = Disjunct() >>> m.y2 = Disjunct() - >>> m.y1.c1 = Constraint(expr=m.x1 >= 2) - >>> m.y1.c2 = Constraint(expr=m.x2 >= 2) - >>> m.y2.c1 = Constraint(expr=m.x1 >= 3) - >>> m.y2.c2 = Constraint(expr=m.x2 >= 3) + >>> m.y1.c1 = pyo.Constraint(expr=m.x1 >= 2) + >>> m.y1.c2 = pyo.Constraint(expr=m.x2 >= 2) + >>> m.y2.c1 = pyo.Constraint(expr=m.x1 >= 3) + >>> m.y2.c2 = pyo.Constraint(expr=m.x2 >= 3) >>> m.djn = Disjunction(expr=[m.y1, m.y2]) Invoke the GDPopt-LBB solver - >>> results = SolverFactory('gdpopt.lbb').solve(m) + >>> results = pyo.SolverFactory('gdpopt.lbb').solve(m) WARNING: 09/06/22: The GDPopt LBB algorithm currently has known issues. Please use the results with caution and report any bugs! @@ -188,7 +188,7 @@ To use the GDPopt-LBB solver, define your Pyomo GDP model as usual: >>> print(results.solver.termination_condition) optimal - >>> print([value(m.y1.indicator_var), value(m.y2.indicator_var)]) + >>> print([pyo.value(m.y1.indicator_var), pyo.value(m.y2.indicator_var)]) [True, False] Logic-based Discrete-Steepest Descent Algorithm (LD-SDA) From 4cc31df490a282be340a94ecc0d0c59aac892cff Mon Sep 17 00:00:00 2001 From: Albert Lee Date: Wed, 2 Apr 2025 03:31:14 -0400 Subject: [PATCH 12/12] Update GDPopt LOA documentation to use explicit pyo imports --- doc/OnlineDocs/explanation/solvers/gdpopt.rst | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/OnlineDocs/explanation/solvers/gdpopt.rst b/doc/OnlineDocs/explanation/solvers/gdpopt.rst index 7414be4421f..f11a6e3650b 100644 --- a/doc/OnlineDocs/explanation/solvers/gdpopt.rst +++ b/doc/OnlineDocs/explanation/solvers/gdpopt.rst @@ -65,27 +65,27 @@ An example that includes the modeling approach may be found below. :skipif: not glpk_available Required imports - >>> from pyomo.environ import * - >>> from pyomo.gdp import * + >>> import pyomo.environ as pyo + >>> from pyomo.gdp import Disjunct, Disjunction Create a simple model - >>> model = ConcreteModel(name='LOA example') + >>> model = pyo.ConcreteModel(name='LOA example') - >>> model.x = Var(bounds=(-1.2, 2)) - >>> model.y = Var(bounds=(-10,10)) - >>> model.c = Constraint(expr= model.x + model.y == 1) + >>> model.x = pyo.Var(bounds=(-1.2, 2)) + >>> model.y = pyo.Var(bounds=(-10,10)) + >>> model.c = pyo.Constraint(expr= model.x + model.y == 1) >>> model.fix_x = Disjunct() - >>> model.fix_x.c = Constraint(expr=model.x == 0) + >>> model.fix_x.c = pyo.Constraint(expr=model.x == 0) >>> model.fix_y = Disjunct() - >>> model.fix_y.c = Constraint(expr=model.y == 0) + >>> model.fix_y.c = pyo.Constraint(expr=model.y == 0) >>> model.d = Disjunction(expr=[model.fix_x, model.fix_y]) - >>> model.objective = Objective(expr=model.x + 0.1*model.y, sense=minimize) + >>> model.objective = pyo.Objective(expr=model.x + 0.1*model.y, sense=pyo.minimize) Solve the model using GDPopt - >>> results = SolverFactory('gdpopt.loa').solve( + >>> results = pyo.SolverFactory('gdpopt.loa').solve( ... model, mip_solver='glpk') # doctest: +IGNORE_RESULT Display the final solution