├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── apopt.py ├── hs001.nl ├── run_test.bat ├── run_test.sh └── test ├── hs001.mod ├── hs001.nl └── hs001.sol /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Advanced Process Solutions, LLC. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are 4 | permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of 7 | conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list 10 | of conditions and the following disclaimer in the documentation and/or other materials 11 | provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY APMONITOR "AS IS" AND ANY EXPRESS OR IMPLIED 14 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 15 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APMONITOR OR 16 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 17 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 18 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 19 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 20 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 21 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | The views and conclusions contained in the software and documentation are those of the 24 | authors and should not be interpreted as representing official policies, either expressed 25 | or implied, of Advanced Process Solutions, LLC. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | APOPT Solver 2 | 3 | APOPT (for Advanced Process OPTimizer) is a software package for solving large-scale optimization problems of any of these forms: 4 | 5 | * Linear programming (LP) 6 | * Quadratic programming (QP) 7 | * Quadratically constrained quadratic program (QCQP) 8 | * Nonlinear programming (NLP) 9 | * Mixed integer programming (MIP) 10 | * Mixed integer linear programming (MILP) 11 | * Mixed integer nonlinear programming (MINLP) 12 | 13 | Applications of the APOPT include chemical reactors, friction stir welding, prevention of hydrate formation in deep-sea pipelines, 14 | computational biology, solid oxide fuel cells, and flight controls for Unmanned Aerial Vehicles (UAVs). APOPT is supported in 15 | AMPL, [APMonitor](http://apmonitor.com), [Gekko](https://gekko.readthedocs.io/en/latest/), and Pyomo. 16 | 17 | APOPT Online Solver for Mixed Integer Nonlinear Programming 18 | Reads output from AMPL, Pyomo, or other NL File Writers. Similar to other 19 | solvers, this script reads the model (NL) file and produces a solution (sol) file. 20 | It sends the NL file to a remote server, computes a solution (remotely), and 21 | retrieves a solution (sol) file through an internet connection. It communicates 22 | with the server http://byu.apopt.com that is hosting the APOPT solver. Contact 23 | support@apmonitor.com for support, especially if there is a feature request or a 24 | concern about a problem solution. 25 | 26 | Instructions for usage: 27 | 1. Place apopt.py in an appropriate folder in the system path (e.g. Linux, /usr/bin/) 28 | 2. Set appropriate permissions to make the script executable (e.g. chmod 775 apopt.py) 29 | 3. In AMPL, Pyomo, or other NL file write, set solver option to apopt.py 30 | 4. Test installation by running apopt.py -test 31 | 5. Visit apopt.com for additional information and solver option help 32 | 33 | Information on the APOPT solver with references can be found at the [Wikipedia article for APOPT](https://wikipedia.org/wiki/APOPT). Besides this Python client version, local executable versions of the solver are available in Linux and Windows. 34 | -------------------------------------------------------------------------------- /apopt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | APOPT Online Solver for Mixed Integer Nonlinear Programming 5 | Reads output from AMPL, Pyomo, or other NL File Writers. Similar to other 6 | solvers, this script reads the model (NL) file and produces a solution (sol) file. 7 | It sends the NL file to a remote server, computes a solution (remotely), and 8 | retrieves a solution (sol) file through an internet connection. It communicates 9 | with the server https://byu.apopt.com that is hosting the APOPT solver. Contact 10 | support@apmonitor.com for support, especially if there is a feature request or a 11 | concern about a problem solution. 12 | 13 | Instructions for usage: 14 | 1. Place apopt.py in an appropriate folder in the system path (e.g. Linux, /usr/bin/) 15 | 2. Set appropriate permissions to make the script executable (e.g. chmod 775 apopt.py) 16 | 3. In AMPL, Pyomo, or other NL file write, set solver option to apopt.py 17 | 4. Test installation by running apopt.py -test 18 | 5. Visit apopt.com for additional information and solver option help 19 | 20 | Information on the APOPT solver with references can be found at 21 | https://wikipedia.org/wiki/APOPT 22 | Besides this Python client version, local executable versions of the solver are 23 | available in Linux and Windows. 24 | 25 | ---BSD License--- 26 | 27 | Copyright (c) 2016, Advanced Process Solutions, LLC 28 | All rights reserved. 29 | 30 | Redistribution and use in source and binary forms, with or without modification, 31 | are permitted provided that the following conditions are met: 32 | 33 | 1. Redistributions of source code must retain the above copyright notice, this list 34 | of conditions and the following disclaimer. 35 | 36 | 2. Redistributions in binary form must reproduce the above copyright notice, this 37 | list of conditions and the following disclaimer in the documentation and/or other 38 | materials provided with the distribution. 39 | 40 | 3. Neither the name of the copyright holder nor the names of its contributors may 41 | be used to endorse or promote products derived from this software without specific 42 | prior written permission. 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 45 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 46 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 47 | SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 48 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 49 | OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 51 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 52 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 53 | ''' 54 | 55 | # Import 56 | import os 57 | import sys 58 | import string 59 | from contextlib import closing 60 | 61 | # Get Python version 62 | ver = sys.version_info[0] 63 | print('Version: '+str(ver)) 64 | if ver==2: # Python 2 65 | import urllib 66 | else: # Python 3+ 67 | import urllib.request, urllib.parse, urllib.error 68 | import socket 69 | 70 | def aps(server,app,aline): 71 | '''Send a request to the server \n \ 72 | server = address of server \n \ 73 | app = application name \n \ 74 | aline = line to send to server \n''' 75 | try: 76 | # Web-server URL address 77 | url_base = server.strip() + '/online/apopt.php' 78 | app = app.lower() 79 | app.replace(" ","") 80 | if ver==2: # Python 2 81 | params = urllib.urlencode({'p':app,'a':aline}) 82 | f = urllib.urlopen(url_base,params) 83 | response = f.read() 84 | else: # Python 3+ 85 | params = urllib.parse.urlencode({'p':app,'a':aline}) 86 | en_params = params.encode() 87 | f = urllib.request.urlopen(url_base,en_params) 88 | en_response = f.read() 89 | response = en_response.decode() 90 | except: 91 | response = 'Failed to connect to server' 92 | return response 93 | 94 | def nl_load(server,app,filename): 95 | '''Load NL model file \n \ 96 | server = address of server \n \ 97 | app = application name \n \ 98 | filename = NL file name''' 99 | # Load NL File 100 | f = open(filename,'r') 101 | aline = f.read() 102 | f.close() 103 | app = app.lower() 104 | app.replace(" ","") 105 | response = aps(server,app,' '+aline) 106 | y = 'Loaded NL file' 107 | return y 108 | 109 | def aps_ip(server): 110 | '''Get current IP address \n \ 111 | server = address of server''' 112 | # get ip address for web-address lookup 113 | url_base = server.strip() + '/ip.php' 114 | if ver==2: # Python 2 115 | f = urllib.urlopen(url_base) 116 | ip = string.strip(f.read()) 117 | else: # Python 3+ 118 | f = urllib.request.urlopen(url_base) 119 | fip = f.read() 120 | ip = fip.decode().strip() 121 | return ip 122 | 123 | def aps_sol(server,app,stub): 124 | '''Retrieve solution results\n \ 125 | server = address of server \n \ 126 | app = application name ''' 127 | # Retrieve IP address 128 | ip = aps_ip(server) 129 | # Web-server URL address 130 | app = app.lower() 131 | app.replace(" ","") 132 | url = server.strip() + '/online/' + ip + '_' + app + '/' + ip + '_' + app + '.sol' 133 | 134 | if ver==2: # Python 2 135 | f = urllib.urlopen(url) 136 | else: # Python 3+ 137 | f = urllib.request.urlopen(url) 138 | 139 | # Send request to web-server 140 | solution = f.read() # Write the file 141 | 142 | # Write the file 143 | sol_file = stub + '.sol' 144 | fh = open(sol_file,'w') 145 | 146 | # possible problem here if file isn't able to open 147 | if ver==2: 148 | fh.write(solution.replace('\r','')) 149 | else: 150 | en_solution = solution.decode().replace('\r','') 151 | fh.write(en_solution) 152 | fh.close() 153 | 154 | # Return message 155 | y = 'Successfully returned sol file' 156 | return y 157 | 158 | def aps_option(server,app,name,value): 159 | '''Load APOPT option \n \ 160 | server = address of server \n \ 161 | app = application name \n \ 162 | name = option \n \ 163 | value = numeric value of option ''' 164 | aline = 'option %s %f' %(name,value) 165 | app = app.lower() 166 | app.replace(" ","") 167 | response = aps(server,app,aline) 168 | return response 169 | 170 | #print('Number of arguments:', len(sys.argv)) 171 | #print('Argument list:', str(sys.argv)) 172 | 173 | stub = '' 174 | retrieve_solution = True 175 | for i in range(1,len(sys.argv)): 176 | arg = sys.argv[i] 177 | if (arg[0]=='-'): 178 | if (arg[1]=='?'): 179 | print('APOPT Online Solver') 180 | print('Usage:') 181 | print(' -? : Show possible flags and options') 182 | print(' -s : Return solution (sol) file') 183 | print(' -AMPL: Return solution (sol) file') 184 | print(' -test: Test installation') 185 | print('') 186 | print('Default option values are listed below') 187 | print(' minlp_maximum_iterations = 10000') 188 | print(' minlp_max_iter_with_int_sol = 500') 189 | print(' minlp_as_nlp = 0') 190 | print(' minlp_branch_method = 3') 191 | print(' minlp_gap_tol = 1.0e-2') 192 | print(' minlp_integer_tol = 1.0e-2') 193 | print(' minlp_integer_max = 2.0e9') 194 | print(' minlp_integer_leaves = 1') 195 | print(' minlp_print_level = 1') 196 | print(' nlp_maximum_iterations = 500') 197 | print(' objective_convergence_tolerance = 1.0e-6') 198 | print(' constraint_convergence_tolerance = 1.0e-6') 199 | print('Note: Adjust solver options in apopt.py') 200 | elif (arg[1].lower()=='t'): 201 | # write example nl file 202 | stub = 'test' 203 | nl_file = stub + '.nl' 204 | fh = open(nl_file,'w') 205 | fh.write('g3 0 1 0\n') 206 | fh.write(' 1 1 1 0 0\n') 207 | fh.write(' 1 1\n') 208 | fh.write(' 0 0\n') 209 | fh.write(' 1 1 1\n') 210 | fh.write(' 0 0 0 1\n') 211 | fh.write(' 0 0 0 0 0\n') 212 | fh.write(' 1 1\n') 213 | fh.write(' 0 0\n') 214 | fh.write(' 0 0 0 0 0\n') 215 | fh.write('C0\n') 216 | fh.write('o16\n') 217 | fh.write('o5\n') 218 | fh.write('v0\n') 219 | fh.write('n2\n') 220 | fh.write('O0 0\n') 221 | fh.write('o5\n') 222 | fh.write('v0\n') 223 | fh.write('n2\n') 224 | fh.write('x1\n') 225 | fh.write('0 0.5\n') 226 | fh.write('r\n') 227 | fh.write('1 -1\n') 228 | fh.write('b\n') 229 | fh.write('3\n') 230 | fh.write('k0\n') 231 | fh.write('J0 1\n') 232 | fh.write('0 1\n') 233 | fh.write('G0 1\n') 234 | fh.write('0 0\n') 235 | fh.close() 236 | elif ((arg[1].lower()=='a') or (arg[1].lower()=='s')): 237 | retrieve_solution = True 238 | else: 239 | stub = os.path.splitext(arg)[0] 240 | 241 | if (len(stub)>=1): 242 | # server and application name 243 | # can use https if needed 244 | s = 'http://byu.apopt.com' 245 | a = ''.join(e for e in stub if e.isalnum()) 246 | 247 | # clear prior application 248 | aps(s,a,'clear all') 249 | 250 | # load nl file 251 | nl_load(s,a,stub+'.nl') 252 | 253 | # adjustable solver options 254 | aps_option(s,a,'minlp_maximum_iterations',10000) 255 | aps_option(s,a,'minlp_max_iter_with_int_sol',500) 256 | aps_option(s,a,'minlp_as_nlp',1) # only NLP is currently working 257 | aps_option(s,a,'minlp_branch_method',3) 258 | aps_option(s,a,'minlp_gap_tol',1.0e-2) 259 | aps_option(s,a,'minlp_integer_tol',1.0e-2) 260 | aps_option(s,a,'minlp_integer_max',2.0e9) 261 | aps_option(s,a,'minlp_integer_leaves',1) 262 | aps_option(s,a,'minlp_print_level',1) 263 | aps_option(s,a,'nlp_maximum_iterations',500) 264 | aps_option(s,a,'objective_convergence_tolerance',1.0e-6) 265 | aps_option(s,a,'constraint_convergence_tolerance',1.0e-6) 266 | 267 | # solve 268 | output = aps(s,a,'solve') 269 | print(output) 270 | 271 | # retrieve solution (sol) file 272 | if retrieve_solution: 273 | aps_sol(s,a,stub) 274 | 275 | # clear solution folder 276 | output = aps(s,a,'clear all') 277 | -------------------------------------------------------------------------------- /hs001.nl: -------------------------------------------------------------------------------- 1 | g3 1 1 0 # problem hs001 2 | 2 0 1 0 0 # vars, constraints, objectives, ranges, eqns 3 | 0 1 # nonlinear constraints, objectives 4 | 0 0 # network constraints: nonlinear, linear 5 | 0 2 0 # nonlinear vars in constraints, objectives, both 6 | 0 0 0 1 # linear network variables; functions; arith, flags 7 | 0 0 0 0 0 # discrete variables: binary, integer, nonlinear (b,c,o) 8 | 0 2 # nonzeros in Jacobian, gradients 9 | 0 0 # max name lengths: constraints, variables 10 | 0 0 0 0 0 # common exprs: b,c,o,c1,o1 11 | O0 0 12 | o0 13 | o2 14 | n100 15 | o5 16 | o1 17 | v1 18 | o5 19 | v0 20 | n2 21 | n2 22 | o5 23 | o1 24 | n1 25 | v0 26 | n2 27 | x2 28 | 0 -2 29 | 1 1 30 | b 31 | 3 32 | 2 -1.5 33 | k1 34 | 0 35 | G0 2 36 | 0 0 37 | 1 0 38 | -------------------------------------------------------------------------------- /run_test.bat: -------------------------------------------------------------------------------- 1 | python apopt.py hs001.nl 2 | pause -------------------------------------------------------------------------------- /run_test.sh: -------------------------------------------------------------------------------- 1 | python apopt.py hs001.nl 2 | -------------------------------------------------------------------------------- /test/hs001.mod: -------------------------------------------------------------------------------- 1 | options solver apopt.py; 2 | 3 | write ghs001; 4 | 5 | var x {1..2}; 6 | 7 | minimize obj: 8 | 100*(x[2] - x[1]^2)^2 + (1-x[1])^2; 9 | ; 10 | 11 | subject to constr: -1.5 <= x[2]; 12 | 13 | let x[1] := -2; 14 | let x[2] := 1; 15 | 16 | write ghs001; 17 | 18 | solve; 19 | 20 | display x; 21 | 22 | display obj; 23 | 24 | display obj + 0.0; 25 | -------------------------------------------------------------------------------- /test/hs001.nl: -------------------------------------------------------------------------------- 1 | g3 1 1 0 # problem hs001 2 | 2 0 1 0 0 # vars, constraints, objectives, ranges, eqns 3 | 0 1 # nonlinear constraints, objectives 4 | 0 0 # network constraints: nonlinear, linear 5 | 0 2 0 # nonlinear vars in constraints, objectives, both 6 | 0 0 0 1 # linear network variables; functions; arith, flags 7 | 0 0 0 0 0 # discrete variables: binary, integer, nonlinear (b,c,o) 8 | 0 2 # nonzeros in Jacobian, gradients 9 | 0 0 # max name lengths: constraints, variables 10 | 0 0 0 0 0 # common exprs: b,c,o,c1,o1 11 | O0 0 12 | o0 13 | o2 14 | n100 15 | o5 16 | o1 17 | v1 18 | o5 19 | v0 20 | n2 21 | n2 22 | o5 23 | o1 24 | n1 25 | v0 26 | n2 27 | x2 28 | 0 -2 29 | 1 1 30 | b 31 | 3 32 | 2 -1.5 33 | k1 34 | 0 35 | G0 2 36 | 0 0 37 | 1 0 38 | -------------------------------------------------------------------------------- /test/hs001.sol: -------------------------------------------------------------------------------- 1 | Successful solution 2 | 3 | Options 4 | 3 5 | 1 6 | 1 7 | 0 8 | 0 9 | 0 10 | 2 11 | 2 12 | 0.9999999727934553 13 | 0.999999956710221 14 | --------------------------------------------------------------------------------