Forum Discussion

FICO's avatar
FICO
Community Manager
8 years ago

Is it possible that the XPRESS c-libraries can be called directly from Python using ctypes?

 I have successfully called a C dll from Python (using ctypes) that calls XPRMInit, XPRMloadmod etc but it would be nice to avoid the need to create the DLL altogether and call these functions directly.

I have tried this but XPRMload mod is returning an int 0 and I can't see how to represent the XPRMModel reference returned by XPRMloadmod from within Python.

9 Replies

  • FICO's avatar
    FICO
    Community Manager

    In xprm_rt.h, the XPRMmodel type is defined as follows:

    typedef struct Model* XPRMmodel;

    This means, that it is a pointer to a struct. In ctypes, use the void pointer c_void_p to represent that pointer. By default, ctypes assumes that the result type of a function is a c_int. You have to change it to c_void_p. Moreover, you have to set the function argument types to the c type that represents "char *": c_char_p.

       lib.XPRMloadmod.restype = c_void_p

       lib.XPRMloadmod.argtypes = [c_char_p, c_char_p]

    Now you can call the function as follows, assuming that bname and intname are normal Python strings:

    mod = lib.XPRMloadmod(bname.encode(), intname.encode())

    I am preparing an example and will attach it to this thread, when it is finished.

  • FICO's avatar
    FICO
    Community Manager

    Thanks, this is really good news.

    I'm afraid that the next hurdle I am struggling with is the int * arg for

    int XPRMrunmod(XPRMmodel model, int *returned, const char *parlist);

    I'd tried unsuccessfully with:

    mosel.XPRMrunmod.restype = c_int

    mosel.XPRMrunmod.argtypes = [c_void_p, POINTER(c_int), c_char_p]

    r = mosel.XPRMrunmod(model, result, None)

    but get

    ctypes.ArgumentError: argument 2: <class 'TypeError'>: expected LP_c_long instance instead of int

  • FICO's avatar
    FICO
    Community Manager

    The signature for XPRMrunmod is:

    lib.XPRMrunmod.restype = c_int

    lib.XPRMrunmod.argtypes = [c_void_p, POINTER(c_int), c_char_p]

    And the function call is:

    def runmod(model, parlist):

    returned = c_int()

    status = lib.XPRMrunmod(model, byref(returned), parlist.encode())

    return status, returned

    where model is of type c_void_p, coming from loadmod, and parlist is a normal Python string. The example that I will post will contain both functions and a few more.

  • FICO's avatar
    FICO
    Community Manager

    Enclosed you find a working example including two example modules for xprm_rt and xprm_mc.

  • FICO's avatar
    FICO
    Community Manager

    Thanks. That's perfect. It would probably be worth adding this to the libraries documentation as it really helps users in Python who want to keep using Mosel for the modelling.

  • FICO's avatar
    FICO
    Community Manager

    Hi Johannes, I have been testing your example files and while they work fine in Xpress 8.5, I cannot get them working in Xpress 8.6 nor 8.8.

    For example, `lib.XPRMfindident()` always returns `(0, c_void_p(None))`...

    Is there some fix to make this work with the latest Xpress?

  • FICO's avatar
    FICO
    Community Manager

    Hello Michal: The interface example provided by Johannes earlier in this discussion thread does not seem to contain any definition of ‘XPRMfindident’ – would you be willing to share the extension of the code that you are working with?

    Besides that, Release 8.6 of Xpress comes with Mosel 5 where we enforce the use of the ‘public’ marker on any entities that you wish to make visible to external programs when using standard compilation of the Mosel model (global, non-public entities will remain visible with debug compilation). I would assume that adding a ‘public’ marker (either onto the whole ‘declarations’ block or for individual entities) will correct the problem that you are reporting.

  • FICO's avatar
    FICO
    Community Manager

    Hi Susanne, you were right: I have not noticed that 8.6 started enforcing the 'public' marker (I did not even know there was such a marker). Once I added it to the Mosel model, the Python code as expected - thanks!

    You are also right that Johannes' example did not use XPRMfindident - I have added support for more functions from C library and have not noticed that this was one of those. The code itself is simple enough:

    def findident(model, name):
        returned = c_void_p()
        objType = lib.XPRMfindident(model, name.encode(), byref(returned))
        return objType, returned

    And yes, i would be willing to share my extension of the code, with the understanding that it is just an extension of an example (not a full module), written by someone wihtout much experience with Python...

    Are you thinking github, or do you have some more suitable place?

  • FICO's avatar
    FICO
    Community Manager

    Hello Michal, we do not yet have Github repository with this Python interface example: to start with, you could perhaps simply upload your updated version of the code as an attachment to this discussion thread? Please edit the header text to document the additions / revisions that you have made to the original version of the code.