My experiences in installing IPOPT on Mac OS X

by Peter Carbonetto
Dept. of Computer Science
University of British Columbia

IPOPT is a software package for solving nonlinear objectives subject to nonlinear constraints. It uses primal-dual interior point methodology. Importantly, it is open source.

After a great deal of time and trouble, I managed to get in working on my laptop which is running the Mac OS X operating system.1 In the following, I detail my experiences in installing the IPOPT package.

Succinctly put, the configure script did not work for IPOPT, so I had to install everything from scratch. While this was really quite a labourous process, I didn't know any other way of installing the package. On the bright side, I learned a lot about compiling fortran and C++, and linking object code, libraries and executables

1. Installing the Fortran compiler

The first problem I encounter is that I do not have a Fortran 77 compiler installed on my machine. I do have the GNU C and C++ compilers installed on my computer already (the programs gcc and g++), but the Fortran 77 compiler is also needed to compile the BLAS, LAPACK and HSL routines. A lot of people are upset that the GNU Fortran compiler g77 was not included with the Apple Developer Tools because installing it ourselves causes many extra headaches. But we'll have to make do.

There are several ways I can install a Fortran compiler. One is by downloading GNU Fortran compiler from the High Performance Computing webpage. Another route is to install g77 via Fink. Instead, I'm going to follow the route that gives me the most control: I will download and build the entire GNU Compiler Collection (GCC), then put the necessary files in the appropriate places. Even though this route is considerably more complicated, it will allow me to ensure that I have the correct version of the compiler. You see, since I'm running Mac OS X 10.3.9 I already have gcc 3.3 installed on my computer in the /usr/bin/ directory. (It is easy to check the version by typing gcc --version.) It is important that the compilers I'm using all belong from the same collection otherwise it is very likely that I will undercover linking errors. I've decided to download GCC 3.3.6 from my local university FTP mirror.

It is crucial that I do not follow the default installation for GCC, because I may end up overwriting important files. The GCC installation instructions advise the same thing. Suppose that I've chosen to install to the directory gcc-install. I've done so simply by passing the option --prefix=gcc-install to the configure script. After following the correct installation steps and waiting a couple hours for the entire package to be built, I now have a whole bunch of files and subdirectories in gcc-install. I'm only really interested in the programs and libraries necessary for compiling and linking Fortran code. I start by installing the Fortran 77 compiler with the command

  sudo mv gcc-install/bin/g77 /usr/bin/g77

In the end, I had installed the following files:

  /usr/bin/g77
  /usr/lib/libg2c.a
  /usr/lib/libg2c.la
  /usr/lib/libfrtlegin.a

2. Building the BLAS, LAPACK and HSL libraries

Now that I have a Fortran 77 compiler installed on my system, I proceed to build the libraries needed by IPOPT from scratch. First, I download the latest BLAS and LAPACK tarballs from the Netlib FTP repository. The BLAS package just consists of a bunch of Fortran files. I compile each of the individual files into object code, starting with the file caxpy.f:

  g77 -funroll-all-loops -O3 -x f77-cpp-input -c caxpy.f

This creates an object file caxpy.o. The rest of the files are compiled similarly. If you want to produce a shared library, you will want to include the -fPIC option. Also, I've noticed that the -fexceptions option should not be used as it causes linking errors down the road. Once I've compiled all the Fortran code, I create a static library via the following commands:

  ar cr libblas.a *.o
  ranlib libblas.a

I create the LAPACK library in precisely the same fashion, with the same options passed to g77. The only difference is that the files in the LAPACK tarball are strewn about in various subdirectories. In brief, the quickest way to build the LAPACK library is to use the existing Makefile and type

  make ../liblapack.a

at the command prompt with lapack/SRC being the current directory. Before we do that, however, we need to modify a few of the options passed to the Makefile. First, I move lapack/make.inc.example to lapack/make.inc. Looking at this file, I see that it specifies among other things the program used to compile the Fortran code, which is g77, exactly as I want it. Near the bottom of this text file, I change the variable LAPACKLIB to

  LAPACKLIB = liblapack.a

Now, I can type the make command in the SRC subdirectory and it should proceed to automatically create the library (this takes about ten minutes on my computer).

