annotate cpp/plml.cpp @ 0:0dd31a8c66bd

Initial check in to Mercurial, V.1
author samer
date Fri, 13 Jan 2012 15:29:02 +0000
parents
children 85c19a49cf7e
rev   line source
samer@0 1 /*
samer@0 2 * Prolog-MATLAB interface
samer@0 3 * Samer Abdallah (2004)
samer@0 4 * Christophe Rhodes (2005)
samer@0 5 *
samer@0 6 * These are some foreign for procedures to enable SWI Prolog to run
samer@0 7 * and communicate with a MATLAB computational engine, which is
samer@0 8 * started as a separate process on the local or a remote machine.
samer@0 9 *
samer@0 10 * Communication is handled by the MATLAB engine API (engFoo
samer@0 11 * functions) which in turn use a pair of pipes connected to the
samer@0 12 * standard input and output of the MATLAB process.
samer@0 13 *
samer@0 14 * Computations are carried out in the workspace of the MATLAB
samer@0 15 * process, but results can be transported in and out of Prolog's
samer@0 16 * memory space using the {Get,Put}Variable APIs.
samer@0 17 *
samer@0 18 * mxArrays can be created and manipulated on the Prolog side.
samer@0 19 * For example, a large numeric array can be created and send to
samer@0 20 * the engine instead of building a huge expression to evaluate.
samer@0 21 *
samer@0 22 * NOTE: memory management of mxArrays on the Prolog side is complicated
samer@0 23 * by the use of cell arrays. Currently, there are two kinds of
samer@0 24 * mxArray reference atom: garbage collected ones and non-garbage
samer@0 25 * collected ones. You should generally use GCed atoms, but mxArrays
samer@0 26 * going in or out of cell arrays should not be GCed because the
samer@0 27 * parent cell array should manage them. Hence, newly created arrays
samer@0 28 * (using CREATENUMERIC, CREATECELL and CREATESTRING) are NOT
samer@0 29 * marked for GC because you might want to put them straight into a
samer@0 30 * cell array. Also, mx atoms returned from GETCELL are not GCed.
samer@0 31 * If a new array is not going into a cell array, you should use
samer@0 32 * NEWREFGC to obtain a managed atom as soon as possible.
samer@0 33 *
samer@0 34 * If you have a managed array you want to put into a cell array,
samer@0 35 * you should use COPYNOGC to make an unmanaged DEEP COPY of the
samer@0 36 * original which can safely be put in the cell array using PUTCELL
samer@0 37 *
samer@0 38 * A better solution would be to flip the management status of a given
samer@0 39 * mx_blob atom as necessary.
samer@0 40 *
samer@0 41 * TODO
samer@0 42 *
samer@0 43 * - (See plmatlab.pl for comments about the syntax for Prolog-side
samer@0 44 * users)
samer@0 45 *
samer@0 46 * - There is a problem if the Matlab script decides to pause - there
samer@0 47 * is apparently no way to communicate a keypress to the engine.
samer@0 48 *
samer@0 49 * - Similarly, there is no way to interrupt a long computation.
samer@0 50 * Pressing Ctrl-C to interrupt Prolog seems to have some effect but
samer@0 51 * it seems to confuse the Matlab engine. Empirically, matlab
samer@0 52 * processes handle some signals (try kill -SEGV `pidof MATLAB`) but
samer@0 53 * not in a useful way.
samer@0 54 *
samer@0 55 * - There is no established protocol for freeing variables from
samer@0 56 * engGetVariable: they are likely to persist for ever, or at least
samer@0 57 * for a long time, except for those handled by the finalization of
samer@0 58 * prolog terms.
samer@0 59 *
samer@0 60 * - Memory management of mxArray references (see above notes)
samer@0 61 *
samer@0 62 * Changes
samer@0 63 * 3/10/04: Added code to retrieve logical variables.
samer@0 64 * Added error checking - eval predicates should fail on error.
samer@0 65 *
samer@0 66 * 5/10/04: Added eng::fp which points to input and output streams
samer@0 67 * of matlab process. This will enable asynchronous evals
samer@0 68 *
samer@0 69 * 22/10/04: Blob handling for mxArray corrected by liberal sprinkling
samer@0 70 * of asterisks.
samer@0 71 *
samer@0 72 * 12/12/04: Removed non-blob mxArray code and added blobs for Matlab
samer@0 73 * workspace variables.
samer@0 74 *
samer@0 75 * 13/12/04: Removed all traces of old ws var handling code.
samer@0 76 *
samer@0 77 * (Later changes may be found in the README file)
samer@0 78 */
samer@0 79
samer@0 80 #include <SWI-cpp.h>
samer@0 81 #include <stdio.h>
samer@0 82 #include "engine.h"
samer@0 83
samer@0 84 /* The maximum number of simultaneous connections to Matlab from one
samer@0 85 Prolog process. */
samer@0 86 #define MAXENGINES 4
samer@0 87 #define BUFSIZE 32768 // buffer for matlab output
samer@0 88 #define MAXCMDLEN 256
samer@0 89 // #define EVALFMT "t__ex=[];\ntry\n%s\ncatch t__ex\ndisp(getReport(t__ex))\nend"
samer@0 90 #define EVALFMT "lasterr(''); %s\nt__ex=lasterr;"
samer@0 91
samer@0 92 using namespace std;
samer@0 93
samer@0 94 // This is for a SWI Prolog BLOB type to manage mxArray pointers. It
samer@0 95 // means that the Prolog garbage collector can deal with freeing
samer@0 96 // unreferenced mxArrays automatically.
samer@0 97
samer@0 98
samer@0 99 #ifdef MX_API_VER
samer@0 100 #if MX_API_VER >= 0x07030000
samer@0 101 #else
samer@0 102 typedef int mwSize;
samer@0 103 typedef int mwIndex;
samer@0 104 #endif
samer@0 105 #else
samer@0 106 typedef int mwSize;
samer@0 107 typedef int mwIndex;
samer@0 108 #endif
samer@0 109
samer@0 110 static PL_blob_t mx_blob;
samer@0 111 static PL_blob_t mxnogc_blob;
samer@0 112 static functor_t mlerror;
samer@0 113
samer@0 114 // Extract an mxArray * from a BLOB atom
samer@0 115 static mxArray *term_to_mx(term_t t) {
samer@0 116 PL_blob_t *type;
samer@0 117 size_t len;
samer@0 118 void *p;
samer@0 119
samer@0 120 PL_get_blob(t, &p, &len, &type);
samer@0 121 if (type != &mx_blob && type != &mxnogc_blob) {
samer@0 122 throw PlException("Not an mx variable");
samer@0 123 }
samer@0 124 return *(mxArray **) p;
samer@0 125 }
samer@0 126
samer@0 127 static mxArray *ablob_to_mx(atom_t a) {
samer@0 128 return term_to_mx(PlTerm(PlAtom(a)));
samer@0 129 }
samer@0 130
samer@0 131 // This is for a SWI Prolog BLOB type to manage Matlab workspace
samer@0 132 // variables. The variable is cleared and the name reclaimed
samer@0 133 // when the blob is garbage collected. This kind of blob has no data
samer@0 134 // apart from the atom's name (ie the variable's name)
samer@0 135
samer@0 136 static PL_blob_t ws_blob;
samer@0 137
samer@0 138 // structure for keeping track of workspace variables
samer@0 139 struct wsvar {
samer@0 140 char name[8]; // designed for short machine generated names
samer@0 141 Engine *engine; // the matlab engine which owns this variable
samer@0 142 atom_t id; // the id of this engine
samer@0 143 };
samer@0 144
samer@0 145 // extract wsvar from blob term
samer@0 146 static struct wsvar *term_to_wsvar(term_t t) {
samer@0 147 PL_blob_t *type;
samer@0 148 size_t len;
samer@0 149 void *p;
samer@0 150
samer@0 151 PL_get_blob(t, &p, &len, &type);
samer@0 152 if (type != &ws_blob) {
samer@0 153 throw PlException("Not a ws variable");
samer@0 154 }
samer@0 155 return (struct wsvar *) p;
samer@0 156 }
samer@0 157
samer@0 158 // extract wsvar from atom by converting to term first
samer@0 159 static struct wsvar *atom_to_wsvar(atom_t a) {
samer@0 160 return term_to_wsvar(PlTerm(PlAtom(a)));
samer@0 161 }
samer@0 162
samer@0 163
samer@0 164 /* MATLAB engine wrapper class */
samer@0 165 class eng {
samer@0 166 public:
samer@0 167 Engine *ep; // MATLAB API engine pointer
samer@0 168 atom_t id; // atom associated with this engine
samer@0 169 char *outbuf; // buffer for textual output from MATLAB
samer@0 170
samer@0 171 eng(): ep(NULL), id(PL_new_atom("")), outbuf(NULL) {}
samer@0 172
samer@0 173 void open(const char *cmd, atom_t id) {
samer@0 174 ep=engOpen(cmd);
samer@0 175
samer@0 176 if (ep) {
samer@0 177 this->id=id;
samer@0 178 outbuf=new char[BUFSIZE];
samer@0 179 outbuf[BUFSIZE-1]=0;
samer@0 180 engOutputBuffer(ep,outbuf,BUFSIZE-1);
samer@0 181 printf("Matlab engine (%s) open.\n",PL_atom_chars(id));
samer@0 182 } else {
samer@0 183 throw PlException("open engine failed");
samer@0 184 }
samer@0 185 }
samer@0 186 void close() {
samer@0 187 engClose(ep);
samer@0 188 id = PL_new_atom("");
samer@0 189 delete [] outbuf;
samer@0 190 ep=0;
samer@0 191 }
samer@0 192
samer@0 193 bool matches(atom_t id) const { return id==this->id; }
samer@0 194 bool isOpen() const { return ep!=NULL; }
samer@0 195 };
samer@0 196
samer@0 197 // pool of engines, all initially closed
samer@0 198 static eng engines[MAXENGINES];
samer@0 199 // functor to be used to wrap array pointers
samer@0 200
samer@0 201 extern "C" {
samer@0 202 // Functions for mx array atom type
samer@0 203 int mx_release(atom_t a);
samer@0 204 int mx_compare(atom_t a, atom_t b);
samer@0 205 // int mx_write(IOSTREAM *s, atom_t a, int flags);
samer@0 206 int mxnogc_release(atom_t a);
samer@0 207
samer@0 208 // Functions for WS variable atom type
samer@0 209 int ws_release(atom_t a);
samer@0 210 // int ws_write(IOSTREAM *s, atom_t a, int flags);
samer@0 211 }
samer@0 212
samer@0 213 extern "C" {
samer@0 214 install_t install();
samer@0 215 foreign_t mlOpen(term_t servercmd, term_t engine);
samer@0 216 foreign_t mlClose(term_t engine);
samer@0 217 foreign_t mlExec(term_t engine, term_t cmd);
samer@0 218 foreign_t mlWSGet(term_t var, term_t val);
samer@0 219 foreign_t mlWSPut(term_t var, term_t val);
samer@0 220 foreign_t mlWSName(term_t engine, term_t var, term_t id);
samer@0 221 foreign_t mlWSAlloc(term_t engine, term_t var);
samer@0 222 foreign_t mlMx2Atom(term_t mx, term_t atom);
samer@0 223 foreign_t mlMx2Float(term_t mx, term_t num);
samer@0 224 foreign_t mlMx2Logical(term_t mx, term_t num);
samer@0 225 foreign_t mlMx2String(term_t mx, term_t num);
samer@0 226 foreign_t mlMxInfo(term_t mx, term_t size, term_t type);
samer@0 227 foreign_t mlMxSub2Ind(term_t mx, term_t subs, term_t ind);
samer@0 228 foreign_t mlMxGetFloat(term_t mx, term_t index, term_t value);
samer@0 229 foreign_t mlMxGetLogical(term_t mx, term_t index, term_t value);
samer@0 230 foreign_t mlMxGetCell(term_t mx, term_t index, term_t value);
samer@0 231 foreign_t mlMxGetField(term_t mx, term_t index, term_t field, term_t value);
samer@0 232 foreign_t mlMxGetReals(term_t mx, term_t values);
samer@0 233 foreign_t mlMxCreateNumeric(term_t size, term_t mx);
samer@0 234 foreign_t mlMxCreateCell(term_t size, term_t mx);
samer@0 235 foreign_t mlMxCreateString(term_t string, term_t mx);
samer@0 236 foreign_t mlMxPutFloat(term_t mx, term_t index, term_t value);
samer@0 237 foreign_t mlMxPutFloats(term_t mx, term_t index, term_t values);
samer@0 238 foreign_t mlMxPutCell(term_t mx, term_t index, term_t value);
samer@0 239 foreign_t mlMxCopyNoGC(term_t src, term_t dst);
samer@0 240 foreign_t mlMxNewRefGC(term_t src, term_t dst);
samer@0 241 }
samer@0 242
samer@0 243 install_t install() {
samer@0 244 PL_register_foreign("mlOPEN", 2, (void *)mlOpen, 0);
samer@0 245 PL_register_foreign("mlCLOSE", 1, (void *)mlClose, 0);
samer@0 246 PL_register_foreign("mlEXEC", 2, (void *)mlExec, 0);
samer@0 247 PL_register_foreign("mlWSNAME", 3, (void *)mlWSName, 0);
samer@0 248 PL_register_foreign("mlWSALLOC", 2, (void *)mlWSAlloc, 0);
samer@0 249 PL_register_foreign("mlWSGET", 2, (void *)mlWSGet,0);
samer@0 250 PL_register_foreign("mlWSPUT", 2, (void *)mlWSPut, 0);
samer@0 251 PL_register_foreign("mlMX2ATOM", 2, (void *)mlMx2Atom, 0);
samer@0 252 PL_register_foreign("mlMX2FLOAT", 2, (void *)mlMx2Float, 0);
samer@0 253 PL_register_foreign("mlMX2LOGICAL", 2, (void *)mlMx2Logical, 0);
samer@0 254 PL_register_foreign("mlMX2STRING", 2, (void *)mlMx2String, 0);
samer@0 255 PL_register_foreign("mlMXINFO", 3, (void *)mlMxInfo, 0);
samer@0 256 PL_register_foreign("mlSUB2IND", 3, (void *)mlMxSub2Ind, 0);
samer@0 257 PL_register_foreign("mlGETFLOAT", 3, (void *)mlMxGetFloat, 0);
samer@0 258 PL_register_foreign("mlGETLOGICAL", 3, (void *)mlMxGetLogical, 0);
samer@0 259 PL_register_foreign("mlGETCELL", 3, (void *)mlMxGetCell, 0);
samer@0 260 PL_register_foreign("mlGETFIELD", 4, (void *)mlMxGetField, 0);
samer@0 261 PL_register_foreign("mlGETREALS", 2, (void *)mlMxGetReals, 0);
samer@0 262 PL_register_foreign("mlCREATENUMERIC", 2, (void *)mlMxCreateNumeric, 0);
samer@0 263 PL_register_foreign("mlCREATECELL", 2, (void *)mlMxCreateCell, 0);
samer@0 264 PL_register_foreign("mlCREATESTRING", 2, (void *)mlMxCreateString, 0);
samer@0 265 PL_register_foreign("mlPUTFLOAT", 3, (void *)mlMxPutFloat, 0);
samer@0 266 PL_register_foreign("mlPUTFLOATS", 3, (void *)mlMxPutFloats, 0);
samer@0 267 PL_register_foreign("mlPUTCELL", 3, (void *)mlMxPutCell, 0);
samer@0 268 PL_register_foreign("mlCOPYNOGC", 2, (void *)mlMxCopyNoGC, 0);
samer@0 269 PL_register_foreign("mlNEWREFGC", 2, (void *)mlMxNewRefGC, 0);
samer@0 270
samer@0 271 mx_blob.magic = PL_BLOB_MAGIC;
samer@0 272 mx_blob.flags = PL_BLOB_UNIQUE;
samer@0 273 mx_blob.name = (char *)"mx";
samer@0 274 mx_blob.acquire = 0;
samer@0 275 mx_blob.release = mx_release;
samer@0 276 mx_blob.compare = mx_compare;
samer@0 277 mx_blob.write = 0; // mx_write;
samer@0 278
samer@0 279 mxnogc_blob.magic = PL_BLOB_MAGIC;
samer@0 280 mxnogc_blob.flags = PL_BLOB_UNIQUE;
samer@0 281 mxnogc_blob.name = (char *)"mxnogc";
samer@0 282 mxnogc_blob.acquire = 0;
samer@0 283 mxnogc_blob.release = mxnogc_release;
samer@0 284 mxnogc_blob.compare = mx_compare;
samer@0 285 mxnogc_blob.write = 0; // mx_write;
samer@0 286
samer@0 287 ws_blob.magic = PL_BLOB_MAGIC;
samer@0 288 ws_blob.flags = PL_BLOB_UNIQUE;
samer@0 289 ws_blob.name = (char *)"ws";
samer@0 290 ws_blob.acquire = 0;
samer@0 291 ws_blob.release = ws_release;
samer@0 292 ws_blob.compare = 0;
samer@0 293 ws_blob.write = 0;
samer@0 294
samer@0 295 mlerror=PL_new_functor(PL_new_atom("mlerror"),3);
samer@0 296 }
samer@0 297
samer@0 298 void check(int rc) { if (!rc) printf("*** plml: Something failed.\n");}
samer@0 299
samer@0 300 void check_array_index(mxArray *mx, long i)
samer@0 301 {
samer@0 302 long n = mxGetNumberOfElements(mx);
samer@0 303 if (i<=0 || i>n) throw PlException("Index out of bounds");
samer@0 304 }
samer@0 305
samer@0 306 int unify_list_sizes(term_t list, const mwSize *ints, int num)
samer@0 307 {
samer@0 308 list=PL_copy_term_ref(list);
samer@0 309
samer@0 310 for (int i=0; i<num; i++) {
samer@0 311 term_t head=PL_new_term_ref();
samer@0 312 term_t tail=PL_new_term_ref();
samer@0 313 if (!PL_unify_list(list,head,tail)) PL_fail;
samer@0 314 if (!PL_unify_integer(head,ints[i])) PL_fail;
samer@0 315 list=tail;
samer@0 316 }
samer@0 317 return PL_unify_nil(list);
samer@0 318 }
samer@0 319
samer@0 320 int unify_list_doubles(term_t list, double *x, int n)
samer@0 321 {
samer@0 322 list=PL_copy_term_ref(list);
samer@0 323
samer@0 324 for (int i=0; i<n; i++) {
samer@0 325 term_t head=PL_new_term_ref();
samer@0 326 term_t tail=PL_new_term_ref();
samer@0 327 if (!PL_unify_list(list,head,tail)) PL_fail;
samer@0 328 if (!PL_unify_float(head,x[i])) PL_fail;
samer@0 329 list=tail;
samer@0 330 }
samer@0 331 return PL_unify_nil(list);
samer@0 332 }
samer@0 333
samer@0 334 // read list of integers from term and write to int array
samer@0 335 int get_list_integers(term_t list, long *len, int *vals)
samer@0 336 {
samer@0 337 term_t head=PL_new_term_ref();
samer@0 338 long n;
samer@0 339
samer@0 340 // copy term ref so as not to modify original
samer@0 341 list=PL_copy_term_ref(list);
samer@0 342 for (n=0;PL_get_list(list,head,list);n++) {
samer@0 343 if (!PL_get_integer(head,&vals[n])) return false;
samer@0 344 }
samer@0 345 if (!PL_get_nil(list)) return false;
samer@0 346 *len=n;
samer@0 347 return true;
samer@0 348 }
samer@0 349
samer@0 350 // read list of floats from term and write to double array
samer@0 351 int get_list_doubles(term_t list, long *len, double *vals)
samer@0 352 {
samer@0 353 term_t head=PL_new_term_ref();
samer@0 354 long n;
samer@0 355
samer@0 356 // copy term ref so as not to modify original
samer@0 357 list=PL_copy_term_ref(list);
samer@0 358 for (n=0;PL_get_list(list,head,list);n++) {
samer@0 359 if (!PL_get_float(head,&vals[n])) return false;
samer@0 360 }
samer@0 361 if (!PL_get_nil(list)) return false;
samer@0 362 *len=n;
samer@0 363 return true;
samer@0 364 }
samer@0 365
samer@0 366
samer@0 367
samer@0 368 /*
samer@0 369 * Member functions for SWIs blob atoms, which allow SWI to manage
samer@0 370 * garbage collection for user-defined data types.
samer@0 371 */
samer@0 372 int mx_release(atom_t a) {
samer@0 373 mxArray *p=ablob_to_mx(a);
samer@0 374 mxDestroyArray(p);
samer@0 375 return TRUE;
samer@0 376 }
samer@0 377
samer@0 378 int mx_compare(atom_t a, atom_t b) {
samer@0 379 mxArray *pa=ablob_to_mx(a);
samer@0 380 mxArray *pb=ablob_to_mx(b);
samer@0 381 if (pa<pb) return -1;
samer@0 382 else if (pa>pb) return 1;
samer@0 383 else return 0;
samer@0 384 }
samer@0 385
samer@0 386 int mxnogc_release(atom_t a) { return TRUE; }
samer@0 387
samer@0 388 /*
samer@0 389 // this requires some jiggery pokery to handle IOSTREAMS.
samer@0 390 int mx_write(IOSTREAM *s, atom_t a, int flags) {
samer@0 391 mxArray *p=ablob_to_mx(a);
samer@0 392 fprintf(s,"<mx:%p>",p);
samer@0 393 }
samer@0 394 */
samer@0 395
samer@0 396
samer@0 397 int ws_release(atom_t a) {
samer@0 398 struct wsvar *x=atom_to_wsvar(a);
samer@0 399 // printf("."); fflush(stdout); // sweet brevity
samer@0 400
samer@0 401 char buf[16];
samer@0 402 sprintf(buf,"clear %s",x->name);
samer@0 403 engEvalString(x->engine,buf);
samer@0 404 x->name[0]=0;
samer@0 405 x->engine=0;
samer@0 406
samer@0 407 return TRUE;
samer@0 408 }
samer@0 409
samer@0 410 /* see mx_write */
samer@0 411 //int ws_write(IOSTREAM *s, atom_t a, int flags) {
samer@0 412 // struct wsvar *p=atom_to_wsvar(a);
samer@0 413 // mxArray *p=ablob_to_mx(a);
samer@0 414 // fprintf(s,"%s",p->name);
samer@0 415 //}
samer@0 416
samer@0 417
samer@0 418 /* Finds the engine associated with the given term
samer@0 419 * (which should just be an atom). Throws an exception
samer@0 420 * if the engine is not found.
samer@0 421 */
samer@0 422 static eng *findEngine(term_t id_term)
samer@0 423 {
samer@0 424 atom_t id;
samer@0 425 if(!PL_get_atom(id_term, &id)) {
samer@0 426 throw PlException("id is not an atom");
samer@0 427 }
samer@0 428 for (int i=0; i<MAXENGINES; i++) {
samer@0 429 if (engines[i].matches(id)) return &engines[i];
samer@0 430 }
samer@0 431 throw PlException("engine not found");
samer@0 432 }
samer@0 433
samer@0 434
samer@0 435 /*
samer@0 436 * Open a matlab engine using the given command and associate
samer@0 437 * it with the second term, which should be an atom.
samer@0 438 */
samer@0 439
samer@0 440 foreign_t mlOpen(term_t servercmd, term_t id_term)
samer@0 441 {
samer@0 442 try {
samer@0 443 findEngine(id_term);
samer@0 444 printf("mlOPEN/2: Engine %s already open\n",(const char *)PlTerm(id_term));
samer@0 445 PL_succeed;
samer@0 446 } catch (...) {}
samer@0 447
samer@0 448 try {
samer@0 449 // look for an unused engine structure
samer@0 450 for (int i=0; i<MAXENGINES; i++) {
samer@0 451 if (!engines[i].isOpen()) {
samer@0 452 atom_t id;
samer@0 453 check(PL_get_atom(id_term,&id));
samer@0 454 engines[i].open(PlTerm(servercmd), id);
samer@0 455 fputs(engines[i].outbuf,stdout);
samer@0 456 PL_succeed;
samer@0 457 }
samer@0 458 }
samer@0 459 return PL_warning("mlOPEN/2: no more engines available.");
samer@0 460 } catch (PlException &e) {
samer@0 461 return e.plThrow();
samer@0 462 }
samer@0 463 }
samer@0 464
samer@0 465 // Close a previously opened Matlab engine
samer@0 466 foreign_t mlClose(term_t engine) {
samer@0 467 try {
samer@0 468 findEngine(engine)->close();
samer@0 469 PL_succeed;
samer@0 470 } catch (PlException &e) {
samer@0 471 return e.plThrow();
samer@0 472 }
samer@0 473 }
samer@0 474
samer@0 475 /*
samer@0 476 * Workspace variable handling
samer@0 477 */
samer@0 478
samer@0 479 // This will create a new workspace variable with an unused name,
samer@0 480 // initialise it to an empty array (to reserve the name) and unify
samer@0 481 // the term (which must be a prolog variable) with a blob representing
samer@0 482 // the variable. This in turn points back to this engine so that
samer@0 483 // if garbage collected, the workspace variable is cleared.
samer@0 484 foreign_t mlWSAlloc(term_t eng, term_t blob) {
samer@0 485 // if varname is already bound, we should check
samer@0 486 // that the name has not been used in the workspace
samer@0 487 try {
samer@0 488 class eng *engine=findEngine(eng);
samer@0 489 struct wsvar x;
samer@0 490 int rc;
samer@0 491
samer@0 492 // in a threaded world, there would either need to be precisely
samer@0 493 // one engine per thread (so that there are no race conditions on
samer@0 494 // the Matlab side) or else these lines (down to PL_unify_blob)
samer@0 495 // need to be atomic.
samer@0 496 x.engine = engine->ep;
samer@0 497 x.id = engine->id;
samer@0 498
samer@0 499 if (engEvalString(x.engine, "t__0=uniquevar([]);"))
samer@0 500 throw PlException("Cannot execute uniquevar");
samer@0 501
samer@0 502 memset(x.name,sizeof(x.name),0);
samer@0 503 mxArray *newname=engGetVariable(x.engine, "t__0");
samer@0 504 if (newname==NULL) {
samer@0 505 engEvalString(x.engine,"clear(t__0)"); // half arsed attempt to fix variable leak
samer@0 506 throw PlException("Cannot get new variable name.");
samer@0 507 }
samer@0 508 rc = mxGetString(newname,x.name, sizeof(x.name));
samer@0 509 mxDestroyArray(newname);
samer@0 510 if (rc) throw PlException("Cannot read new variable name.");
samer@0 511
samer@0 512 return PL_unify_blob(blob,&x,sizeof(x),&ws_blob);
samer@0 513 } catch (PlException &e) {
samer@0 514 return e.plThrow();
samer@0 515 }
samer@0 516 }
samer@0 517
samer@0 518 foreign_t mlWSName(term_t blob, term_t name, term_t engine) {
samer@0 519 // if varname is already bound, we should check
samer@0 520 // that the name has not been used in the workspace
samer@0 521 try {
samer@0 522 struct wsvar *x = term_to_wsvar(blob);
samer@0 523 return ( PL_unify_atom_chars(name, x->name)
samer@0 524 && PL_unify_atom(engine, x->id));
samer@0 525 } catch (PlException &e) {
samer@0 526 PL_fail; // return e.plThrow();
samer@0 527 }
samer@0 528 }
samer@0 529
samer@0 530 // Get a named variable from the MATLAB workspace and return a term
samer@0 531 // containing a pointer to an mxArray (in Prolog's memory space).
samer@0 532 foreign_t mlWSGet(term_t var, term_t val) {
samer@0 533 try {
samer@0 534 struct wsvar *x = term_to_wsvar(var);
samer@0 535 mxArray *p = engGetVariable(x->engine, x->name);
samer@0 536 return PL_unify_blob(val, (void **)&p, sizeof(p), &mx_blob);
samer@0 537 } catch (PlException &e) {
samer@0 538 return e.plThrow();
samer@0 539 }
samer@0 540 }
samer@0 541
samer@0 542 // Put an array back in Matlab workspace under given variable name
samer@0 543 foreign_t mlWSPut(term_t var, term_t val) {
samer@0 544 try {
samer@0 545 struct wsvar *x=term_to_wsvar(var);
samer@0 546 engPutVariable(x->engine, x->name, term_to_mx(val));
samer@0 547 PL_succeed;
samer@0 548 } catch (PlException &e) {
samer@0 549 return e.plThrow();
samer@0 550 }
samer@0 551 }
samer@0 552
samer@0 553 /*
samer@0 554 * Executing MATLAB code
samer@0 555 */
samer@0 556
samer@0 557 // Call a Matlab engine to execute the given command
samer@0 558 foreign_t mlExec(term_t engine, term_t cmd)
samer@0 559 {
samer@0 560 // printf(">>> Entering mlEXEC\n");
samer@0 561 try {
samer@0 562 eng *eng=findEngine(engine);
samer@0 563 const char *cmdstr=PlTerm(cmd);
samer@0 564 char *eval_cmd;
samer@0 565 int cmdlen=strlen(cmdstr);
samer@0 566 int rc;
samer@0 567
samer@0 568 // if string is very long, send it via local mxArray
samer@0 569 if (cmdlen>MAXCMDLEN) {
samer@0 570 mxArray *mxcmd=mxCreateString(cmdstr);
samer@0 571 // printf(" >>> Putting command\n");
samer@0 572 engPutVariable(eng->ep,"t__cmd",mxcmd);
samer@0 573 // printf(" <<< Put command ok\n");
samer@0 574 mxDestroyArray(mxcmd);
samer@0 575 cmdstr="eval(t__cmd)";
samer@0 576 cmdlen=strlen(cmdstr);
samer@0 577 }
samer@0 578
samer@0 579 eval_cmd = new char[cmdlen+strlen(EVALFMT)-1];
samer@0 580 if (eval_cmd==NULL) throw PlException("Failed to allocate memory for command");
samer@0 581 sprintf(eval_cmd, EVALFMT, cmdstr);
samer@0 582 // printf(" >>> Calling Matlab engine...\n"),
samer@0 583 rc=engEvalString(eng->ep,eval_cmd);
samer@0 584 // printf(" <<< Returned from Matlab engine...\n"),
samer@0 585 delete [] eval_cmd;
samer@0 586
samer@0 587 if (rc) {
samer@0 588 // printf("*** MATLAB evaluation error. Output buffer contains:\n"),
samer@0 589 // fputs(eng->outbuf,stdout);
samer@0 590 // printf("*** throwing exception.\n");
samer@0 591 // throw PlException("MATLAB evaluation error");
samer@0 592 }
samer@0 593
samer@0 594
samer@0 595 // write whatever is in the output buffer now.
samer@0 596 fputs(eng->outbuf,stdout);
samer@0 597
samer@0 598 // SA 2010. Giving up any pretence of being thread-safe -
samer@0 599 // each engine is to be used by one Prolog thread ONLY.
samer@0 600 // If you want fancy threading stuff, do it in Prolog.
samer@0 601
samer@0 602 // printf(" >>> Getting variable\n");
samer@0 603 mxArray *lasterr = engGetVariable(eng->ep, "t__ex");
samer@0 604 // printf(" <<< Got variable\n");
samer@0 605 // if (!lasterr) throw PlException("Failed to get status information.");
samer@0 606
samer@0 607 if (mxGetNumberOfElements(lasterr)>0) {
samer@0 608 //char *string=mxArrayToString(mxGetField(lasterr,0,"message"));
samer@0 609 char *string=mxArrayToString(lasterr);
samer@0 610 mxDestroyArray(lasterr);
samer@0 611
samer@0 612 term_t desc=PL_new_term_ref();
samer@0 613 term_t cmd=PL_new_term_ref();
samer@0 614 term_t ex=PL_new_term_ref();
samer@0 615
samer@0 616 PL_put_atom_chars(desc,string);
samer@0 617 PL_put_atom_chars(cmd,cmdstr);
samer@0 618 mxFree(string);
samer@0 619 check(PL_cons_functor(ex,mlerror,engine,desc,cmd));
samer@0 620 throw PlException(ex);
samer@0 621
samer@0 622 } else mxDestroyArray(lasterr);
samer@0 623
samer@0 624 // if we've got this far, then everything went well, so
samer@0 625 // printf("<<< Returning from mlEXEC\n");
samer@0 626 PL_succeed;
samer@0 627 } catch (PlException &e) {
samer@0 628 return e.plThrow();
samer@0 629 }
samer@0 630 }
samer@0 631
samer@0 632 // Get a Prolog string out of a matlab char array
samer@0 633 foreign_t mlMx2String(term_t mx, term_t a)
samer@0 634 {
samer@0 635 try {
samer@0 636 char *str = mxArrayToString(term_to_mx(mx));
samer@0 637 if (!str) {
samer@0 638 return PL_warning("array is not a character array");
samer@0 639 }
samer@0 640 int rc = PL_unify_string_chars(a, str);
samer@0 641 mxFree(str);
samer@0 642 return rc;
samer@0 643 } catch (PlException &e) {
samer@0 644 return e.plThrow();
samer@0 645 }
samer@0 646 }
samer@0 647
samer@0 648 // Convert Matlab char array to a Prolog atom
samer@0 649 foreign_t mlMx2Atom(term_t mx, term_t a)
samer@0 650 {
samer@0 651 try {
samer@0 652 char *str = mxArrayToString(term_to_mx(mx));
samer@0 653 if (!str) {
samer@0 654 return PL_warning("array is not a character array");
samer@0 655 }
samer@0 656 int rc = PL_unify_atom_chars(a, str);
samer@0 657 mxFree(str);
samer@0 658 return rc;
samer@0 659 } catch (PlException &e) {
samer@0 660 return e.plThrow();
samer@0 661 }
samer@0 662 }
samer@0 663
samer@0 664 // Convert Matlab numerical array with one element to Prolog float
samer@0 665 foreign_t mlMx2Float(term_t mxterm, term_t a)
samer@0 666 {
samer@0 667 try {
samer@0 668 mxArray *mx = term_to_mx(mxterm);
samer@0 669 if (!mxIsDouble(mx)) {
samer@0 670 return PL_warning("not numeric");
samer@0 671 }
samer@0 672 if (mxGetNumberOfElements(mx)!=1) {
samer@0 673 return PL_warning("Not a scalar");
samer@0 674 }
samer@0 675 double x = mxGetScalar(mx);
samer@0 676
samer@0 677 return PL_unify_float(a, x);
samer@0 678 } catch (PlException &e) {
samer@0 679 return e.plThrow();
samer@0 680 }
samer@0 681 }
samer@0 682
samer@0 683 // Convert Matlab numerical (REAL) array to list
samer@0 684 foreign_t mlMxGetReals(term_t mxterm, term_t a)
samer@0 685 {
samer@0 686 try {
samer@0 687 mxArray *mx = term_to_mx(mxterm);
samer@0 688 int n = mxGetNumberOfElements(mx);
samer@0 689
samer@0 690 if (!mxIsDouble(mx)) return PL_warning("not numeric");
samer@0 691 return unify_list_doubles(a,mxGetPr(mx),n);
samer@0 692 } catch (PlException &e) {
samer@0 693 return e.plThrow();
samer@0 694 }
samer@0 695 }
samer@0 696
samer@0 697 // Convert Matlab logical or numeric array with one element to
samer@0 698 // Prolog integer 0 or 1 (does not fail or succeed depending on
samer@0 699 // logical value - this is can be done by prolog code).
samer@0 700 foreign_t mlMx2Logical(term_t mxterm, term_t a)
samer@0 701 {
samer@0 702 try {
samer@0 703 mxArray *mx = term_to_mx(mxterm);
samer@0 704 if (mxGetNumberOfElements(mx) != 1) return PL_warning("Not a scalar");
samer@0 705
samer@0 706 int f;
samer@0 707 if (mxIsLogical(mx)) {
samer@0 708 f = mxIsLogicalScalarTrue(mx) ? 1 : 0;
samer@0 709 } else if (mxIsDouble(mx)) {
samer@0 710 f = (mxGetScalar(mx) > 0) ? 1 : 0;
samer@0 711 } else {
samer@0 712 return PL_warning("neither numeric nor logical (captain)");
samer@0 713 }
samer@0 714
samer@0 715 return PL_unify_integer(a,f);
samer@0 716 } catch (PlException &e) {
samer@0 717 return e.plThrow();
samer@0 718 }
samer@0 719 }
samer@0 720
samer@0 721 // Get array information (size and type of elements)
samer@0 722 foreign_t mlMxInfo(term_t mxterm, term_t size, term_t type)
samer@0 723 {
samer@0 724 try {
samer@0 725 mxArray *mx = term_to_mx(mxterm);
samer@0 726 long ndims = mxGetNumberOfDimensions(mx);
samer@0 727 const mwSize *dims = mxGetDimensions(mx);
samer@0 728 const char *cnm = mxGetClassName(mx);
samer@0 729
samer@0 730 if (PL_unify_atom_chars(type, cnm)) {
samer@0 731 if (dims[ndims-1]==1) ndims--; // remove trailing singletons
samer@0 732 return unify_list_sizes(size,dims,ndims);
samer@0 733 }
samer@0 734 PL_fail;
samer@0 735 } catch (PlException &e) {
samer@0 736 return e.plThrow();
samer@0 737 }
samer@0 738 }
samer@0 739
samer@0 740 // Convert multidimensional subscript to linear index
samer@0 741 foreign_t mlMxSub2Ind(term_t mxterm, term_t substerm, term_t indterm)
samer@0 742 {
samer@0 743 try {
samer@0 744 mxArray *mx=term_to_mx(mxterm);
samer@0 745 mwIndex subs[64]; // 64 dimensional should be enough!
samer@0 746 long nsubs;
samer@0 747
samer@0 748 // get substerm as int array
samer@0 749 if (!get_list_integers(substerm,&nsubs,(int *)subs)) // !!
samer@0 750 return PL_warning("Bad subscript list");
samer@0 751
samer@0 752 // switch to zero-based subscripts
samer@0 753 for (int i=0; i<nsubs; i++) subs[i]--;
samer@0 754
samer@0 755 int ind = mxCalcSingleSubscript(mx,nsubs,subs);
samer@0 756 check_array_index(mx,ind);
samer@0 757
samer@0 758 return PL_unify_integer(indterm, ind);
samer@0 759 } catch (PlException &e) {
samer@0 760 return e.plThrow();
samer@0 761 }
samer@0 762 }
samer@0 763
samer@0 764 // Dereference double from mx array
samer@0 765 foreign_t mlMxGetFloat(term_t mxterm, term_t index, term_t value)
samer@0 766 {
samer@0 767 try {
samer@0 768 mxArray *mx = term_to_mx(mxterm);
samer@0 769 long i;
samer@0 770
samer@0 771 check(PL_get_long(index,&i));
samer@0 772 check_array_index(mx,i);
samer@0 773 if (!mxIsDouble(mx)) { return PL_warning("not numeric"); }
samer@0 774
samer@0 775 double *p = (double *)mxGetData(mx);
samer@0 776 return PL_unify_float(value, p[i-1]);
samer@0 777 } catch (PlException &e) {
samer@0 778 return e.plThrow();
samer@0 779 }
samer@0 780 }
samer@0 781
samer@0 782 // Dereference logical from mx array
samer@0 783 foreign_t mlMxGetLogical(term_t mxterm, term_t index, term_t value)
samer@0 784 {
samer@0 785 try {
samer@0 786 mxArray *mx = term_to_mx(mxterm);
samer@0 787 long i;
samer@0 788
samer@0 789 check(PL_get_long(index,&i));
samer@0 790 check_array_index(mx,i);
samer@0 791
samer@0 792 if (mxIsLogical(mx)) {
samer@0 793 mxLogical *p = mxGetLogicals(mx);
samer@0 794 return PL_unify_integer(value,(p[i-1]) ? 1 : 0);
samer@0 795 } else if (mxIsDouble(mx)) {
samer@0 796 double *p = (double *)mxGetData(mx);
samer@0 797 return PL_unify_integer(value, (p[i-1]>0) ? 1 : 0);
samer@0 798 } else {
samer@0 799 return PL_warning("neither logical nor numeric");
samer@0 800 }
samer@0 801
samer@0 802 } catch (PlException &e) {
samer@0 803 return e.plThrow();
samer@0 804 }
samer@0 805 }
samer@0 806
samer@0 807 // Dereference mxArray from cell array
samer@0 808 // Note that we return a non-gargage collected atom, otherwise,
samer@0 809 // the parent cell array would be spoiled when one of its elements
samer@0 810 // is released and destroyed. However, if the parent cell is
samer@0 811 // released and destroyed, any remaining references to elements
samer@0 812 // will be prematurely invalidated.
samer@0 813 // FIXME: This is almost certain to confuse the garbage collector
samer@0 814 foreign_t mlMxGetCell(term_t mxterm, term_t index, term_t value)
samer@0 815 {
samer@0 816 try {
samer@0 817 mxArray *mx = term_to_mx(mxterm);
samer@0 818 long i;
samer@0 819
samer@0 820 check(PL_get_long(index,&i));
samer@0 821 check_array_index(mx,i);
samer@0 822 if (!mxIsCell(mx)) { return PL_warning("not numeric"); }
samer@0 823
samer@0 824 mxArray *p = mxGetCell(mx,i-1);
samer@0 825 return PL_unify_blob(value, (void **)&p, sizeof(p), &mxnogc_blob);
samer@0 826 } catch (PlException &e) {
samer@0 827 return e.plThrow();
samer@0 828 }
samer@0 829 }
samer@0 830
samer@0 831 foreign_t mlMxGetField(term_t mxterm, term_t index, term_t field, term_t value)
samer@0 832 {
samer@0 833 try {
samer@0 834 mxArray *mx = term_to_mx(mxterm);
samer@0 835 long i;
samer@0 836 char *fname;
samer@0 837
samer@0 838 check(PL_get_long(index,&i));
samer@0 839 check(PL_get_atom_chars(field,&fname));
samer@0 840 check_array_index(mx,i);
samer@0 841 if (!mxIsStruct(mx)) { return PL_warning("not a structure"); }
samer@0 842
samer@0 843 mxArray *p = mxGetField(mx,i-1,fname);
samer@0 844 if (!p) return PL_warning("Field not present");
samer@0 845 return PL_unify_blob(value, (void **)&p, sizeof(p), &mxnogc_blob);
samer@0 846 } catch (PlException &e) {
samer@0 847 return e.plThrow();
samer@0 848 }
samer@0 849 }
samer@0 850
samer@0 851 // Create numeric array. Currently only real double arrays created
samer@0 852 foreign_t mlMxCreateNumeric(term_t size, term_t mx) {
samer@0 853 try {
samer@0 854 mwSize dims[64];
samer@0 855 long ndims;
samer@0 856
samer@0 857 // get size as int array
samer@0 858 if (!get_list_integers(size,&ndims,(int *)dims))
samer@0 859 return PL_warning("Bad size list");
samer@0 860
samer@0 861 mxArray *p = mxCreateNumericArray(ndims,dims,mxDOUBLE_CLASS,mxREAL);
samer@0 862 return PL_unify_blob(mx, (void **)&p, sizeof(p), &mxnogc_blob);
samer@0 863 } catch (PlException &e) {
samer@0 864 return e.plThrow();
samer@0 865 }
samer@0 866 }
samer@0 867
samer@0 868 // Create cell array.
samer@0 869 foreign_t mlMxCreateCell(term_t size, term_t mx) {
samer@0 870 try {
samer@0 871 mwSize dims[64];
samer@0 872 long ndims;
samer@0 873
samer@0 874 // get size as int array
samer@0 875 if (!get_list_integers(size,&ndims,(int *)dims))
samer@0 876 return PL_warning("Bad size list");
samer@0 877
samer@0 878 mxArray *p = mxCreateCellArray(ndims,dims);
samer@0 879 return PL_unify_blob(mx, (void **)&p, sizeof(p), &mxnogc_blob);
samer@0 880 } catch (PlException &e) {
samer@0 881 return e.plThrow();
samer@0 882 }
samer@0 883 }
samer@0 884
samer@0 885 // Create numeric array. Currently only real double arrays created
samer@0 886 foreign_t mlMxCreateString(term_t string, term_t mx) {
samer@0 887 try {
samer@0 888 mxArray *p = mxCreateString(PlTerm(string));
samer@0 889 return PL_unify_blob(mx, (void **)&p, sizeof(p), &mxnogc_blob);
samer@0 890 } catch (PlException &e) {
samer@0 891 return e.plThrow();
samer@0 892 }
samer@0 893 }
samer@0 894
samer@0 895
samer@0 896 // Write float into double array
samer@0 897 foreign_t mlMxPutFloat(term_t mxterm, term_t index, term_t value)
samer@0 898 {
samer@0 899 try {
samer@0 900 mxArray *mx = term_to_mx(mxterm);
samer@0 901 long i;
samer@0 902 double val;
samer@0 903
samer@0 904 if (!mxIsDouble(mx)) { return PL_warning("not numeric"); }
samer@0 905 check(PL_get_long(index,&i));
samer@0 906 check(PL_get_float(value,&val));
samer@0 907 check_array_index(mx,i);
samer@0 908 *(mxGetPr(mx)+i-1)=val;
samer@0 909 return true;
samer@0 910 } catch (PlException &e) {
samer@0 911 return e.plThrow();
samer@0 912 }
samer@0 913 }
samer@0 914
samer@0 915 // Write list of floats into double array starting at given index
samer@0 916 foreign_t mlMxPutFloats(term_t mxterm, term_t index, term_t values)
samer@0 917 {
samer@0 918 try {
samer@0 919 mxArray *mx = term_to_mx(mxterm);
samer@0 920 long i, len;
samer@0 921
samer@0 922 if (!mxIsDouble(mx)) { return PL_warning("not numeric"); }
samer@0 923 check(PL_get_long(index,&i));
samer@0 924 check_array_index(mx,i);
samer@0 925 get_list_doubles(values,&len,mxGetPr(mx)+i-1);
samer@0 926 return true;
samer@0 927 } catch (PlException &e) {
samer@0 928 return e.plThrow();
samer@0 929 }
samer@0 930 }
samer@0 931
samer@0 932 // Put an mxArray into a cell array
samer@0 933 // IMPORTANT: the object being put must in a non-memory managed atom
samer@0 934 foreign_t mlMxPutCell(term_t mxterm, term_t index, term_t element)
samer@0 935 {
samer@0 936 try {
samer@0 937 mxArray *mx = term_to_mx(mxterm);
samer@0 938 mxArray *el = term_to_mx(element);
samer@0 939 long i;
samer@0 940
samer@0 941 if (!mxIsCell(mx)) { return PL_warning("not a cell array"); }
samer@0 942 check(PL_get_long(index,&i));
samer@0 943 check_array_index(mx,i);
samer@0 944 mxSetCell(mx,i-1,el);
samer@0 945 return true;
samer@0 946 } catch (PlException &e) {
samer@0 947 return e.plThrow();
samer@0 948 }
samer@0 949 }
samer@0 950
samer@0 951 foreign_t mlMxCopyNoGC(term_t in, term_t out)
samer@0 952 {
samer@0 953 try {
samer@0 954 mxArray *mx = term_to_mx(in);
samer@0 955 mxArray *p = mxDuplicateArray(mx);
samer@0 956 return PL_unify_blob(out, (void **)&p, sizeof(p), &mxnogc_blob);
samer@0 957 } catch (PlException &e) {
samer@0 958 return e.plThrow();
samer@0 959 }
samer@0 960 }
samer@0 961
samer@0 962 foreign_t mlMxNewRefGC(term_t in, term_t out)
samer@0 963 {
samer@0 964 try {
samer@0 965 mxArray *p = term_to_mx(in);
samer@0 966 return PL_unify_blob(out, (void **)&p, sizeof(p), &mx_blob);
samer@0 967 } catch (PlException &e) {
samer@0 968 return e.plThrow();
samer@0 969 }
samer@0 970 }
samer@0 971
samer@0 972
samer@0 973 /*
samer@0 974 * Local Variables:
samer@0 975 * c-basic-offset: 2
samer@0 976 * indent-tabs-mode: nil
samer@0 977 * End:
samer@0 978 */
samer@0 979