diff --git a/.pylintrc b/.pylintrc index 793d793b7..4f4a801b7 100644 --- a/.pylintrc +++ b/.pylintrc @@ -9,7 +9,7 @@ # Add files or directories to the blacklist. They should be base names, not # paths. -ignore=CVS,expopt_h.py,modified_ctypesgen.py +ignore=CVS,baked_ctypesgen.py # Pickle collected data for later comparisons. persistent=yes diff --git a/MANIFEST b/MANIFEST index 70c5b78a3..bb4b1fd80 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,6 +1,7 @@ gpkit/_mosek/__init__.py gpkit/_mosek/cli_expopt.py gpkit/_mosek/expopt.py +gpkit/_mosek/baked_ctypesgen.py gpkit/_pint/__init__.py gpkit/_pint/usd_cpi.txt gpkit/constraints/__init__.py @@ -56,7 +57,6 @@ gpkit/build.py gpkit/exceptions.py gpkit/globals.py gpkit/keydict.py -gpkit/modified_ctypesgen.py gpkit/repr_conventions.py gpkit/small_classes.py gpkit/small_scripts.py diff --git a/docs/source/installation.rst b/docs/source/installation.rst index 548c19dd7..772216ac9 100644 --- a/docs/source/installation.rst +++ b/docs/source/installation.rst @@ -18,8 +18,6 @@ GPkit interfaces with two off the shelf solvers: cvxopt, and MOSEK. Cvxopt is open source and installed by default; MOSEK requires a commercial licence or (free) academic license. -Before any of the steps below, if you are using Python 2 run ``pip install ctypesgen``. - Mac OS X - If ``which gcc`` does not return anything, install the `Apple Command Line Tools `_. - Download `MOSEK 8 `_, then: diff --git a/gpkit/_mosek/baked_ctypesgen.py b/gpkit/_mosek/baked_ctypesgen.py new file mode 100644 index 000000000..f2945fe4d --- /dev/null +++ b/gpkit/_mosek/baked_ctypesgen.py @@ -0,0 +1,581 @@ +'''Wrapper for expopt.h + +Generated with ctypesgen (see copyright statement at loader) +''' + +__docformat__ = 'restructuredtext' + +# Begin preamble + +import ctypes, os, sys +from ctypes import * + +_int_types = (c_int16, c_int32) +if hasattr(ctypes, 'c_int64'): + # Some builds of ctypes apparently do not have c_int64 + # defined; it's a pretty good bet that these builds do not + # have 64-bit pointers. + _int_types += (c_int64,) +for t in _int_types: + if sizeof(t) == sizeof(c_size_t): + c_ptrdiff_t = t +del t +del _int_types + +class c_void(Structure): + # c_void_p is a buggy return type, converting to int, so + # POINTER(None) == c_void_p is actually written as + # POINTER(c_void), so it can be treated as a real pointer. + _fields_ = [('dummy', c_int)] + +def POINTER(obj): + p = ctypes.POINTER(obj) + + # Convert None to a real NULL pointer to work around bugs + # in how ctypes handles None on 64-bit platforms + if not isinstance(p.from_param, classmethod): + def from_param(cls, x): + if x is None: + return cls() + else: + return x + p.from_param = classmethod(from_param) + + return p + +class UserString: + def __init__(self, seq): + if isinstance(seq, basestring): + self.data = seq + elif isinstance(seq, UserString): + self.data = seq.data[:] + else: + self.data = str(seq) + def __str__(self): return str(self.data) + def __repr__(self): return repr(self.data) + def __int__(self): return int(self.data) + def __long__(self): return long(self.data) + def __float__(self): return float(self.data) + def __complex__(self): return complex(self.data) + def __hash__(self): return hash(self.data) + + def __cmp__(self, string): + if isinstance(string, UserString): + return cmp(self.data, string.data) + else: + return cmp(self.data, string) + def __contains__(self, char): + return char in self.data + + def __len__(self): return len(self.data) + def __getitem__(self, index): return self.__class__(self.data[index]) + def __getslice__(self, start, end): + start = max(start, 0); end = max(end, 0) + return self.__class__(self.data[start:end]) + + def __add__(self, other): + if isinstance(other, UserString): + return self.__class__(self.data + other.data) + elif isinstance(other, basestring): + return self.__class__(self.data + other) + else: + return self.__class__(self.data + str(other)) + def __radd__(self, other): + if isinstance(other, basestring): + return self.__class__(other + self.data) + else: + return self.__class__(str(other) + self.data) + def __mul__(self, n): + return self.__class__(self.data*n) + __rmul__ = __mul__ + def __mod__(self, args): + return self.__class__(self.data % args) + + # the following methods are defined in alphabetical order: + def capitalize(self): return self.__class__(self.data.capitalize()) + def center(self, width, *args): + return self.__class__(self.data.center(width, *args)) + def count(self, sub, start=0, end=sys.maxint): + return self.data.count(sub, start, end) + def decode(self, encoding=None, errors=None): # XXX improve this? + if encoding: + if errors: + return self.__class__(self.data.decode(encoding, errors)) + else: + return self.__class__(self.data.decode(encoding)) + else: + return self.__class__(self.data.decode()) + def encode(self, encoding=None, errors=None): # XXX improve this? + if encoding: + if errors: + return self.__class__(self.data.encode(encoding, errors)) + else: + return self.__class__(self.data.encode(encoding)) + else: + return self.__class__(self.data.encode()) + def endswith(self, suffix, start=0, end=sys.maxint): + return self.data.endswith(suffix, start, end) + def expandtabs(self, tabsize=8): + return self.__class__(self.data.expandtabs(tabsize)) + def find(self, sub, start=0, end=sys.maxint): + return self.data.find(sub, start, end) + def index(self, sub, start=0, end=sys.maxint): + return self.data.index(sub, start, end) + def isalpha(self): return self.data.isalpha() + def isalnum(self): return self.data.isalnum() + def isdecimal(self): return self.data.isdecimal() + def isdigit(self): return self.data.isdigit() + def islower(self): return self.data.islower() + def isnumeric(self): return self.data.isnumeric() + def isspace(self): return self.data.isspace() + def istitle(self): return self.data.istitle() + def isupper(self): return self.data.isupper() + def join(self, seq): return self.data.join(seq) + def ljust(self, width, *args): + return self.__class__(self.data.ljust(width, *args)) + def lower(self): return self.__class__(self.data.lower()) + def lstrip(self, chars=None): return self.__class__(self.data.lstrip(chars)) + def partition(self, sep): + return self.data.partition(sep) + def replace(self, old, new, maxsplit=-1): + return self.__class__(self.data.replace(old, new, maxsplit)) + def rfind(self, sub, start=0, end=sys.maxint): + return self.data.rfind(sub, start, end) + def rindex(self, sub, start=0, end=sys.maxint): + return self.data.rindex(sub, start, end) + def rjust(self, width, *args): + return self.__class__(self.data.rjust(width, *args)) + def rpartition(self, sep): + return self.data.rpartition(sep) + def rstrip(self, chars=None): return self.__class__(self.data.rstrip(chars)) + def split(self, sep=None, maxsplit=-1): + return self.data.split(sep, maxsplit) + def rsplit(self, sep=None, maxsplit=-1): + return self.data.rsplit(sep, maxsplit) + def splitlines(self, keepends=0): return self.data.splitlines(keepends) + def startswith(self, prefix, start=0, end=sys.maxint): + return self.data.startswith(prefix, start, end) + def strip(self, chars=None): return self.__class__(self.data.strip(chars)) + def swapcase(self): return self.__class__(self.data.swapcase()) + def title(self): return self.__class__(self.data.title()) + def translate(self, *args): + return self.__class__(self.data.translate(*args)) + def upper(self): return self.__class__(self.data.upper()) + def zfill(self, width): return self.__class__(self.data.zfill(width)) + +class MutableString(UserString): + """mutable string objects + + Python strings are immutable objects. This has the advantage, that + strings may be used as dictionary keys. If this property isn't needed + and you insist on changing string values in place instead, you may cheat + and use MutableString. + + But the purpose of this class is an educational one: to prevent + people from inventing their own mutable string class derived + from UserString and than forget thereby to remove (override) the + __hash__ method inherited from UserString. This would lead to + errors that would be very hard to track down. + + A faster and better solution is to rewrite your program using lists.""" + def __init__(self, string=""): + self.data = string + def __hash__(self): + raise TypeError("unhashable type (it is mutable)") + def __setitem__(self, index, sub): + if index < 0: + index += len(self.data) + if index < 0 or index >= len(self.data): raise IndexError + self.data = self.data[:index] + sub + self.data[index+1:] + def __delitem__(self, index): + if index < 0: + index += len(self.data) + if index < 0 or index >= len(self.data): raise IndexError + self.data = self.data[:index] + self.data[index+1:] + def __setslice__(self, start, end, sub): + start = max(start, 0); end = max(end, 0) + if isinstance(sub, UserString): + self.data = self.data[:start]+sub.data+self.data[end:] + elif isinstance(sub, basestring): + self.data = self.data[:start]+sub+self.data[end:] + else: + self.data = self.data[:start]+str(sub)+self.data[end:] + def __delslice__(self, start, end): + start = max(start, 0); end = max(end, 0) + self.data = self.data[:start] + self.data[end:] + def immutable(self): + return UserString(self.data) + def __iadd__(self, other): + if isinstance(other, UserString): + self.data += other.data + elif isinstance(other, basestring): + self.data += other + else: + self.data += str(other) + return self + def __imul__(self, n): + self.data *= n + return self + +class String(MutableString, Union): + + _fields_ = [('raw', POINTER(c_char)), + ('data', c_char_p)] + + def __init__(self, obj=""): + if isinstance(obj, (str, unicode, UserString)): + self.data = str(obj) + else: + self.raw = obj + + def __len__(self): + return self.data and len(self.data) or 0 + + def from_param(cls, obj): + # Convert None or 0 + if obj is None or obj == 0: + return cls(POINTER(c_char)()) + + # Convert from String + elif isinstance(obj, String): + return obj + + # Convert from str + elif isinstance(obj, str): + return cls(obj) + + # Convert from c_char_p + elif isinstance(obj, c_char_p): + return obj + + # Convert from POINTER(c_char) + elif isinstance(obj, POINTER(c_char)): + return obj + + # Convert from raw pointer + elif isinstance(obj, int): + return cls(cast(obj, POINTER(c_char))) + + # Convert from object + else: + return String.from_param(obj._as_parameter_) + from_param = classmethod(from_param) + +def ReturnString(obj, func=None, arguments=None): + return String.from_param(obj) + +# As of ctypes 1.0, ctypes does not support custom error-checking +# functions on callbacks, nor does it support custom datatypes on +# callbacks, so we must ensure that all callbacks return +# primitive datatypes. +# +# Non-primitive return values wrapped with UNCHECKED won't be +# typechecked, and will be converted to c_void_p. +def UNCHECKED(type): + if (hasattr(type, "_type_") and isinstance(type._type_, str) + and type._type_ != "P"): + return type + else: + return c_void_p + +# ctypes doesn't have direct support for variadic functions, so we have to write +# our own wrapper class +class _variadic_function(object): + def __init__(self,func,restype,argtypes): + self.func=func + self.func.restype=restype + self.argtypes=argtypes + def _as_parameter_(self): + # So we can pass this variadic function as a function pointer + return self.func + def __call__(self,*args): + fixed_args=[] + i=0 + for argtype in self.argtypes: + # Typecheck what we can + fixed_args.append(argtype.from_param(args[i])) + i+=1 + return self.func(*fixed_args+list(args[i:])) + +# End preamble + +_libs = {} +_libdirs = [] + +# Begin loader + +# ---------------------------------------------------------------------------- +# Copyright (c) 2008 David James +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +import os.path, re, sys, glob +import ctypes +import ctypes.util + +def _environ_path(name): + if name in os.environ: + return os.environ[name].split(":") + else: + return [] + +class LibraryLoader(object): + def __init__(self): + self.other_dirs=[] + + def load_library(self,libname): + """Given the name of a library, load it.""" + paths = self.getpaths(libname) + + for path in paths: + if os.path.exists(path): + return self.load(path) + + raise ImportError("%s not found." % libname) + + def load(self,path): + """Given a path to a library, load it.""" + try: + # Darwin requires dlopen to be called with mode RTLD_GLOBAL instead + # of the default RTLD_LOCAL. Without this, you end up with + # libraries not being loadable, resulting in "Symbol not found" + # errors + if sys.platform == 'darwin': + return ctypes.CDLL(path, ctypes.RTLD_GLOBAL) + else: + return ctypes.cdll.LoadLibrary(path) + except OSError,e: + raise ImportError(e) + + def getpaths(self,libname): + """Return a list of paths where the library might be found.""" + if os.path.isabs(libname): + yield libname + + else: + for path in self.getplatformpaths(libname): + yield path + + path = ctypes.util.find_library(libname) + if path: yield path + + def getplatformpaths(self, libname): + return [] + +# Darwin (Mac OS X) + +class DarwinLibraryLoader(LibraryLoader): + name_formats = ["lib%s.dylib", "lib%s.so", "lib%s.bundle", "%s.dylib", + "%s.so", "%s.bundle", "%s"] + + def getplatformpaths(self,libname): + if os.path.pathsep in libname: + names = [libname] + else: + names = [format % libname for format in self.name_formats] + + for dir in self.getdirs(libname): + for name in names: + yield os.path.join(dir,name) + + def getdirs(self,libname): + '''Implements the dylib search as specified in Apple documentation: + + http://developer.apple.com/documentation/DeveloperTools/Conceptual/ + DynamicLibraries/Articles/DynamicLibraryUsageGuidelines.html + + Before commencing the standard search, the method first checks + the bundle's ``Frameworks`` directory if the application is running + within a bundle (OS X .app). + ''' + + dyld_fallback_library_path = _environ_path("DYLD_FALLBACK_LIBRARY_PATH") + if not dyld_fallback_library_path: + dyld_fallback_library_path = [os.path.expanduser('~/lib'), + '/usr/local/lib', '/usr/lib'] + + dirs = [] + + if '/' in libname: + dirs.extend(_environ_path("DYLD_LIBRARY_PATH")) + else: + dirs.extend(_environ_path("LD_LIBRARY_PATH")) + dirs.extend(_environ_path("DYLD_LIBRARY_PATH")) + + dirs.extend(self.other_dirs) + dirs.append(".") + + if hasattr(sys, 'frozen') and sys.frozen == 'macosx_app': + dirs.append(os.path.join( + os.environ['RESOURCEPATH'], + '..', + 'Frameworks')) + + dirs.extend(dyld_fallback_library_path) + + return dirs + +# Posix + +class PosixLibraryLoader(LibraryLoader): + _ld_so_cache = None + + def _create_ld_so_cache(self): + # Recreate search path followed by ld.so. This is going to be + # slow to build, and incorrect (ld.so uses ld.so.cache, which may + # not be up-to-date). Used only as fallback for distros without + # /sbin/ldconfig. + # + # We assume the DT_RPATH and DT_RUNPATH binary sections are omitted. + + directories = [] + for name in ("LD_LIBRARY_PATH", + "SHLIB_PATH", # HPUX + "LIBPATH", # OS/2, AIX + "LIBRARY_PATH", # BE/OS + ): + if name in os.environ: + directories.extend(os.environ[name].split(os.pathsep)) + directories.extend(self.other_dirs) + directories.append(".") + + try: directories.extend([dir.strip() for dir in open('/etc/ld.so.conf')]) + except IOError: pass + + directories.extend(['/lib', '/usr/lib', '/lib64', '/usr/lib64']) + + cache = {} + lib_re = re.compile(r'lib(.*)\.s[ol]') + ext_re = re.compile(r'\.s[ol]$') + for dir in directories: + try: + for path in glob.glob("%s/*.s[ol]*" % dir): + file = os.path.basename(path) + + # Index by filename + if file not in cache: + cache[file] = path + + # Index by library name + match = lib_re.match(file) + if match: + library = match.group(1) + if library not in cache: + cache[library] = path + except OSError: + pass + + self._ld_so_cache = cache + + def getplatformpaths(self, libname): + if self._ld_so_cache is None: + self._create_ld_so_cache() + + result = self._ld_so_cache.get(libname) + if result: yield result + + path = ctypes.util.find_library(libname) + if path: yield os.path.join("/lib",path) + +# Windows + +class _WindowsLibrary(object): + def __init__(self, path): + self.cdll = ctypes.cdll.LoadLibrary(path) + self.windll = ctypes.windll.LoadLibrary(path) + + def __getattr__(self, name): + try: return getattr(self.cdll,name) + except AttributeError: + try: return getattr(self.windll,name) + except AttributeError: + raise + +class WindowsLibraryLoader(LibraryLoader): + name_formats = ["%s.dll", "lib%s.dll", "%slib.dll"] + + def load_library(self, libname): + try: + result = LibraryLoader.load_library(self, libname) + except ImportError: + result = None + if os.path.sep not in libname: + for name in self.name_formats: + try: + result = getattr(ctypes.cdll, name % libname) + if result: + break + except WindowsError: + result = None + if result is None: + try: + result = getattr(ctypes.cdll, libname) + except WindowsError: + result = None + if result is None: + raise ImportError("%s not found." % libname) + return result + + def load(self, path): + return _WindowsLibrary(path) + + def getplatformpaths(self, libname): + if os.path.sep not in libname: + for name in self.name_formats: + dll_in_current_dir = os.path.abspath(name % libname) + if os.path.exists(dll_in_current_dir): + yield dll_in_current_dir + path = ctypes.util.find_library(name % libname) + if path: + yield path + +# Platform switching + +# If your value of sys.platform does not appear in this dict, please contact +# the Ctypesgen maintainers. + +loaderclass = { + "darwin": DarwinLibraryLoader, + "cygwin": WindowsLibraryLoader, + "win32": WindowsLibraryLoader +} + +loader = loaderclass.get(sys.platform, PosixLibraryLoader)() + +def add_library_search_dirs(other_dirs): + loader.other_dirs = other_dirs + +load_library = loader.load_library + +del loaderclass + +# End loader diff --git a/gpkit/_mosek/cli_expopt.py b/gpkit/_mosek/cli_expopt.py index 8f03680e9..850d54c4c 100644 --- a/gpkit/_mosek/cli_expopt.py +++ b/gpkit/_mosek/cli_expopt.py @@ -24,7 +24,7 @@ def error_remove_read_only(func, path, exc): func(path) # try again -def imize_fn(path=None, clearfiles=True): +def imize_fn(path=None): """Constructor for the MOSEK CLI solver function. Arguments @@ -33,7 +33,8 @@ def imize_fn(path=None, clearfiles=True): The directory in which to put the MOSEK CLI input/output files. By default uses a system-appropriate temp directory. """ - if not path: + clearfiles = path is None + if path is None: path = tempfile.mkdtemp() filename = path + os.sep + "gpkit_mosek" if "mosek_bin_dir" in settings: diff --git a/gpkit/_mosek/expopt.py b/gpkit/_mosek/expopt.py index ba20eae12..4a8240a00 100644 --- a/gpkit/_mosek/expopt.py +++ b/gpkit/_mosek/expopt.py @@ -12,15 +12,10 @@ """ from __future__ import unicode_literals, print_function - from ctypes import pointer as ptr -from ctypes import POINTER as ptr_factory -from ctypes import c_double, c_int, c_void_p - -try: - from .lib import expopt_h # pylint: disable=import-error -except Exception as e: - raise ImportError("Could not load MOSEK library: "+repr(e)) +from ctypes import c_double, c_int, CFUNCTYPE +from .baked_ctypesgen import load_library, String, c_void, POINTER, UNCHECKED +from .. import settings class ModuleShortener(object): @@ -53,30 +48,26 @@ def __getattr__(self, attribute): ------- attribute from self.module """ - return getattr(self.module, self.stub+attribute) - + return getattr(self.module, self.stub + attribute) -MSK = ModuleShortener("MSK", expopt_h) # below is MSKsolsta_enum from mosek.h # positions changed as noted because MOSEK solves the dual GP problem -# Pylint disabled because the _ naming comes from the C code -# pylint: disable=invalid-name,attribute-defined-outside-init,protected-access -MSK._SOL_STA_LOOKUPTABLE = ["UNKNOWN", - "OPTIMAL", - "DUAL_FEAS", # originally position 3 - "PRIM_FEAS", # originally position 2 - "PRIM_AND_DUAL_FEAS", - "DUAL_INFEAS_CER", # originally position 6 - "PRIM_INFEAS_CER", # originally position 5 - "NEAR_OPTIMAL", - "NEAR_DUAL_FEAS", # originally position 9 - "NEAR_PRIM_FEAS", # originally position 8 - "NEAR_PRIM_AND_DUAL_FEAS", - "NEAR_DUAL_INFEAS_CER", # originally position 12 - "NEAR_PRIM_INFEAS_CER", # originally position 11 - "INTEGER_OPTIMAL", - "NEAR_INTEGER_OPTIMAL"] +MSK_SOL_STA_LOOKUPTABLE = ["UNKNOWN", + "OPTIMAL", + "DUAL_FEAS", # originally position 3 + "PRIM_FEAS", # originally position 2 + "PRIM_AND_DUAL_FEAS", + "DUAL_INFEAS_CER", # originally position 6 + "PRIM_INFEAS_CER", # originally position 5 + "NEAR_OPTIMAL", + "NEAR_DUAL_FEAS", # originally position 9 + "NEAR_PRIM_FEAS", # originally position 8 + "NEAR_PRIM_AND_DUAL_FEAS", + "NEAR_DUAL_INFEAS_CER", # originally position 12 + "NEAR_PRIM_INFEAS_CER", # originally position 11 + "INTEGER_OPTIMAL", + "NEAR_INTEGER_OPTIMAL"] def c_array(py_array, c_type): @@ -98,8 +89,19 @@ def c_array(py_array, c_type): return (c_type * len(pya))(*pya) -@MSK.streamfunc -def printcb(void, msg): # pylint: disable=unused-argument +MSK = ModuleShortener("MSK", load_library(settings["mosek_bin_path"])) +MSK_RES_OK = 0 +if settings["mosek_version"] == "7": + MSK_IPAR_INTPNT_MAX_ITERATIONS = 28 + MSKuserhandle_t = POINTER(None) +else: + MSK_IPAR_INTPNT_MAX_ITERATIONS = 19 + MSKuserhandle_t = POINTER(c_void) +MSKstreamfunc = CFUNCTYPE(UNCHECKED(None), MSKuserhandle_t, String) + + +@MSKstreamfunc +def printcb(_, msg): """Function to handle MOSEK's internal logging To enable printing to the python console, add a line like @@ -163,7 +165,7 @@ def imize(c, A, p_idxs, *args, **kwargs): """ - r = MSK._RES_OK + r = MSK_RES_OK numcon = 1+p_idxs[-1] numter, numvar = map(int, A.shape) @@ -182,27 +184,24 @@ def imize(c, A, p_idxs, *args, **kwargs): numanz = c_int(len(A.data)) objval = c_double() - env = MSK.env_t() - prosta = MSK.prostae() - solsta = MSK.solstae() - expopttask = MSK.task_t() - expopthnd = c_void_p() + env = POINTER(c_void)() + prosta = c_int() + solsta = c_int() + expopttask = POINTER(c_void)() + expopthnd = POINTER(c_void)() # a little extra work to declare a pointer for expopthnd... - ptr_expopthnd = ptr_factory(c_void_p)(expopthnd) + ptr_expopthnd = POINTER(POINTER(c_void))(expopthnd) - if r == MSK._RES_OK: + if r == MSK_RES_OK: r = MSK._makeenv(ptr(env), None) - if r == MSK._RES_OK: + if r == MSK_RES_OK: r = MSK._makeemptytask(env, ptr(expopttask)) - if r == MSK._RES_OK: - r = MSK._linkfunctotaskstream(expopttask, - MSK._STREAM_LOG, - None, - printcb) + if r == MSK_RES_OK: + r = MSK._linkfunctotaskstream(expopttask, 0, None, printcb) - if r == MSK._RES_OK: + if r == MSK_RES_OK: # Initialize expopttask with problem data r = MSK._expoptsetup(expopttask, c_int(1), # Solve the dual formulation @@ -220,15 +219,14 @@ def imize(c, A, p_idxs, *args, **kwargs): ) # Any parameter can now be changed with standard mosek function calls - if r == MSK._RES_OK: + if r == MSK_RES_OK: r = MSK._putintparam(expopttask, - MSK._IPAR_INTPNT_MAX_ITERATIONS, + MSK_IPAR_INTPNT_MAX_ITERATIONS, c_int(200)) # Optimize, xx holds the primal optimal solution, # yy holds solution to the dual problem - - if r == MSK._RES_OK: + if r == MSK_RES_OK: r = MSK._expoptimize(expopttask, ptr(prosta), ptr(solsta), @@ -245,7 +243,7 @@ def imize(c, A, p_idxs, *args, **kwargs): MSK._deletetask(ptr(expopttask)) MSK._deleteenv(ptr(env)) - status = MSK._SOL_STA_LOOKUPTABLE[solsta.value] + status = MSK_SOL_STA_LOOKUPTABLE[solsta.value] # pylint:disable=invalid-sequence-index # Allow mosek's NEAR_DUAL_FEAS solution status, because our check in gp.py # will catch solutions that don't actually meet our tolerance # TODO: when we standardize solver status responses, revisit this. diff --git a/gpkit/build.py b/gpkit/build.py index d9b2aa4f3..9d14c9c51 100644 --- a/gpkit/build.py +++ b/gpkit/build.py @@ -164,7 +164,6 @@ def look(self): # pylint: disable=too-many-return-statements mosek_platform = "osx64x86" libpattern = "libmosek64.?.?.dylib" self.flags = "-Wl,-rpath" - elif sys.platform[:5] == "linux": rootdir = pathjoin(os.path.expanduser("~"), "mosek") mosek_platform = "linux64x86" @@ -212,24 +211,14 @@ def look(self): # pylint: disable=too-many-return-statements for expopt_file in self.expopt_files: if not isfile(expopt_file): return None - # pylint: disable=global-statement,global-variable-not-assigned - global settings settings["mosek_bin_dir"] = self.bin_dir + settings["mosek_version"] = self.version os.environ['PATH'] = os.environ['PATH'] + os.pathsep + self.bin_dir return "version %s, installed to %s" % (self.version, rootdir) def build(self): "Builds a dynamic library to GPKITBUILD or $HOME/.gpkit" - try: - # Testing the import, so the variable is intentionally not used - import ctypesgencore # pylint: disable=unused-variable - except ImportError: - log("## SKIPPING MOSEK INSTALL: CTYPESGENCORE WAS NOT FOUND") - return None - - lib_dir = replacedir(pathjoin("_mosek", "lib")) - open(pathjoin(lib_dir, "__init__.py"), 'w').close() build_dir = replacedir(pathjoin("_mosek", "build")) if "GPKITBUILD" in os.environ: @@ -271,18 +260,9 @@ def build(self): + " @executable_path/libmosek64.8.1.dylib " + pathjoin(self.bin_dir, "mskexpopt")) - if built_expopt_lib != 0: - return False + settings["mosek_bin_path"] = pathjoin(solib_dir, "expopt.so") - log("#\n# Building Python bindings for expopt and Mosek...") - log("# (if this fails on Windows, verify the mingw version)") - built_expopt_h = call("python modified_ctypesgen.py -a" + - " -l " + pathjoin(solib_dir, "expopt.so").replace("\\", "/") + # pylint: disable=line-too-long - ' -l "' + self.lib_path.replace("\\", "/") + '"' + - " -o "+pathjoin(lib_dir, "expopt_h.py") + - " "+pathjoin(build_dir, "expopt.h")) - - if built_expopt_h != 0: + if built_expopt_lib != 0: return False return True diff --git a/gpkit/modified_ctypesgen.py b/gpkit/modified_ctypesgen.py deleted file mode 100644 index db7115b83..000000000 --- a/gpkit/modified_ctypesgen.py +++ /dev/null @@ -1,207 +0,0 @@ -# from https://github.com/kanzure/ctypesgen -# -# Copyright (c) 2007-2008, Ctypesgen Developers -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# 1. Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# -# -*- coding: us-ascii -*- -# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab - -import os -import sys - -f = open("ctypesgen.log", "w") -sys.stdout = f -sys.stderr = f - -def find_names_in_modules(modules): - names = set() - for module in modules: - try: - mod = __import__(module) - except: - pass - else: - names.union(dir(module)) - return names - -import optparse, sys - -def option_callback_W(option, opt, value, parser): - # Options preceded by a "-Wl," are simply treated as though the "-Wl," - # is not there? I don't understand the purpose of this code... - if len(value) < 4 or value[0:3] != 'l,-': - raise optparse.BadOptionError("not in '-Wl,' form: %s%s" - % (opt, value)) - opt = value[2:] - if opt not in ['-L', '-R', '--rpath']: - raise optparse.BadOptionError("-Wl option must be -L, -R" - " or --rpath, not " + value[2:]) - # Push the linker option onto the list for further parsing. - parser.rargs.insert(0, value) - -def option_callback_libdir(option, opt, value, parser): - # There are two sets of linker search paths: those for use at compile time - # and those for use at runtime. Search paths specified with -L, -R, or - # --rpath are added to both sets. - parser.values.compile_libdirs.append(value) - parser.values.runtime_libdirs.append(value) - -import ctypesgencore -import ctypesgencore.messages as msgs - -if __name__=="__main__": - usage = 'usage: %prog [options] /path/to/header.h ...' - op = optparse.OptionParser(usage=usage) - - # Arguments - op.add_option('-o', '--output', dest='output', metavar='FILE', - help='write wrapper to FILE [default stdout]') - op.add_option('-l', '--library', dest='libraries', action='append', - default=[], metavar='LIBRARY', help='link to LIBRARY') - op.add_option('', '--include', dest='other_headers', action='append', - default=[], metavar='HEADER', - help='include system header HEADER (e.g. stdio.h or stdlib.h)') - op.add_option('-m', '--module', '--link-module', action='append', - dest='modules', metavar='MODULE', default=[], - help='use symbols from Python module MODULE') - op.add_option('-I', '--includedir', dest='include_search_paths', - action='append', default=[], metavar='INCLUDEDIR', - help='add INCLUDEDIR as a directory to search for headers') - op.add_option('-W', action="callback", callback=option_callback_W, - metavar="l,OPTION", type="str", - help="where OPTION is -L, -R, or --rpath") - op.add_option("-L", "-R", "--rpath", "--libdir", action="callback", - callback=option_callback_libdir, metavar="LIBDIR", type="str", - help="Add LIBDIR to the search path (both compile-time and run-time)") - op.add_option('', "--compile-libdir", action="append", - dest="compile_libdirs", metavar="LIBDIR", default=[], - help="Add LIBDIR to the compile-time library search path.") - op.add_option('', "--runtime-libdir", action="append", - dest="runtime_libdirs", metavar="LIBDIR", default=[], - help="Add LIBDIR to the run-time library search path.") - - # Parser options - op.add_option('', '--cpp', dest='cpp', default='gcc -E', - help='The command to invoke the c preprocessor, including any ' \ - 'necessary options (default: gcc -E)') - op.add_option('', '--save-preprocessed-headers', metavar='FILENAME', - dest='save_preprocessed_headers', default=None, - help='Save the preprocessed headers to the specified FILENAME') - - # Processor options - op.add_option('-a', '--all-headers', action='store_true', - dest='all_headers', default=False, - help='include symbols from all headers, including system headers') - op.add_option('', '--builtin-symbols', action='store_true', - dest='builtin_symbols', default=False, - help='include symbols automatically generated by the preprocessor') - op.add_option('', '--no-macros', action='store_false', dest='include_macros', - default=True, help="Don't output macros.") - op.add_option('-i', '--include-symbols', dest='include_symbols', - default=None, help='regular expression for symbols to always include') - op.add_option('-x', '--exclude-symbols', dest='exclude_symbols', - default=None, help='regular expression for symbols to exclude') - op.add_option('', '--no-stddef-types', action='store_true', - dest='no_stddef_types', default=False, - help='Do not support extra C types from stddef.h') - op.add_option('', '--no-gnu-types', action='store_true', - dest='no_gnu_types', default=False, - help='Do not support extra GNU C types') - op.add_option('', '--no-python-types', action='store_true', - dest='no_python_types', default=False, - help='Do not support extra C types built in to Python') - - # Printer options - op.add_option('', '--header-template', dest='header_template', default=None, - metavar='TEMPLATE', - help='Use TEMPLATE as the header template in the output file.') - op.add_option('', '--strip-build-path', dest='strip_build_path', - default=None, metavar='BUILD_PATH', - help='Strip build path from header paths in the wrapper file.') - op.add_option('', '--insert-file', dest='inserted_files', default=[], - action='append', metavar='FILENAME', - help='Add the contents of FILENAME to the end of the wrapper file.') - op.add_option('', '--output-language', dest='output_language', metavar='LANGUAGE', - default='python', - help="Choose output language (`json' or `python' [default])") - - # Error options - op.add_option('', "--all-errors", action="store_true", default=False, - dest="show_all_errors", help="Display all warnings and errors even " \ - "if they would not affect output.") - op.add_option('', "--show-long-errors", action="store_true", default=False, - dest="show_long_errors", help="Display long error messages " \ - "instead of abbreviating error messages.") - op.add_option('', "--no-macro-warnings", action="store_false", default=True, - dest="show_macro_warnings", help="Do not print macro warnings.") - - op.set_defaults(**ctypesgencore.options.default_values) - - (options, args) = op.parse_args(list(sys.argv[1:])) - options.headers = args - - # Figure out what names will be defined by imported Python modules - options.other_known_names = find_names_in_modules(options.modules) - - # Required parameters - if len(args) < 1: - msgs.error_message('No header files specified', cls='usage') - sys.exit(1) - - if len(options.libraries) == 0: - msgs.warning_message('No libraries specified', cls='usage') - - # Check output language - printer = None - if options.output_language == "python": - printer = (ctypesgencore.printer_python.WrapperPrinter - if hasattr(ctypesgencore, 'printer_python') - else ctypesgencore.printer.WrapperPrinter) - elif options.output_language == "json": - printer = ctypesgencore.printer_json.WrapperPrinter - else: - msgs.error_message("No such output language `" + options.output_language + "'", cls='usage') - sys.exit(1) - - # Step 1: Parse - descriptions=ctypesgencore.parser.parse(options.headers,options) - - # Step 2: Process - ctypesgencore.processor.process(descriptions,options) - - # Step 3: Print - printer(options.output,options,descriptions) - - msgs.status_message("Wrapping complete.") - - # Correct what may be a common mistake - if descriptions.all == []: - if not options.all_headers: - msgs.warning_message("There wasn't anything of use in the " \ - "specified header file(s). Perhaps you meant to run with " \ - "--all-headers to include objects from included sub-headers? ", - cls = 'usage')