Lastly, I create a library with the HSL subroutines. After following the instructions in the IPOPT document for downloading the code from the HSL Archive, I create the library with the following commands:

  g77 -funroll-all-loops -O3 -x f77-cpp-input -c ma27ad.f
  g77 -funroll-all-loops -O3 -x f77-cpp-input -c mc19ad.f
  ar cr libhsl.a ma27ad.o mc19ad.o
  ranlib libhsl.a

Now I'm ready to create the IPOPT library.

3. Building the IPOPT library

I will elect not to follow the standard installation instructions (since they didn't work) and instead build the IPOPT library by hand. Basically, I'm going to follow almost the same steps as I did before. The trickiest part is that I need to modify the file Ipopt/inc/config_ipopt.h manually; the configure script does this automatically. My file looked like

  #define COIN_HAS_BLAS         1
  #define COIN_HAS_IPOPT        1
  #define COIN_HAS_LAPACK       1
  #define COIN_IPOPT_CHECKLEVEL 0
  #define COIN_IPOPT_VERBOSITY  0
  #define F77_FUNC(name,NAME)   name ## _
  #define F77_FUNC_(name,NAME)  name ## __
  #define FORTRAN_INTEGER_TYPE  int

  #define HAVE_CASSERT      1
  #define HAVE_CCTYPE       1
  #define HAVE_CFLOAT       1
  #define HAVE_CMATH        1
  #define HAVE_CSTDARG      1
  #define HAVE_CSTDIO       1
  #define HAVE_CSTDLIB      1
  #define HAVE_CTIME        1
  #define HAVE_DLFCN_H      1
  #define HAVE_INTTYPES_H   1
  #define HAVE_MA27         1
  #define HAVE_MC19         1
  #define HAVE_MEMORY_H     1
  #define HAVE_STDINT_H     1
  #define HAVE_STDLIB_H     1
  #define HAVE_STRINGS_H    1
  #define HAVE_STRING_H     1
  #define HAVE_SYS_STAT_H   1
  #define HAVE_SYS_TYPES_H  1
  #define HAVE_UNISTD_H     1
  #define MY_C_FINITE       finite
  #define PACKAGE           "ipopt"
  #define PACKAGE_BUGREPORT "http://projects.coin-or.org/..."
  #define PACKAGE_NAME      "Ipopt"
  #define PACKAGE_STRING    "Ipopt 3.2.3"
  #define PACKAGE_TARNAME   "ipopt"
  #define PACKAGE_VERSION   "3.2.3"
  #define SIZEOF_INT_P      sizeof(int*)
  #define STDC_HEADERS      1
  #define VERSION           "3.2.3"

Next, I compile the C and C++ source files into object files. For instance, a create the object file IpAdaptiveMuUpdate.o with the command

  g++ -O3 -Iipopt-include-dir -c IpAdaptiveMuUpdate.cpp

where ipopt-include-dir is the directory containing all the .h and .hpp header files. And so on. Once I've compiled all the source files, I create the static library with the commands

  ar cr libipopt.a *.o
  ranlib libipopt.a

Note that in most cases it will not make sense to archive all the object files into the library. For instance, you should not include IpMa57TSolverInterface.o unless you have downloaded that solver (I didn't). By the same token, I didn't include the code for interfacing with the AMPL and CUTEr.

4. Testing the installation with an example

I can't be positive that we built the libraries correctly without trying to run a program that actually uses IPOPT. I try the Hock and Schittkowski nonlinear programming test example no. 71, which is included with the IPOPT tarball. After copying all the static libraries to libhome, I compile and link the code with the following sequence of commands:

  g++ -Iipopt-include-dir -c hs071_nlp.cpp
  g++ -Iipopt-include-dir -c hs071_main.cpp
  g++ -o hs071 hs071_nlp.o hs071_main.o libhome/libipopt.a \
    libhome/liblapack.a libhome/libblas.a libhome/libhsl.a \
    -lg2c -lm

Now have a program which is executed by typing ./hs071. Note that the order in which I included the object files and libraries in the above lines is important. The g2c library helps us link the Fortran object code to the C++ object code (recall that I placed this library in /usr/lib).

Footnotes

1 More precisely, I'm using Mac OS X version 10.3.9 (Panther), and I have a PowerPC G4 processor. You may find that the steps I follow apply to your system, even if it isn't the same process or version of the operating system.


December 16, 2006