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@12
|
82 #include <unistd.h>
|
samer@18
|
83 #include <pthread.h>
|
samer@0
|
84 #include "engine.h"
|
samer@0
|
85
|
samer@15
|
86
|
samer@15
|
87 #define ALT_LASTERR 1
|
samer@15
|
88
|
samer@0
|
89 /* The maximum number of simultaneous connections to Matlab from one
|
samer@0
|
90 Prolog process. */
|
samer@0
|
91 #define MAXENGINES 4
|
samer@0
|
92 #define BUFSIZE 32768 // buffer for matlab output
|
samer@0
|
93 #define MAXCMDLEN 256
|
samer@15
|
94
|
samer@15
|
95 #ifdef ALT_LASTERR
|
samer@15
|
96 # define EVALFMT "lasterr('');disp('#');%s"
|
samer@15
|
97 #else
|
samer@15
|
98 # define EVALFMT "lasterr('');disp('#');%s\nt__ex=lasterr;"
|
samer@15
|
99 #endif
|
samer@0
|
100
|
samer@0
|
101 using namespace std;
|
samer@0
|
102
|
samer@0
|
103 // This is for a SWI Prolog BLOB type to manage mxArray pointers. It
|
samer@0
|
104 // means that the Prolog garbage collector can deal with freeing
|
samer@0
|
105 // unreferenced mxArrays automatically.
|
samer@0
|
106
|
samer@0
|
107
|
samer@0
|
108 #ifdef MX_API_VER
|
samer@0
|
109 #if MX_API_VER >= 0x07030000
|
samer@0
|
110 #else
|
samer@0
|
111 typedef int mwSize;
|
samer@0
|
112 typedef int mwIndex;
|
samer@0
|
113 #endif
|
samer@0
|
114 #else
|
samer@0
|
115 typedef int mwSize;
|
samer@0
|
116 typedef int mwIndex;
|
samer@0
|
117 #endif
|
samer@0
|
118
|
samer@0
|
119 static PL_blob_t mx_blob;
|
samer@0
|
120 static PL_blob_t mxnogc_blob;
|
samer@0
|
121 static functor_t mlerror;
|
samer@0
|
122
|
samer@0
|
123 // Extract an mxArray * from a BLOB atom
|
samer@0
|
124 static mxArray *term_to_mx(term_t t) {
|
samer@0
|
125 PL_blob_t *type;
|
samer@0
|
126 size_t len;
|
samer@0
|
127 void *p;
|
samer@0
|
128
|
samer@0
|
129 PL_get_blob(t, &p, &len, &type);
|
samer@0
|
130 if (type != &mx_blob && type != &mxnogc_blob) {
|
samer@0
|
131 throw PlException("Not an mx variable");
|
samer@0
|
132 }
|
samer@0
|
133 return *(mxArray **) p;
|
samer@0
|
134 }
|
samer@0
|
135
|
samer@0
|
136 static mxArray *ablob_to_mx(atom_t a) {
|
samer@0
|
137 return term_to_mx(PlTerm(PlAtom(a)));
|
samer@0
|
138 }
|
samer@0
|
139
|
samer@0
|
140 // This is for a SWI Prolog BLOB type to manage Matlab workspace
|
samer@0
|
141 // variables. The variable is cleared and the name reclaimed
|
samer@0
|
142 // when the blob is garbage collected. This kind of blob has no data
|
samer@0
|
143 // apart from the atom's name (ie the variable's name)
|
samer@0
|
144
|
samer@0
|
145 static PL_blob_t ws_blob;
|
samer@0
|
146
|
samer@0
|
147 // structure for keeping track of workspace variables
|
samer@0
|
148 struct wsvar {
|
samer@0
|
149 char name[8]; // designed for short machine generated names
|
samer@0
|
150 Engine *engine; // the matlab engine which owns this variable
|
samer@0
|
151 atom_t id; // the id of this engine
|
samer@0
|
152 };
|
samer@0
|
153
|
samer@0
|
154 // extract wsvar from blob term
|
samer@0
|
155 static struct wsvar *term_to_wsvar(term_t t) {
|
samer@0
|
156 PL_blob_t *type;
|
samer@0
|
157 size_t len;
|
samer@0
|
158 void *p;
|
samer@0
|
159
|
samer@0
|
160 PL_get_blob(t, &p, &len, &type);
|
samer@0
|
161 if (type != &ws_blob) {
|
samer@0
|
162 throw PlException("Not a ws variable");
|
samer@0
|
163 }
|
samer@0
|
164 return (struct wsvar *) p;
|
samer@0
|
165 }
|
samer@0
|
166
|
samer@0
|
167 // extract wsvar from atom by converting to term first
|
samer@0
|
168 static struct wsvar *atom_to_wsvar(atom_t a) {
|
samer@0
|
169 return term_to_wsvar(PlTerm(PlAtom(a)));
|
samer@0
|
170 }
|
samer@0
|
171
|
samer@0
|
172
|
samer@0
|
173 /* MATLAB engine wrapper class */
|
samer@0
|
174 class eng {
|
samer@0
|
175 public:
|
samer@18
|
176 const char *magic;
|
samer@0
|
177 Engine *ep; // MATLAB API engine pointer
|
samer@0
|
178 atom_t id; // atom associated with this engine
|
samer@0
|
179 char *outbuf; // buffer for textual output from MATLAB
|
samer@0
|
180
|
samer@18
|
181 eng(): ep(NULL), id(PL_new_atom("")), outbuf(NULL) { magic="mleng"; }
|
samer@0
|
182
|
samer@0
|
183 void open(const char *cmd, atom_t id) {
|
samer@0
|
184 ep=engOpen(cmd);
|
samer@0
|
185
|
samer@0
|
186 if (ep) {
|
samer@0
|
187 this->id=id;
|
samer@0
|
188 outbuf=new char[BUFSIZE];
|
samer@0
|
189 outbuf[BUFSIZE-1]=0;
|
samer@0
|
190 engOutputBuffer(ep,outbuf,BUFSIZE-1);
|
samer@0
|
191 printf("Matlab engine (%s) open.\n",PL_atom_chars(id));
|
samer@0
|
192 } else {
|
samer@0
|
193 throw PlException("open engine failed");
|
samer@0
|
194 }
|
samer@0
|
195 }
|
samer@0
|
196 void close() {
|
samer@0
|
197 engClose(ep);
|
samer@0
|
198 id = PL_new_atom("");
|
samer@0
|
199 delete [] outbuf;
|
samer@0
|
200 ep=0;
|
samer@0
|
201 }
|
samer@0
|
202
|
samer@0
|
203 bool matches(atom_t id) const { return id==this->id; }
|
samer@0
|
204 bool isOpen() const { return ep!=NULL; }
|
samer@0
|
205 };
|
samer@0
|
206
|
samer@0
|
207 // pool of engines, all initially closed
|
samer@0
|
208 static eng engines[MAXENGINES];
|
samer@0
|
209 // functor to be used to wrap array pointers
|
samer@0
|
210
|
samer@18
|
211 static pthread_mutex_t EngMutex;
|
samer@18
|
212
|
samer@18
|
213 class lock {
|
samer@18
|
214 public:
|
samer@18
|
215 lock() { pthread_mutex_lock(&EngMutex); }
|
samer@18
|
216 ~lock() { pthread_mutex_unlock(&EngMutex); }
|
samer@18
|
217 };
|
samer@18
|
218
|
samer@18
|
219
|
samer@0
|
220 extern "C" {
|
samer@0
|
221 // Functions for mx array atom type
|
samer@0
|
222 int mx_release(atom_t a);
|
samer@0
|
223 int mx_compare(atom_t a, atom_t b);
|
samer@0
|
224 // int mx_write(IOSTREAM *s, atom_t a, int flags);
|
samer@0
|
225 int mxnogc_release(atom_t a);
|
samer@0
|
226
|
samer@0
|
227 // Functions for WS variable atom type
|
samer@0
|
228 int ws_release(atom_t a);
|
samer@0
|
229 // int ws_write(IOSTREAM *s, atom_t a, int flags);
|
samer@0
|
230 }
|
samer@0
|
231
|
samer@0
|
232 extern "C" {
|
samer@0
|
233 install_t install();
|
samer@0
|
234 foreign_t mlOpen(term_t servercmd, term_t engine);
|
samer@0
|
235 foreign_t mlClose(term_t engine);
|
samer@0
|
236 foreign_t mlExec(term_t engine, term_t cmd);
|
samer@0
|
237 foreign_t mlWSGet(term_t var, term_t val);
|
samer@0
|
238 foreign_t mlWSPut(term_t var, term_t val);
|
samer@0
|
239 foreign_t mlWSName(term_t engine, term_t var, term_t id);
|
samer@0
|
240 foreign_t mlWSAlloc(term_t engine, term_t var);
|
samer@0
|
241 foreign_t mlMx2Atom(term_t mx, term_t atom);
|
samer@0
|
242 foreign_t mlMx2Float(term_t mx, term_t num);
|
samer@0
|
243 foreign_t mlMx2Logical(term_t mx, term_t num);
|
samer@0
|
244 foreign_t mlMx2String(term_t mx, term_t num);
|
samer@0
|
245 foreign_t mlMxInfo(term_t mx, term_t size, term_t type);
|
samer@0
|
246 foreign_t mlMxSub2Ind(term_t mx, term_t subs, term_t ind);
|
samer@0
|
247 foreign_t mlMxGetFloat(term_t mx, term_t index, term_t value);
|
samer@0
|
248 foreign_t mlMxGetLogical(term_t mx, term_t index, term_t value);
|
samer@0
|
249 foreign_t mlMxGetCell(term_t mx, term_t index, term_t value);
|
samer@0
|
250 foreign_t mlMxGetField(term_t mx, term_t index, term_t field, term_t value);
|
samer@0
|
251 foreign_t mlMxGetReals(term_t mx, term_t values);
|
samer@0
|
252 foreign_t mlMxCreateNumeric(term_t size, term_t mx);
|
samer@0
|
253 foreign_t mlMxCreateCell(term_t size, term_t mx);
|
samer@0
|
254 foreign_t mlMxCreateString(term_t string, term_t mx);
|
samer@0
|
255 foreign_t mlMxPutFloat(term_t mx, term_t index, term_t value);
|
samer@0
|
256 foreign_t mlMxPutFloats(term_t mx, term_t index, term_t values);
|
samer@0
|
257 foreign_t mlMxPutCell(term_t mx, term_t index, term_t value);
|
samer@0
|
258 foreign_t mlMxCopyNoGC(term_t src, term_t dst);
|
samer@0
|
259 foreign_t mlMxNewRefGC(term_t src, term_t dst);
|
samer@0
|
260 }
|
samer@0
|
261
|
samer@0
|
262 install_t install() {
|
samer@37
|
263 PL_register_foreign("mlOPEN", 2, (void (*)())mlOpen, 0);
|
samer@37
|
264 PL_register_foreign("mlCLOSE", 1, (void (*)())mlClose, 0);
|
samer@37
|
265 PL_register_foreign("mlEXEC", 2, (void (*)())mlExec, 0);
|
samer@37
|
266 PL_register_foreign("mlWSNAME", 3, (void (*)())mlWSName, 0);
|
samer@37
|
267 PL_register_foreign("mlWSALLOC", 2, (void (*)())mlWSAlloc, 0);
|
samer@37
|
268 PL_register_foreign("mlWSGET", 2, (void (*)())mlWSGet,0);
|
samer@37
|
269 PL_register_foreign("mlWSPUT", 2, (void (*)())mlWSPut, 0);
|
samer@37
|
270 PL_register_foreign("mlMX2ATOM", 2, (void (*)())mlMx2Atom, 0);
|
samer@37
|
271 PL_register_foreign("mlMX2FLOAT", 2, (void (*)())mlMx2Float, 0);
|
samer@37
|
272 PL_register_foreign("mlMX2LOGICAL", 2, (void (*)())mlMx2Logical, 0);
|
samer@37
|
273 PL_register_foreign("mlMX2STRING", 2, (void (*)())mlMx2String, 0);
|
samer@37
|
274 PL_register_foreign("mlMXINFO", 3, (void (*)())mlMxInfo, 0);
|
samer@37
|
275 PL_register_foreign("mlSUB2IND", 3, (void (*)())mlMxSub2Ind, 0);
|
samer@37
|
276 PL_register_foreign("mlGETFLOAT", 3, (void (*)())mlMxGetFloat, 0);
|
samer@37
|
277 PL_register_foreign("mlGETLOGICAL", 3, (void (*)())mlMxGetLogical, 0);
|
samer@37
|
278 PL_register_foreign("mlGETCELL", 3, (void (*)())mlMxGetCell, 0);
|
samer@37
|
279 PL_register_foreign("mlGETFIELD", 4, (void (*)())mlMxGetField, 0);
|
samer@37
|
280 PL_register_foreign("mlGETREALS", 2, (void (*)())mlMxGetReals, 0);
|
samer@37
|
281 PL_register_foreign("mlCREATENUMERIC", 2, (void (*)())mlMxCreateNumeric, 0);
|
samer@37
|
282 PL_register_foreign("mlCREATECELL", 2, (void (*)())mlMxCreateCell, 0);
|
samer@37
|
283 PL_register_foreign("mlCREATESTRING", 2, (void (*)())mlMxCreateString, 0);
|
samer@37
|
284 PL_register_foreign("mlPUTFLOAT", 3, (void (*)())mlMxPutFloat, 0);
|
samer@37
|
285 PL_register_foreign("mlPUTFLOATS", 3, (void (*)())mlMxPutFloats, 0);
|
samer@37
|
286 PL_register_foreign("mlPUTCELL", 3, (void (*)())mlMxPutCell, 0);
|
samer@37
|
287 PL_register_foreign("mlCOPYNOGC", 2, (void (*)())mlMxCopyNoGC, 0);
|
samer@37
|
288 PL_register_foreign("mlNEWREFGC", 2, (void (*)())mlMxNewRefGC, 0);
|
samer@0
|
289
|
samer@0
|
290 mx_blob.magic = PL_BLOB_MAGIC;
|
samer@0
|
291 mx_blob.flags = PL_BLOB_UNIQUE;
|
samer@0
|
292 mx_blob.name = (char *)"mx";
|
samer@0
|
293 mx_blob.acquire = 0;
|
samer@0
|
294 mx_blob.release = mx_release;
|
samer@0
|
295 mx_blob.compare = mx_compare;
|
samer@0
|
296 mx_blob.write = 0; // mx_write;
|
samer@0
|
297
|
samer@0
|
298 mxnogc_blob.magic = PL_BLOB_MAGIC;
|
samer@0
|
299 mxnogc_blob.flags = PL_BLOB_UNIQUE;
|
samer@0
|
300 mxnogc_blob.name = (char *)"mxnogc";
|
samer@0
|
301 mxnogc_blob.acquire = 0;
|
samer@0
|
302 mxnogc_blob.release = mxnogc_release;
|
samer@0
|
303 mxnogc_blob.compare = mx_compare;
|
samer@0
|
304 mxnogc_blob.write = 0; // mx_write;
|
samer@0
|
305
|
samer@0
|
306 ws_blob.magic = PL_BLOB_MAGIC;
|
samer@0
|
307 ws_blob.flags = PL_BLOB_UNIQUE;
|
samer@0
|
308 ws_blob.name = (char *)"ws";
|
samer@0
|
309 ws_blob.acquire = 0;
|
samer@0
|
310 ws_blob.release = ws_release;
|
samer@0
|
311 ws_blob.compare = 0;
|
samer@0
|
312 ws_blob.write = 0;
|
samer@0
|
313
|
samer@0
|
314 mlerror=PL_new_functor(PL_new_atom("mlerror"),3);
|
samer@18
|
315 pthread_mutex_init(&EngMutex,NULL);
|
samer@0
|
316 }
|
samer@0
|
317
|
samer@0
|
318 void check(int rc) { if (!rc) printf("*** plml: Something failed.\n");}
|
samer@0
|
319
|
samer@0
|
320 void check_array_index(mxArray *mx, long i)
|
samer@0
|
321 {
|
samer@0
|
322 long n = mxGetNumberOfElements(mx);
|
samer@0
|
323 if (i<=0 || i>n) throw PlException("Index out of bounds");
|
samer@0
|
324 }
|
samer@0
|
325
|
samer@0
|
326 int unify_list_sizes(term_t list, const mwSize *ints, int num)
|
samer@0
|
327 {
|
samer@0
|
328 list=PL_copy_term_ref(list);
|
samer@0
|
329
|
samer@0
|
330 for (int i=0; i<num; i++) {
|
samer@0
|
331 term_t head=PL_new_term_ref();
|
samer@0
|
332 term_t tail=PL_new_term_ref();
|
samer@0
|
333 if (!PL_unify_list(list,head,tail)) PL_fail;
|
samer@0
|
334 if (!PL_unify_integer(head,ints[i])) PL_fail;
|
samer@0
|
335 list=tail;
|
samer@0
|
336 }
|
samer@0
|
337 return PL_unify_nil(list);
|
samer@0
|
338 }
|
samer@0
|
339
|
samer@0
|
340 int unify_list_doubles(term_t list, double *x, int n)
|
samer@0
|
341 {
|
samer@0
|
342 list=PL_copy_term_ref(list);
|
samer@0
|
343
|
samer@0
|
344 for (int i=0; i<n; i++) {
|
samer@0
|
345 term_t head=PL_new_term_ref();
|
samer@0
|
346 term_t tail=PL_new_term_ref();
|
samer@0
|
347 if (!PL_unify_list(list,head,tail)) PL_fail;
|
samer@0
|
348 if (!PL_unify_float(head,x[i])) PL_fail;
|
samer@0
|
349 list=tail;
|
samer@0
|
350 }
|
samer@0
|
351 return PL_unify_nil(list);
|
samer@0
|
352 }
|
samer@0
|
353
|
samer@0
|
354 // read list of integers from term and write to int array
|
samer@0
|
355 int get_list_integers(term_t list, long *len, int *vals)
|
samer@0
|
356 {
|
samer@0
|
357 term_t head=PL_new_term_ref();
|
samer@0
|
358 long n;
|
samer@0
|
359
|
samer@0
|
360 // copy term ref so as not to modify original
|
samer@0
|
361 list=PL_copy_term_ref(list);
|
samer@0
|
362 for (n=0;PL_get_list(list,head,list);n++) {
|
samer@0
|
363 if (!PL_get_integer(head,&vals[n])) return false;
|
samer@0
|
364 }
|
samer@0
|
365 if (!PL_get_nil(list)) return false;
|
samer@0
|
366 *len=n;
|
samer@0
|
367 return true;
|
samer@0
|
368 }
|
samer@0
|
369
|
samer@0
|
370 // read list of floats from term and write to double array
|
samer@0
|
371 int get_list_doubles(term_t list, long *len, double *vals)
|
samer@0
|
372 {
|
samer@0
|
373 term_t head=PL_new_term_ref();
|
samer@0
|
374 long n;
|
samer@0
|
375
|
samer@0
|
376 // copy term ref so as not to modify original
|
samer@0
|
377 list=PL_copy_term_ref(list);
|
samer@0
|
378 for (n=0;PL_get_list(list,head,list);n++) {
|
samer@0
|
379 if (!PL_get_float(head,&vals[n])) return false;
|
samer@0
|
380 }
|
samer@0
|
381 if (!PL_get_nil(list)) return false;
|
samer@0
|
382 *len=n;
|
samer@0
|
383 return true;
|
samer@0
|
384 }
|
samer@0
|
385
|
samer@0
|
386
|
samer@0
|
387
|
samer@0
|
388 /*
|
samer@0
|
389 * Member functions for SWIs blob atoms, which allow SWI to manage
|
samer@0
|
390 * garbage collection for user-defined data types.
|
samer@0
|
391 */
|
samer@0
|
392 int mx_release(atom_t a) {
|
samer@0
|
393 mxArray *p=ablob_to_mx(a);
|
samer@0
|
394 mxDestroyArray(p);
|
samer@0
|
395 return TRUE;
|
samer@0
|
396 }
|
samer@0
|
397
|
samer@0
|
398 int mx_compare(atom_t a, atom_t b) {
|
samer@0
|
399 mxArray *pa=ablob_to_mx(a);
|
samer@0
|
400 mxArray *pb=ablob_to_mx(b);
|
samer@0
|
401 if (pa<pb) return -1;
|
samer@0
|
402 else if (pa>pb) return 1;
|
samer@0
|
403 else return 0;
|
samer@0
|
404 }
|
samer@0
|
405
|
samer@0
|
406 int mxnogc_release(atom_t a) { return TRUE; }
|
samer@0
|
407
|
samer@0
|
408 /*
|
samer@0
|
409 // this requires some jiggery pokery to handle IOSTREAMS.
|
samer@0
|
410 int mx_write(IOSTREAM *s, atom_t a, int flags) {
|
samer@0
|
411 mxArray *p=ablob_to_mx(a);
|
samer@0
|
412 fprintf(s,"<mx:%p>",p);
|
samer@0
|
413 }
|
samer@0
|
414 */
|
samer@0
|
415
|
samer@0
|
416
|
samer@0
|
417 int ws_release(atom_t a) {
|
samer@0
|
418 struct wsvar *x=atom_to_wsvar(a);
|
samer@18
|
419 int rc;
|
samer@0
|
420 // printf("."); fflush(stdout); // sweet brevity
|
samer@0
|
421
|
samer@0
|
422 char buf[16];
|
samer@0
|
423 sprintf(buf,"clear %s",x->name);
|
samer@18
|
424 if (pthread_mutex_trylock(&EngMutex)==0) {
|
samer@18
|
425 rc=engEvalString(x->engine,buf) ? FALSE : TRUE;
|
samer@18
|
426 pthread_mutex_unlock(&EngMutex);
|
samer@18
|
427 } else {
|
samer@18
|
428 // printf("\n *** cannot release %s while engine locked ***\n",x->name);
|
samer@18
|
429 rc=FALSE;
|
samer@18
|
430 }
|
samer@18
|
431
|
samer@18
|
432 if (rc) {
|
samer@18
|
433 x->name[0]=0;
|
samer@18
|
434 x->engine=0;
|
samer@18
|
435 }
|
samer@0
|
436
|
samer@18
|
437 return rc;
|
samer@0
|
438 }
|
samer@0
|
439
|
samer@0
|
440 /* see mx_write */
|
samer@0
|
441 //int ws_write(IOSTREAM *s, atom_t a, int flags) {
|
samer@0
|
442 // struct wsvar *p=atom_to_wsvar(a);
|
samer@0
|
443 // mxArray *p=ablob_to_mx(a);
|
samer@0
|
444 // fprintf(s,"%s",p->name);
|
samer@0
|
445 //}
|
samer@0
|
446
|
samer@0
|
447
|
samer@0
|
448 /* Finds the engine associated with the given term
|
samer@0
|
449 * (which should just be an atom). Throws an exception
|
samer@0
|
450 * if the engine is not found.
|
samer@0
|
451 */
|
samer@0
|
452 static eng *findEngine(term_t id_term)
|
samer@0
|
453 {
|
samer@0
|
454 atom_t id;
|
samer@0
|
455 if(!PL_get_atom(id_term, &id)) {
|
samer@0
|
456 throw PlException("id is not an atom");
|
samer@0
|
457 }
|
samer@0
|
458 for (int i=0; i<MAXENGINES; i++) {
|
samer@0
|
459 if (engines[i].matches(id)) return &engines[i];
|
samer@0
|
460 }
|
samer@0
|
461 throw PlException("engine not found");
|
samer@0
|
462 }
|
samer@0
|
463
|
samer@0
|
464
|
samer@26
|
465 static void displayOutput(const char *prefix,const char *p)
|
samer@24
|
466 {
|
samer@24
|
467 while (*p) {
|
samer@24
|
468 fputs(prefix,stdout);
|
samer@24
|
469 while (*p && *p!='\n') putchar(*p++);
|
samer@24
|
470 putchar('\n'); if (*p) p++;
|
samer@24
|
471 }
|
samer@24
|
472 }
|
samer@24
|
473
|
samer@0
|
474 /*
|
samer@0
|
475 * Open a matlab engine using the given command and associate
|
samer@0
|
476 * it with the second term, which should be an atom.
|
samer@0
|
477 */
|
samer@0
|
478
|
samer@0
|
479 foreign_t mlOpen(term_t servercmd, term_t id_term)
|
samer@0
|
480 {
|
samer@0
|
481 try {
|
samer@0
|
482 findEngine(id_term);
|
samer@0
|
483 printf("mlOPEN/2: Engine %s already open\n",(const char *)PlTerm(id_term));
|
samer@0
|
484 PL_succeed;
|
samer@0
|
485 } catch (...) {}
|
samer@0
|
486
|
samer@0
|
487 try {
|
samer@0
|
488 // look for an unused engine structure
|
samer@0
|
489 for (int i=0; i<MAXENGINES; i++) {
|
samer@0
|
490 if (!engines[i].isOpen()) {
|
samer@0
|
491 atom_t id;
|
samer@0
|
492 check(PL_get_atom(id_term,&id));
|
samer@0
|
493 engines[i].open(PlTerm(servercmd), id);
|
samer@24
|
494 displayOutput("| ",engines[i].outbuf);
|
samer@0
|
495 PL_succeed;
|
samer@0
|
496 }
|
samer@0
|
497 }
|
samer@0
|
498 return PL_warning("mlOPEN/2: no more engines available.");
|
samer@0
|
499 } catch (PlException &e) {
|
samer@0
|
500 return e.plThrow();
|
samer@0
|
501 }
|
samer@0
|
502 }
|
samer@0
|
503
|
samer@0
|
504 // Close a previously opened Matlab engine
|
samer@0
|
505 foreign_t mlClose(term_t engine) {
|
samer@0
|
506 try {
|
samer@0
|
507 findEngine(engine)->close();
|
samer@0
|
508 PL_succeed;
|
samer@0
|
509 } catch (PlException &e) {
|
samer@0
|
510 return e.plThrow();
|
samer@0
|
511 }
|
samer@0
|
512 }
|
samer@0
|
513
|
samer@13
|
514
|
samer@18
|
515 static int raise_exception(const char *msg, const char *loc, const char *info) {
|
samer@14
|
516 // printf("\n!! raising exception: %s\n",msg);
|
samer@13
|
517 // return FALSE;
|
samer@13
|
518
|
samer@13
|
519 term_t ex = PL_new_term_ref();
|
samer@18
|
520 return PL_unify_term(ex, PL_FUNCTOR_CHARS, "error", 2,
|
samer@18
|
521 PL_FUNCTOR_CHARS, "plml_error", 3, PL_CHARS, msg, PL_CHARS, loc, PL_CHARS, info,
|
samer@18
|
522 PL_VARIABLE)
|
samer@13
|
523
|
samer@18
|
524 && PL_raise_exception(ex);
|
samer@13
|
525 }
|
samer@13
|
526
|
samer@0
|
527 /*
|
samer@0
|
528 * Workspace variable handling
|
samer@0
|
529 */
|
samer@0
|
530
|
samer@0
|
531 // This will create a new workspace variable with an unused name,
|
samer@0
|
532 // initialise it to an empty array (to reserve the name) and unify
|
samer@0
|
533 // the term (which must be a prolog variable) with a blob representing
|
samer@0
|
534 // the variable. This in turn points back to this engine so that
|
samer@0
|
535 // if garbage collected, the workspace variable is cleared.
|
samer@0
|
536 foreign_t mlWSAlloc(term_t eng, term_t blob) {
|
samer@0
|
537 // if varname is already bound, we should check
|
samer@0
|
538 // that the name has not been used in the workspace
|
samer@13
|
539 class eng *engine;
|
samer@13
|
540 try { engine=findEngine(eng); }
|
samer@18
|
541 catch (PlException &ex) { return ex.plThrow(); }
|
samer@0
|
542
|
samer@13
|
543 //printf("-- Entering mlWSALLOC \r"); fflush(stdout);
|
samer@13
|
544 struct wsvar x;
|
samer@0
|
545
|
samer@13
|
546 x.engine = engine->ep;
|
samer@13
|
547 x.id = engine->id;
|
samer@0
|
548
|
samer@15
|
549 // printf("-- mlWSAlloc: Calling uniquevar... \r"); fflush(stdout);
|
samer@18
|
550 { lock l;
|
samer@18
|
551 if (engEvalString(x.engine, "uniquevar([])"))
|
samer@18
|
552 return raise_exception("eval_failed","uniquevar","none");
|
samer@15
|
553 }
|
samer@15
|
554
|
samer@15
|
555 if (strncmp(engine->outbuf,">> \nans =\n\nt_",13)!=0) {
|
samer@15
|
556 //printf("\n** mlWSAlloc: output buffer looks bad: '%s'\n",engine->outbuf);
|
samer@18
|
557 return raise_exception("bad_output_buffer","uniquevar",engine->outbuf);
|
samer@15
|
558 }
|
samer@15
|
559
|
samer@16
|
560 unsigned int len=strlen(engine->outbuf+11)-2;
|
samer@15
|
561 if (len+1>sizeof(x.name)) {
|
samer@18
|
562 return raise_exception("name_too_long","uniquevar",engine->outbuf);
|
samer@15
|
563 }
|
samer@15
|
564 memcpy(x.name,engine->outbuf+11,len);
|
samer@15
|
565 x.name[len]=0;
|
samer@13
|
566
|
samer@13
|
567 return PL_unify_blob(blob,&x,sizeof(x),&ws_blob);
|
samer@0
|
568 }
|
samer@0
|
569
|
samer@0
|
570 foreign_t mlWSName(term_t blob, term_t name, term_t engine) {
|
samer@0
|
571 // if varname is already bound, we should check
|
samer@0
|
572 // that the name has not been used in the workspace
|
samer@0
|
573 try {
|
samer@0
|
574 struct wsvar *x = term_to_wsvar(blob);
|
samer@0
|
575 return ( PL_unify_atom_chars(name, x->name)
|
samer@0
|
576 && PL_unify_atom(engine, x->id));
|
samer@0
|
577 } catch (PlException &e) {
|
samer@0
|
578 PL_fail; // return e.plThrow();
|
samer@0
|
579 }
|
samer@0
|
580 }
|
samer@0
|
581
|
samer@0
|
582 // Get a named variable from the MATLAB workspace and return a term
|
samer@0
|
583 // containing a pointer to an mxArray (in Prolog's memory space).
|
samer@0
|
584 foreign_t mlWSGet(term_t var, term_t val) {
|
samer@0
|
585 try {
|
samer@0
|
586 struct wsvar *x = term_to_wsvar(var);
|
samer@18
|
587 lock l;
|
samer@18
|
588 // class eng *engine=findEngine(PlTerm(PlAtom(x->id)));
|
samer@18
|
589 // char *before=strdup(engine->outbuf);
|
samer@18
|
590 //printf("-- mlWSGET: calling get variable...\n");
|
samer@0
|
591 mxArray *p = engGetVariable(x->engine, x->name);
|
samer@18
|
592 //printf("-- mlWSGET: returned from get variable.\n");
|
samer@17
|
593 if (p) return PL_unify_blob(val, (void **)&p, sizeof(p), &mx_blob);
|
samer@17
|
594 else {
|
samer@18
|
595 //printf("\n!! mlWSGet: failed to get %s.\n",x->name);
|
samer@18
|
596 //printf("\n!! mlWSGet: before buffer: %s.\n",before);
|
samer@18
|
597 //printf("\n!! mlWSGet: before after: %s.\n",engine->outbuf);
|
samer@18
|
598 //return raise_exception("get_variable_failed",before,engine->outbuf);
|
samer@18
|
599 return raise_exception("get_variable_failed","mlWSGET",x->name);
|
samer@17
|
600 }
|
samer@0
|
601 } catch (PlException &e) {
|
samer@0
|
602 return e.plThrow();
|
samer@0
|
603 }
|
samer@0
|
604 }
|
samer@0
|
605
|
samer@18
|
606
|
samer@0
|
607 // Put an array back in Matlab workspace under given variable name
|
samer@0
|
608 foreign_t mlWSPut(term_t var, term_t val) {
|
samer@0
|
609 try {
|
samer@0
|
610 struct wsvar *x=term_to_wsvar(var);
|
samer@18
|
611 lock l;
|
samer@18
|
612 return engPutVariable(x->engine, x->name, term_to_mx(val)) ? FALSE : TRUE;
|
samer@0
|
613 } catch (PlException &e) {
|
samer@0
|
614 return e.plThrow();
|
samer@0
|
615 }
|
samer@0
|
616 }
|
samer@0
|
617
|
samer@0
|
618 /*
|
samer@0
|
619 * Executing MATLAB code
|
samer@0
|
620 */
|
samer@0
|
621
|
samer@0
|
622 // Call a Matlab engine to execute the given command
|
samer@0
|
623 foreign_t mlExec(term_t engine, term_t cmd)
|
samer@0
|
624 {
|
samer@13
|
625 // printf(" - mlExec: Entering \r"); fflush(stdout);
|
samer@0
|
626 try {
|
samer@0
|
627 eng *eng=findEngine(engine);
|
samer@0
|
628 const char *cmdstr=PlTerm(cmd);
|
samer@0
|
629 int cmdlen=strlen(cmdstr);
|
samer@0
|
630 int rc;
|
samer@18
|
631 lock l;
|
samer@0
|
632
|
samer@0
|
633 // if string is very long, send it via local mxArray
|
samer@0
|
634 if (cmdlen>MAXCMDLEN) {
|
samer@0
|
635 mxArray *mxcmd=mxCreateString(cmdstr);
|
samer@0
|
636 engPutVariable(eng->ep,"t__cmd",mxcmd);
|
samer@0
|
637 mxDestroyArray(mxcmd);
|
samer@0
|
638 cmdstr="eval(t__cmd)";
|
samer@0
|
639 cmdlen=strlen(cmdstr);
|
samer@0
|
640 }
|
samer@0
|
641
|
samer@15
|
642 { // scope for eval_cmd
|
samer@15
|
643 char *eval_cmd = new char[cmdlen+strlen(EVALFMT)-1];
|
samer@15
|
644 if (eval_cmd==NULL) throw PlException("Failed to allocate memory for command");
|
samer@15
|
645 sprintf(eval_cmd, EVALFMT, cmdstr);
|
samer@15
|
646 //printf("-- Calling Matlab engine... \r"); fflush(stdout);
|
samer@18
|
647 rc=engEvalString(eng->ep,eval_cmd);
|
samer@15
|
648 //printf("-- Returned from Matlab engine... \r"); fflush(stdout);
|
samer@15
|
649 delete [] eval_cmd;
|
samer@15
|
650 }
|
samer@15
|
651
|
samer@15
|
652 if (rc) { throw PlException("mlExec: engEvalString failed."); }
|
samer@0
|
653
|
samer@14
|
654 // EVALFMT starts with disp('#'). This means that the output buffer should
|
samer@15
|
655 // contain at least the 5 characters: ">> #\n". If they are not there,
|
samer@13
|
656 // something is terribly wrong and we must throw an exeption to avoid
|
samer@15
|
657 // locking up in triserver.
|
samer@14
|
658 if (strncmp(eng->outbuf,">> #\n",5)!=0) {
|
samer@18
|
659 throw PlException(PlCompound("bad_output_buffer",PlTermv("exec",eng->outbuf)));
|
samer@13
|
660 }
|
samer@23
|
661
|
samer@15
|
662 // write whatever is in the output buffer now, starting after the "#\n"
|
samer@24
|
663 displayOutput("| ", eng->outbuf+5);
|
samer@12
|
664
|
samer@15
|
665 #ifdef ALT_LASTERR
|
samer@15
|
666
|
samer@15
|
667 // --------------- ALTERNATIVE LASTERR SCHEME ------------------
|
samer@23
|
668 // call engine to eval lasterr, then scrape from output buffer: it's faster and easier.
|
samer@0
|
669
|
samer@14
|
670 rc=engEvalString(eng->ep,"lasterr");
|
samer@15
|
671 if (rc) { throw PlException("mlExec: unable to execute lasterr"); }
|
samer@14
|
672 if (strncmp(eng->outbuf,">> \nans =",9)!=0) {
|
samer@18
|
673 throw PlException(PlCompound("bad_output_buffer",PlTermv("lasterr",eng->outbuf)));
|
samer@14
|
674 }
|
samer@14
|
675
|
samer@14
|
676 if (strncmp(eng->outbuf+11," ''",7)!=0) {
|
samer@14
|
677 int len=strlen(eng->outbuf+11)-2;
|
samer@15
|
678 char *lasterr= new char[len+1];
|
samer@14
|
679 term_t desc=PL_new_term_ref();
|
samer@14
|
680 term_t cmd=PL_new_term_ref();
|
samer@14
|
681 term_t ex=PL_new_term_ref();
|
samer@15
|
682
|
samer@14
|
683 memcpy(lasterr,eng->outbuf+11,len);
|
samer@14
|
684 lasterr[len]=0;
|
samer@14
|
685
|
samer@14
|
686 PL_put_atom_chars(desc,lasterr);
|
samer@14
|
687 PL_put_atom_chars(cmd,cmdstr);
|
samer@15
|
688 delete [] lasterr;
|
samer@15
|
689
|
samer@14
|
690 check(PL_cons_functor(ex,mlerror,engine,desc,cmd));
|
samer@14
|
691 throw PlException(ex);
|
samer@14
|
692 }
|
samer@14
|
693
|
samer@15
|
694 #else
|
samer@14
|
695
|
samer@15
|
696 // --------------- ORIGINAL LASTERR SCHEME ------------------
|
samer@15
|
697 // Execution puts lasterr into t__ex, then we use engGetVariable to
|
samer@15
|
698 // retrieve it.
|
samer@15
|
699
|
samer@14
|
700 //printf(" - mlExec: output buffer: '%s'\n",eng->outbuf);
|
samer@0
|
701 mxArray *lasterr = engGetVariable(eng->ep, "t__ex");
|
samer@14
|
702 //printf(" - mlExec: output buffer after: ++%s++\n",eng->outbuf);
|
samer@13
|
703 //printf(" - mlExec: Got last error (%p) \r",lasterr); fflush(stdout);
|
samer@12
|
704
|
samer@12
|
705 if (!lasterr) {
|
samer@13
|
706 printf("\n** mlExec: unable to get lasterr.\n");
|
samer@14
|
707 printf("** mlExec: Output buffer contains: '%s'.\n",eng->outbuf);
|
samer@13
|
708 throw PlException("mlExec: unable to get lasterr");
|
samer@14
|
709 }
|
samer@0
|
710
|
samer@15
|
711 if (mxGetNumberOfElements(lasterr)==0) mxDestroyArray(lasterr);
|
samer@15
|
712 else {
|
samer@0
|
713 char *string=mxArrayToString(lasterr);
|
samer@0
|
714 mxDestroyArray(lasterr);
|
samer@0
|
715
|
samer@0
|
716 term_t desc=PL_new_term_ref();
|
samer@0
|
717 term_t cmd=PL_new_term_ref();
|
samer@0
|
718 term_t ex=PL_new_term_ref();
|
samer@0
|
719
|
samer@0
|
720 PL_put_atom_chars(desc,string);
|
samer@0
|
721 PL_put_atom_chars(cmd,cmdstr);
|
samer@0
|
722 mxFree(string);
|
samer@0
|
723 check(PL_cons_functor(ex,mlerror,engine,desc,cmd));
|
samer@0
|
724 throw PlException(ex);
|
samer@0
|
725
|
samer@15
|
726 }
|
samer@15
|
727 #endif
|
samer@23
|
728
|
samer@12
|
729 return TRUE;
|
samer@0
|
730 } catch (PlException &e) {
|
samer@0
|
731 return e.plThrow();
|
samer@0
|
732 }
|
samer@0
|
733 }
|
samer@0
|
734
|
samer@0
|
735 // Get a Prolog string out of a matlab char array
|
samer@0
|
736 foreign_t mlMx2String(term_t mx, term_t a)
|
samer@0
|
737 {
|
samer@0
|
738 try {
|
samer@0
|
739 char *str = mxArrayToString(term_to_mx(mx));
|
samer@0
|
740 if (!str) {
|
samer@0
|
741 return PL_warning("array is not a character array");
|
samer@0
|
742 }
|
samer@0
|
743 int rc = PL_unify_string_chars(a, str);
|
samer@0
|
744 mxFree(str);
|
samer@0
|
745 return rc;
|
samer@0
|
746 } catch (PlException &e) {
|
samer@0
|
747 return e.plThrow();
|
samer@0
|
748 }
|
samer@0
|
749 }
|
samer@0
|
750
|
samer@0
|
751 // Convert Matlab char array to a Prolog atom
|
samer@0
|
752 foreign_t mlMx2Atom(term_t mx, term_t a)
|
samer@0
|
753 {
|
samer@0
|
754 try {
|
samer@0
|
755 char *str = mxArrayToString(term_to_mx(mx));
|
samer@0
|
756 if (!str) {
|
samer@0
|
757 return PL_warning("array is not a character array");
|
samer@0
|
758 }
|
samer@0
|
759 int rc = PL_unify_atom_chars(a, str);
|
samer@0
|
760 mxFree(str);
|
samer@0
|
761 return rc;
|
samer@0
|
762 } catch (PlException &e) {
|
samer@0
|
763 return e.plThrow();
|
samer@0
|
764 }
|
samer@0
|
765 }
|
samer@0
|
766
|
samer@0
|
767 // Convert Matlab numerical array with one element to Prolog float
|
samer@0
|
768 foreign_t mlMx2Float(term_t mxterm, term_t a)
|
samer@0
|
769 {
|
samer@0
|
770 try {
|
samer@0
|
771 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
772 if (!mxIsDouble(mx)) {
|
samer@0
|
773 return PL_warning("not numeric");
|
samer@0
|
774 }
|
samer@0
|
775 if (mxGetNumberOfElements(mx)!=1) {
|
samer@0
|
776 return PL_warning("Not a scalar");
|
samer@0
|
777 }
|
samer@0
|
778 double x = mxGetScalar(mx);
|
samer@0
|
779
|
samer@0
|
780 return PL_unify_float(a, x);
|
samer@0
|
781 } catch (PlException &e) {
|
samer@0
|
782 return e.plThrow();
|
samer@0
|
783 }
|
samer@0
|
784 }
|
samer@0
|
785
|
samer@0
|
786 // Convert Matlab numerical (REAL) array to list
|
samer@0
|
787 foreign_t mlMxGetReals(term_t mxterm, term_t a)
|
samer@0
|
788 {
|
samer@0
|
789 try {
|
samer@0
|
790 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
791 int n = mxGetNumberOfElements(mx);
|
samer@0
|
792
|
samer@0
|
793 if (!mxIsDouble(mx)) return PL_warning("not numeric");
|
samer@0
|
794 return unify_list_doubles(a,mxGetPr(mx),n);
|
samer@0
|
795 } catch (PlException &e) {
|
samer@0
|
796 return e.plThrow();
|
samer@0
|
797 }
|
samer@0
|
798 }
|
samer@0
|
799
|
samer@0
|
800 // Convert Matlab logical or numeric array with one element to
|
samer@0
|
801 // Prolog integer 0 or 1 (does not fail or succeed depending on
|
samer@0
|
802 // logical value - this is can be done by prolog code).
|
samer@0
|
803 foreign_t mlMx2Logical(term_t mxterm, term_t a)
|
samer@0
|
804 {
|
samer@0
|
805 try {
|
samer@0
|
806 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
807 if (mxGetNumberOfElements(mx) != 1) return PL_warning("Not a scalar");
|
samer@0
|
808
|
samer@0
|
809 int f;
|
samer@0
|
810 if (mxIsLogical(mx)) {
|
samer@0
|
811 f = mxIsLogicalScalarTrue(mx) ? 1 : 0;
|
samer@0
|
812 } else if (mxIsDouble(mx)) {
|
samer@0
|
813 f = (mxGetScalar(mx) > 0) ? 1 : 0;
|
samer@0
|
814 } else {
|
samer@0
|
815 return PL_warning("neither numeric nor logical (captain)");
|
samer@0
|
816 }
|
samer@0
|
817
|
samer@0
|
818 return PL_unify_integer(a,f);
|
samer@0
|
819 } catch (PlException &e) {
|
samer@0
|
820 return e.plThrow();
|
samer@0
|
821 }
|
samer@0
|
822 }
|
samer@0
|
823
|
samer@0
|
824 // Get array information (size and type of elements)
|
samer@0
|
825 foreign_t mlMxInfo(term_t mxterm, term_t size, term_t type)
|
samer@0
|
826 {
|
samer@0
|
827 try {
|
samer@0
|
828 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
829 long ndims = mxGetNumberOfDimensions(mx);
|
samer@0
|
830 const mwSize *dims = mxGetDimensions(mx);
|
samer@0
|
831 const char *cnm = mxGetClassName(mx);
|
samer@0
|
832
|
samer@0
|
833 if (PL_unify_atom_chars(type, cnm)) {
|
samer@0
|
834 if (dims[ndims-1]==1) ndims--; // remove trailing singletons
|
samer@0
|
835 return unify_list_sizes(size,dims,ndims);
|
samer@0
|
836 }
|
samer@0
|
837 PL_fail;
|
samer@0
|
838 } catch (PlException &e) {
|
samer@0
|
839 return e.plThrow();
|
samer@0
|
840 }
|
samer@0
|
841 }
|
samer@0
|
842
|
samer@0
|
843 // Convert multidimensional subscript to linear index
|
samer@0
|
844 foreign_t mlMxSub2Ind(term_t mxterm, term_t substerm, term_t indterm)
|
samer@0
|
845 {
|
samer@0
|
846 try {
|
samer@0
|
847 mxArray *mx=term_to_mx(mxterm);
|
samer@0
|
848 mwIndex subs[64]; // 64 dimensional should be enough!
|
samer@0
|
849 long nsubs;
|
samer@0
|
850
|
samer@0
|
851 // get substerm as int array
|
samer@0
|
852 if (!get_list_integers(substerm,&nsubs,(int *)subs)) // !!
|
samer@0
|
853 return PL_warning("Bad subscript list");
|
samer@0
|
854
|
samer@0
|
855 // switch to zero-based subscripts
|
samer@0
|
856 for (int i=0; i<nsubs; i++) subs[i]--;
|
samer@0
|
857
|
samer@0
|
858 int ind = mxCalcSingleSubscript(mx,nsubs,subs);
|
samer@0
|
859 check_array_index(mx,ind);
|
samer@0
|
860
|
samer@0
|
861 return PL_unify_integer(indterm, ind);
|
samer@0
|
862 } catch (PlException &e) {
|
samer@0
|
863 return e.plThrow();
|
samer@0
|
864 }
|
samer@0
|
865 }
|
samer@0
|
866
|
samer@0
|
867 // Dereference double from mx array
|
samer@0
|
868 foreign_t mlMxGetFloat(term_t mxterm, term_t index, term_t value)
|
samer@0
|
869 {
|
samer@0
|
870 try {
|
samer@0
|
871 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
872 long i;
|
samer@0
|
873
|
samer@0
|
874 check(PL_get_long(index,&i));
|
samer@0
|
875 check_array_index(mx,i);
|
samer@0
|
876 if (!mxIsDouble(mx)) { return PL_warning("not numeric"); }
|
samer@0
|
877
|
samer@0
|
878 double *p = (double *)mxGetData(mx);
|
samer@0
|
879 return PL_unify_float(value, p[i-1]);
|
samer@0
|
880 } catch (PlException &e) {
|
samer@0
|
881 return e.plThrow();
|
samer@0
|
882 }
|
samer@0
|
883 }
|
samer@0
|
884
|
samer@0
|
885 // Dereference logical from mx array
|
samer@0
|
886 foreign_t mlMxGetLogical(term_t mxterm, term_t index, term_t value)
|
samer@0
|
887 {
|
samer@0
|
888 try {
|
samer@0
|
889 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
890 long i;
|
samer@0
|
891
|
samer@0
|
892 check(PL_get_long(index,&i));
|
samer@0
|
893 check_array_index(mx,i);
|
samer@0
|
894
|
samer@0
|
895 if (mxIsLogical(mx)) {
|
samer@0
|
896 mxLogical *p = mxGetLogicals(mx);
|
samer@0
|
897 return PL_unify_integer(value,(p[i-1]) ? 1 : 0);
|
samer@0
|
898 } else if (mxIsDouble(mx)) {
|
samer@0
|
899 double *p = (double *)mxGetData(mx);
|
samer@0
|
900 return PL_unify_integer(value, (p[i-1]>0) ? 1 : 0);
|
samer@0
|
901 } else {
|
samer@0
|
902 return PL_warning("neither logical nor numeric");
|
samer@0
|
903 }
|
samer@0
|
904
|
samer@0
|
905 } catch (PlException &e) {
|
samer@0
|
906 return e.plThrow();
|
samer@0
|
907 }
|
samer@0
|
908 }
|
samer@0
|
909
|
samer@0
|
910 // Dereference mxArray from cell array
|
samer@0
|
911 // Note that we return a non-gargage collected atom, otherwise,
|
samer@0
|
912 // the parent cell array would be spoiled when one of its elements
|
samer@0
|
913 // is released and destroyed. However, if the parent cell is
|
samer@0
|
914 // released and destroyed, any remaining references to elements
|
samer@0
|
915 // will be prematurely invalidated.
|
samer@0
|
916 // FIXME: This is almost certain to confuse the garbage collector
|
samer@0
|
917 foreign_t mlMxGetCell(term_t mxterm, term_t index, term_t value)
|
samer@0
|
918 {
|
samer@0
|
919 try {
|
samer@0
|
920 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
921 long i;
|
samer@0
|
922
|
samer@0
|
923 check(PL_get_long(index,&i));
|
samer@0
|
924 check_array_index(mx,i);
|
samer@0
|
925 if (!mxIsCell(mx)) { return PL_warning("not numeric"); }
|
samer@0
|
926
|
samer@0
|
927 mxArray *p = mxGetCell(mx,i-1);
|
samer@0
|
928 return PL_unify_blob(value, (void **)&p, sizeof(p), &mxnogc_blob);
|
samer@0
|
929 } catch (PlException &e) {
|
samer@0
|
930 return e.plThrow();
|
samer@0
|
931 }
|
samer@0
|
932 }
|
samer@0
|
933
|
samer@0
|
934 foreign_t mlMxGetField(term_t mxterm, term_t index, term_t field, term_t value)
|
samer@0
|
935 {
|
samer@0
|
936 try {
|
samer@0
|
937 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
938 long i;
|
samer@0
|
939 char *fname;
|
samer@0
|
940
|
samer@0
|
941 check(PL_get_long(index,&i));
|
samer@0
|
942 check(PL_get_atom_chars(field,&fname));
|
samer@0
|
943 check_array_index(mx,i);
|
samer@0
|
944 if (!mxIsStruct(mx)) { return PL_warning("not a structure"); }
|
samer@0
|
945
|
samer@0
|
946 mxArray *p = mxGetField(mx,i-1,fname);
|
samer@0
|
947 if (!p) return PL_warning("Field not present");
|
samer@0
|
948 return PL_unify_blob(value, (void **)&p, sizeof(p), &mxnogc_blob);
|
samer@0
|
949 } catch (PlException &e) {
|
samer@0
|
950 return e.plThrow();
|
samer@0
|
951 }
|
samer@0
|
952 }
|
samer@0
|
953
|
samer@0
|
954 // Create numeric array. Currently only real double arrays created
|
samer@0
|
955 foreign_t mlMxCreateNumeric(term_t size, term_t mx) {
|
samer@0
|
956 try {
|
samer@0
|
957 mwSize dims[64];
|
samer@0
|
958 long ndims;
|
samer@0
|
959
|
samer@0
|
960 // get size as int array
|
samer@0
|
961 if (!get_list_integers(size,&ndims,(int *)dims))
|
samer@0
|
962 return PL_warning("Bad size list");
|
samer@0
|
963
|
samer@0
|
964 mxArray *p = mxCreateNumericArray(ndims,dims,mxDOUBLE_CLASS,mxREAL);
|
samer@0
|
965 return PL_unify_blob(mx, (void **)&p, sizeof(p), &mxnogc_blob);
|
samer@0
|
966 } catch (PlException &e) {
|
samer@0
|
967 return e.plThrow();
|
samer@0
|
968 }
|
samer@0
|
969 }
|
samer@0
|
970
|
samer@0
|
971 // Create cell array.
|
samer@0
|
972 foreign_t mlMxCreateCell(term_t size, term_t mx) {
|
samer@0
|
973 try {
|
samer@0
|
974 mwSize dims[64];
|
samer@0
|
975 long ndims;
|
samer@0
|
976
|
samer@0
|
977 // get size as int array
|
samer@0
|
978 if (!get_list_integers(size,&ndims,(int *)dims))
|
samer@0
|
979 return PL_warning("Bad size list");
|
samer@0
|
980
|
samer@0
|
981 mxArray *p = mxCreateCellArray(ndims,dims);
|
samer@0
|
982 return PL_unify_blob(mx, (void **)&p, sizeof(p), &mxnogc_blob);
|
samer@0
|
983 } catch (PlException &e) {
|
samer@0
|
984 return e.plThrow();
|
samer@0
|
985 }
|
samer@0
|
986 }
|
samer@0
|
987
|
samer@0
|
988 // Create numeric array. Currently only real double arrays created
|
samer@0
|
989 foreign_t mlMxCreateString(term_t string, term_t mx) {
|
samer@0
|
990 try {
|
samer@0
|
991 mxArray *p = mxCreateString(PlTerm(string));
|
samer@0
|
992 return PL_unify_blob(mx, (void **)&p, sizeof(p), &mxnogc_blob);
|
samer@0
|
993 } catch (PlException &e) {
|
samer@0
|
994 return e.plThrow();
|
samer@0
|
995 }
|
samer@0
|
996 }
|
samer@0
|
997
|
samer@0
|
998
|
samer@0
|
999 // Write float into double array
|
samer@0
|
1000 foreign_t mlMxPutFloat(term_t mxterm, term_t index, term_t value)
|
samer@0
|
1001 {
|
samer@0
|
1002 try {
|
samer@0
|
1003 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
1004 long i;
|
samer@0
|
1005 double val;
|
samer@0
|
1006
|
samer@0
|
1007 if (!mxIsDouble(mx)) { return PL_warning("not numeric"); }
|
samer@0
|
1008 check(PL_get_long(index,&i));
|
samer@0
|
1009 check(PL_get_float(value,&val));
|
samer@0
|
1010 check_array_index(mx,i);
|
samer@0
|
1011 *(mxGetPr(mx)+i-1)=val;
|
samer@0
|
1012 return true;
|
samer@0
|
1013 } catch (PlException &e) {
|
samer@0
|
1014 return e.plThrow();
|
samer@0
|
1015 }
|
samer@0
|
1016 }
|
samer@0
|
1017
|
samer@0
|
1018 // Write list of floats into double array starting at given index
|
samer@0
|
1019 foreign_t mlMxPutFloats(term_t mxterm, term_t index, term_t values)
|
samer@0
|
1020 {
|
samer@0
|
1021 try {
|
samer@0
|
1022 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
1023 long i, len;
|
samer@0
|
1024
|
samer@0
|
1025 if (!mxIsDouble(mx)) { return PL_warning("not numeric"); }
|
samer@0
|
1026 check(PL_get_long(index,&i));
|
samer@0
|
1027 check_array_index(mx,i);
|
samer@0
|
1028 get_list_doubles(values,&len,mxGetPr(mx)+i-1);
|
samer@0
|
1029 return true;
|
samer@0
|
1030 } catch (PlException &e) {
|
samer@0
|
1031 return e.plThrow();
|
samer@0
|
1032 }
|
samer@0
|
1033 }
|
samer@0
|
1034
|
samer@0
|
1035 // Put an mxArray into a cell array
|
samer@0
|
1036 // IMPORTANT: the object being put must in a non-memory managed atom
|
samer@0
|
1037 foreign_t mlMxPutCell(term_t mxterm, term_t index, term_t element)
|
samer@0
|
1038 {
|
samer@0
|
1039 try {
|
samer@0
|
1040 mxArray *mx = term_to_mx(mxterm);
|
samer@0
|
1041 mxArray *el = term_to_mx(element);
|
samer@0
|
1042 long i;
|
samer@0
|
1043
|
samer@0
|
1044 if (!mxIsCell(mx)) { return PL_warning("not a cell array"); }
|
samer@0
|
1045 check(PL_get_long(index,&i));
|
samer@0
|
1046 check_array_index(mx,i);
|
samer@0
|
1047 mxSetCell(mx,i-1,el);
|
samer@0
|
1048 return true;
|
samer@0
|
1049 } catch (PlException &e) {
|
samer@0
|
1050 return e.plThrow();
|
samer@0
|
1051 }
|
samer@0
|
1052 }
|
samer@0
|
1053
|
samer@0
|
1054 foreign_t mlMxCopyNoGC(term_t in, term_t out)
|
samer@0
|
1055 {
|
samer@0
|
1056 try {
|
samer@0
|
1057 mxArray *mx = term_to_mx(in);
|
samer@0
|
1058 mxArray *p = mxDuplicateArray(mx);
|
samer@0
|
1059 return PL_unify_blob(out, (void **)&p, sizeof(p), &mxnogc_blob);
|
samer@0
|
1060 } catch (PlException &e) {
|
samer@0
|
1061 return e.plThrow();
|
samer@0
|
1062 }
|
samer@0
|
1063 }
|
samer@0
|
1064
|
samer@0
|
1065 foreign_t mlMxNewRefGC(term_t in, term_t out)
|
samer@0
|
1066 {
|
samer@0
|
1067 try {
|
samer@0
|
1068 mxArray *p = term_to_mx(in);
|
samer@0
|
1069 return PL_unify_blob(out, (void **)&p, sizeof(p), &mx_blob);
|
samer@0
|
1070 } catch (PlException &e) {
|
samer@0
|
1071 return e.plThrow();
|
samer@0
|
1072 }
|
samer@0
|
1073 }
|
samer@0
|
1074
|
samer@0
|
1075
|
samer@0
|
1076 /*
|
samer@0
|
1077 * Local Variables:
|
samer@0
|
1078 * c-basic-offset: 2
|
samer@0
|
1079 * indent-tabs-mode: nil
|
samer@0
|
1080 * End:
|
samer@0
|
1081 */
|
samer@0
|
1082
|