Mercurial > hg > plml
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 |