# ___________________________________________________________________________
#
# 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 logging
import glob
from pyomo.common.tempfiles import TempfileManager
from pyomo.solvers.plugins.solvers.ASL import ASL
logger = logging.getLogger('pyomo.contrib.gjh')
[docs]
def readgjh(fname=None):
"""
Build objective gradient and constraint Jacobian
from gjh file written by the ASL gjh 'solver'.
gjh solver may be called using pyomo and 'keepfiles=True'.
Enable 'symbolic_solver_labels' as well to write
.row and col file to get variable mappings.
Parameters
----------
fname : string, optional
gjh file name. The default is None.
Returns
-------
g : list
Current objective gradient.
J : list
Current objective Jacobian.
H : list
Current objective Hessian.
variableList : list
Variables as defined by `*.col` file.
constraintList : list
Constraints as defined by `*.row` file.
"""
if fname is None:
files = list(glob.glob("*.gjh"))
fname = files.pop(0)
if len(files) > 1:
print("WARNING: More than one gjh file in current directory")
print(" Processing: %s\nIgnoring: %s" % (fname, '\n\t\t'.join(files)))
with open(fname, "r") as f:
line = "dummy_str"
# Skip top lines until g value is reached
while line != "param g :=\n":
line = f.readline()
line = f.readline()
g = []
# ; is the escape character
while line[0] != ';':
"""
When printed via ampl interface:
ampl: display g;
g [*] :=
1 0.204082
2 0.367347
3 0.44898
4 0.44898
5 0.244898
6 -0.173133
7 -0.173133
8 -0.0692532
9 0.0692532
10 0.346266
;
"""
index = int(line.split()[0]) - 1
value = float(line.split()[1])
g.append([index, value])
line = f.readline()
# Skip lines until J value is reached
while line != "param J :=\n":
line = f.readline()
line = f.readline()
J = []
while line[0] != ';':
"""
When printed via ampl interface:
ampl: display J;
J [*,*]
: 1 2 3 4 5 6
:=
1 -0.434327 0.784302 . . . -0.399833
2 2.22045e-16 . 1.46939 . . -0.831038
3 0.979592 . . 1.95918 . -0.9596
4 1.79592 . . . 2.12245 -0.692532
5 0.979592 . . . . 0
6 . -0.0640498 0.545265 . . .
7 . 0.653061 . 1.14286 . .
8 . 1.63265 . . 1.63265 .
9 . 1.63265 . . . .
10 . . 0.262481 0.262481 . .
11 . . 1.14286 . 0.653061 .
12 . . 1.95918 . . .
13 . . . 0.545265 -0.0640498 .
14 . . . 1.95918 . .
15 . . . . 1.63265 .
16 . . . . . -1
: 7 8 9 10 :=
1 0.399833 . . .
2 . 0.831038 . .
3 . . 0.9596 .
4 . . . 0.692532
6 -0.799667 0.799667 . .
7 -1.38506 . 1.38506 .
8 -1.33278 . . 1.33278
9 0 . . .
10 . -0.9596 0.9596 .
11 . -1.38506 . 1.38506
12 . 0 . .
13 . . -0.799667 0.799667
14 . . 0 .
15 . . . 0
16 1 . . .
17 -1 1 . .
18 . -1 1 .
19 . . -1 1
;
"""
if line[0] == '[':
# Jacobian row index
row = int(''.join(filter(str.isdigit, line))) - 1
line = f.readline()
column = int(line.split()[0]) - 1
value = float(line.split()[1])
J.append([row, column, value])
line = f.readline()
while line != "param H :=\n":
line = f.readline()
line = f.readline()
H = []
while line[0] != ';':
"""
When printed via ampl interface:
ampl: display H;
H [*,*]
: 1 2 3 4 5 6 :=
1 . 0.25 . . . -0.35348
2 0.25 . 0.25 . . -0.212088
3 . 0.25 . 0.25 . .
4 . . 0.25 . 0.25 .
5 . . . 0.25 . .
6 -0.35348 -0.212088 . . . -0.0999584
7 0.35348 -0.212088 -0.35348 . . 0.0999584
8 . 0.424176 -0.070696 -0.424176 . .
9 . . 0.424176 0.070696 -0.424176 .
10 . . . 0.35348 0.424176 .
: 7 8 9 10 :=
1 0.35348 . . .
2 -0.212088 0.424176 . .
3 -0.35348 -0.070696 0.424176 .
4 . -0.424176 0.070696 0.35348
5 . . -0.424176 0.424176
6 0.0999584 . . .
7 -0.299875 0.199917 . .
8 0.199917 -0.439817 0.2399 .
9 . 0.2399 -0.439817 0.199917
10 . . 0.199917 -0.199917
;
"""
if line[0] == '[':
# Hessian row index
row = int(''.join(filter(str.isdigit, line))) - 1
line = f.readline()
column = int(line.split()[0]) - 1
value = float(line.split()[1])
H.append([row, column, value])
line = f.readline()
with open(fname[:-3] + 'col', 'r') as f:
data = f.read()
variableList = data.split()
with open(fname[:-3] + 'row', 'r') as f:
data = f.read()
constraintList = data.split()
return g, J, H, variableList, constraintList
[docs]
class GJHSolver(ASL):
"""
An interface to the AMPL GJH "solver" for evaluating a model at a
point.
"""
[docs]
def __init__(self, **kwds):
kwds['type'] = 'gjh'
kwds['symbolic_solver_labels'] = True
super().__init__(**kwds)
self.options.solver = 'gjh'
self._metasolver = False
# A hackish way to hold on to the model so that we can parse the
# results.
def _initialize_callbacks(self, model):
self._model = model
self._model._gjh_info = None
super()._initialize_callbacks(model)
def _presolve(self, *args, **kwds):
super()._presolve(*args, **kwds)
self._gjh_file = self._soln_file[:-3] + 'gjh'
TempfileManager.add_tempfile(self._gjh_file, exists=False)
def _postsolve(self):
#
# TODO: We should return the information using a better data
# structure (ComponentMap?) so that the GJH solver does not need
# to be called with symbolic_solver_labels=True
#
self._model._gjh_info = readgjh(self._gjh_file)
self._model = None
return super()._postsolve()