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