samer@0
|
1 *** Prolog Matlab interface
|
samer@0
|
2 ***
|
samer@0
|
3 *** Authors:
|
samer@0
|
4 *** Samer Abdallah
|
samer@0
|
5 *** Centre for Digital Music,
|
samer@0
|
6 *** Queen Mary, University of London
|
samer@0
|
7 ***
|
samer@0
|
8 *** Christophe Rhodes
|
samer@0
|
9 *** Centre for Computational Creativity
|
samer@0
|
10 *** Goldsmiths College, University of London
|
samer@0
|
11 ***
|
samer@19
|
12 *** 2004--2012
|
samer@0
|
13
|
samer@0
|
14
|
samer@8
|
15 -------------------------------------------------------------------------
|
samer@8
|
16 NB: output type tagging system has changed but this documentation
|
samer@8
|
17 has not yet. See pldoc documentation of plml.pl.
|
samer@8
|
18
|
samer@19
|
19 Also: you need to make sure the required Matlab dynamic libraries are
|
samer@19
|
20 on your dyld search path before starting SWI Prolog. The bash script
|
samer@19
|
21 swiplml shows how you can do this by setting the DYLD_FALLBACK_LIBRARY_PATH
|
samer@19
|
22 environment variable before calling swipl, but be aware that there
|
samer@19
|
23 are several environment variables that can affect dynamic library loading
|
samer@19
|
24 and that they can have weird effects on your system if you have conflicting
|
samer@19
|
25 libraries is different places. See the dyld man page ('man dyld').
|
samer@0
|
26
|
samer@0
|
27
|
samer@0
|
28 -------------------------------------------------------------------------
|
samer@0
|
29 OVERVIEW
|
samer@0
|
30
|
samer@0
|
31 PLML is a foreign interface that enables Matlab to be used as a computational
|
samer@0
|
32 engine from within SWI Prolog. The basic idea is that instead of using
|
samer@0
|
33 the standard is/2 operator to evaluate a certain class of terms, we can
|
samer@0
|
34 use the ===/2 operator to get Matlab to evaluate a (much richer) class of
|
samer@0
|
35 terms, eg
|
samer@0
|
36
|
samer@0
|
37 ?- float(A)===trace(eye(3)).
|
samer@0
|
38
|
samer@0
|
39 A = 3.0
|
samer@0
|
40
|
samer@0
|
41 We can also get Matlab to perform actions with side effects, like
|
samer@0
|
42 making sounds and graphics; obviously these do not fit into the declartive
|
samer@0
|
43 semantics of Prolog and have to be dealt with under the procedural semantics.
|
samer@0
|
44 If you want to execute a Matlab command in an imperative way and see the
|
samer@0
|
45 textual output, use the ??/1 operator, eg
|
samer@0
|
46
|
samer@0
|
47 ?- ??disp(`hello).
|
samer@0
|
48 >> hello
|
samer@0
|
49
|
samer@0
|
50
|
samer@0
|
51
|
samer@0
|
52 The interface works by using the Matlab Engine API, which starts up a Matlab
|
samer@0
|
53 process on the end of a pipe. The Matlab process can be on another machine,
|
samer@0
|
54 and multiple Matlab engines can be started on the same or different machines.
|
samer@0
|
55 Matlab expressions are sent down the pipe and executed. Matlab's textual
|
samer@0
|
56 output comes back through the pipe. In addition, Matlab variables can be
|
samer@0
|
57 transferred directly between the Matlab engine's memory space and SWI's
|
samer@0
|
58 memory space.
|
samer@0
|
59
|
samer@0
|
60
|
samer@0
|
61 *** Expression language
|
samer@0
|
62
|
samer@0
|
63 Expressions to evaluate are given in a sublanguage of terms which is
|
samer@0
|
64 similar to but not exactly the same as Matlab. In particular, Prolog
|
samer@0
|
65 syntax cannot accommodate the single quoted Matlab strings,
|
samer@0
|
66 the Matlab syntax of matrices, (eg [1 2; 3 4]), and the Matlab syntax
|
samer@0
|
67 for slicing arrays (eg A(:,3:4)) if A is a Prolog variable.
|
samer@0
|
68 Strings are handled using the q/1 or `/1 functors, ie `hello and q(hello)
|
samer@0
|
69 both evaluate to 'hello'. Arrays can be given either as flat lists,
|
samer@0
|
70 which are interpreted as horizontal concatenation as in Matlab:
|
samer@0
|
71
|
samer@0
|
72 ?- ??[1,2,3].
|
samer@0
|
73 >> ans = 1 2 3
|
samer@0
|
74
|
samer@0
|
75 ?- ??[eye(2),magic(2)].
|
samer@0
|
76 >> ans =
|
samer@0
|
77 1 0 1 3
|
samer@0
|
78 0 1 4 2
|
samer@0
|
79
|
samer@0
|
80 or as nested listed for multidimensional arrays using the arr/1 functor,
|
samer@0
|
81 where the innermost nesting corresponds to the FIRST Matlab dimensions
|
samer@0
|
82
|
samer@0
|
83 ?- ??arr([1,2,3]).
|
samer@0
|
84 >> ans =
|
samer@0
|
85 1
|
samer@0
|
86 2
|
samer@0
|
87 3
|
samer@0
|
88
|
samer@0
|
89 ?- ??arr([[1,2],[3,4]]).
|
samer@0
|
90 >> ans =
|
samer@0
|
91 1 3
|
samer@0
|
92 2 4
|
samer@0
|
93
|
samer@0
|
94 Cell arrays can be specified in a similar way using braces or the cell/1 functor.
|
samer@0
|
95
|
samer@0
|
96
|
samer@0
|
97 To help with accessing array elements, see the Matlab functions general/paren,
|
samer@0
|
98 general/row, and general/col in the matlab directory.
|
samer@0
|
99
|
samer@0
|
100
|
samer@0
|
101
|
samer@0
|
102 *** Return values
|
samer@0
|
103
|
samer@0
|
104 The results of computations can handled in several ways:
|
samer@0
|
105
|
samer@0
|
106 1. Keep the result in a Matlab workspace variable in the engine's memory
|
samer@0
|
107 space. The names of these variables are allocated automatically
|
samer@0
|
108 and stored in a Prolog atom. The atoms have a garbage collection
|
samer@0
|
109 callback which means that the Matlab workspace variable is deleted
|
samer@0
|
110 if the Prolog atom goes out of scope.
|
samer@0
|
111
|
samer@0
|
112 ?- A===2+2, ??disp(A).
|
samer@0
|
113 >> 4 % matlab textual output
|
samer@0
|
114
|
samer@0
|
115 A = ws(<ml:t_2311>) % Prolog blob pointing to Matlab variable t_2311
|
samer@0
|
116
|
samer@0
|
117
|
samer@0
|
118
|
samer@0
|
119 2. Convert the result to a prolog atom or term. The type of the resulting
|
samer@0
|
120 prolog term depends on the *right hand side* of the ===/2 operator:
|
samer@0
|
121
|
samer@0
|
122 ?- int(A)===2+2.
|
samer@0
|
123 A = 4
|
samer@0
|
124
|
samer@0
|
125 ?- float(A)===2+2.
|
samer@0
|
126 A = 4.0
|
samer@0
|
127
|
samer@0
|
128
|
samer@0
|
129 There are other types for strings and atoms:
|
samer@0
|
130
|
samer@0
|
131 ?- atom(A) === q(hello). % q/1 means quote as Matlab string
|
samer@0
|
132 A = hello.
|
samer@0
|
133
|
samer@0
|
134 ?- string(A) === `hello. % `/1 is shorthand for q/1
|
samer@0
|
135 A = "hello".
|
samer@0
|
136
|
samer@0
|
137
|
samer@0
|
138 You can also get the result as a Matlab binary array on the Prolog side:
|
samer@0
|
139
|
samer@0
|
140 ?- mx(A)===eye(4). % identity matrix
|
samer@0
|
141 A = <#0239c3a0> % Prolog blob handle (with garbage collection)
|
samer@0
|
142
|
samer@0
|
143
|
samer@0
|
144 I haven't completely settled on the best way of handling arrays as
|
samer@0
|
145 self-contained Prolog terms, but you can do this:
|
samer@0
|
146
|
samer@0
|
147 ?- array(A)===magic(3).
|
samer@0
|
148 A = [[8.0, 3.0, 4.0], [1.0, 5.0, 9.0], [6.0, 7.0, 2.0]]::[[3, 3]]
|
samer@0
|
149
|
samer@0
|
150 As you can see, multidimensional arrays are returned as nested lists, and the
|
samer@0
|
151 size of the array is given after the :: as [[3,3]].
|
samer@0
|
152
|
samer@0
|
153
|
samer@0
|
154 3. Store the result to a MAT file and return a Prolog term which points to the
|
samer@0
|
155 file. The names are generated automatically. This allows for persistence
|
samer@0
|
156 of values which are referred to by stable names that can be stored, eg
|
samer@0
|
157 in a database:
|
samer@0
|
158
|
samer@0
|
159 ?- mat(A)===fft(buffer(wavread('somefile.wav'),256,128)).
|
samer@0
|
160
|
samer@0
|
161 A = mat:d0608/m48598|x % dynamically generated unique locator
|
samer@0
|
162
|
samer@0
|
163 This relies on the mechanism provided by the functions in matlab/db.
|
samer@0
|
164 A certain directory is designed the root of a 'matbase' (MAT file database).
|
samer@0
|
165 The Matlab function dbroot returns or sets this directory:
|
samer@0
|
166
|
samer@0
|
167 ?- ??dbroot.
|
samer@0
|
168 >>
|
samer@0
|
169 ans =
|
samer@0
|
170
|
samer@0
|
171 /Users/samer/matbase
|
samer@0
|
172
|
samer@0
|
173 ?- ??dbroot(q('/usr/share/lib/matbase')). % switch to shared matbase
|
samer@0
|
174 >>
|
samer@0
|
175 ans =
|
samer@0
|
176
|
samer@0
|
177 /usr/share/lib/matbase
|
samer@0
|
178
|
samer@0
|
179 In this case, the locator mat:d0608/m48598|x refers to a Matlab variable called
|
samer@0
|
180 'x' (it's always 'x') in the file /usr/share/lib/matbase/d0608/m48598.mat.
|
samer@0
|
181 A new directory is created each month, and the filenames are chosen dynamically
|
samer@0
|
182 to avoid clashes with existing files.
|
samer@0
|
183
|
samer@0
|
184
|
samer@0
|
185
|
samer@0
|
186 *** Debugging/tracing
|
samer@0
|
187
|
samer@0
|
188 To help with debugging, you can issue the command:
|
samer@0
|
189
|
samer@19
|
190 ?- debug(plml).
|
samer@0
|
191
|
samer@0
|
192 which will cause each Matlab expression to be printed in its Matlab form
|
samer@0
|
193 before execution.
|
samer@0
|
194 I'm afraid the best documentation is the code itself, but I do intend to
|
samer@0
|
195 produce a manual once some of the more embarrassing aspects of system
|
samer@0
|
196 are resolved!
|
samer@0
|
197
|
samer@0
|
198
|
samer@0
|
199
|
samer@0
|
200 -------------------------------------------------------------------------
|
samer@0
|
201 BUILDING/INSTALLATION
|
samer@0
|
202
|
samer@0
|
203 See INSTALL
|
samer@0
|
204
|
samer@0
|
205
|
samer@0
|
206
|
samer@0
|
207
|
samer@0
|
208 -------------------------------------------------------------------------
|
samer@0
|
209 CHANGES
|
samer@0
|
210
|
samer@0
|
211 12/04 - Using Prolog blobs with garbage collection to handle
|
samer@0
|
212 Matlab workspace temporary variables. Works but code is
|
samer@0
|
213 still a little messy. Would like to unify variable handling-
|
samer@0
|
214 the important functions are alloc, free, get, put.
|
samer@0
|
215 Also, garbage collection seems to be rather difficult to
|
samer@0
|
216 provoke.
|
samer@0
|
217
|
samer@0
|
218 2005-08-08
|
samer@0
|
219 Handle Matlab errors in mlEXEC by setting lasterr() before and
|
samer@0
|
220 checking it after engEvalString. If we do get a Matlab
|
samer@0
|
221 error, then throw a Prolog exception, because nothing
|
samer@0
|
222 else is safe in general. CSR.
|
samer@0
|
223
|
samer@0
|
224 2005-08-09
|
samer@0
|
225 Be a little more paranoid in handling workspace variables, both
|
samer@0
|
226 in terms of checking matlab engine error codes and for
|
samer@0
|
227 bounds checking of our own functions such as uniquevar.
|
samer@0
|
228
|
samer@0
|
229 2005-09-26
|
samer@0
|
230 Added matbase_mat/1 to enumerate all mat objects actually in
|
samer@0
|
231 the file system pointed to by the matlab function dbroot.
|
samer@0
|
232
|
samer@0
|
233 2005-11-11
|
samer@0
|
234 Now sending very long Matlab commands via a char array
|
samer@0
|
235 Progress in Prolog-side mxArray support:
|
samer@0
|
236 done:
|
samer@0
|
237 MXINFO - get size and type of array
|
samer@0
|
238 MXSUB2IND - convert multi-dim subscript to linear index
|
samer@0
|
239 MXGETFLOAT - get one element of numeric array (must be real)
|
samer@0
|
240 MXGETLOGICAL - get element of logical or 0|1 array
|
samer@0
|
241 MXGETCELL - get sub mxArray from cell array
|
samer@0
|
242 MXGETREALS - get reals part of all elements as flat Prolog list
|
samer@0
|
243 MXCREATENUMERIC - create double array
|
samer@0
|
244 MXCREATECELL - create cell array
|
samer@0
|
245 MXCREATESTRING - create char array from string or atom
|
samer@0
|
246 MXPUTFLOAT - write one element of double array
|
samer@0
|
247 MXPUTFLOATS - write list of elements to double array
|
samer@0
|
248 MXPUTCELL - put an mxArray into a cell array
|
samer@0
|
249 MXCOPYNOGC - deep copy of array, return NON-MANAGED mx ref
|
samer@0
|
250 MXNEWREFGC - return memory managed ref to array, ie will be GCed
|
samer@0
|
251 to do:
|
samer@0
|
252 Imaginary parts?
|
samer@0
|
253 Reading list of fields from a structure
|
samer@0
|
254 Getting cell array contents as a list
|
samer@0
|
255 tidy up: error checking and function names
|
samer@0
|
256 possibly reduce the amount of bounds checking to improve speed?
|
samer@0
|
257 -> need to do proper profiling!
|
samer@0
|
258
|
samer@0
|
259 2006-11-28
|
samer@0
|
260 Errors generated on the matlab side (ie errors in user functions
|
samer@0
|
261 rather than the mechanisms of this library) throw exceptions of
|
samer@0
|
262 the form mlerror(Engine,Message) instead of just atomic messages.
|
samer@0
|
263
|
samer@0
|
264 Some changes to plml.pl - see header in that file for details.
|
samer@0
|
265
|
samer@0
|
266 2008-11-25
|
samer@0
|
267 Moved declaration of \ operator to ops.pl
|
samer@0
|
268 Changed interface and implementation of ml_open to use list of options.
|
samer@0
|
269 Changed build procedure to use build script and two makefiles.
|
samer@0
|
270
|
samer@0
|
271 2010-02-25
|
samer@0
|
272 Replaced use of mxFree with mxDestroyArray to release resources
|
samer@0
|
273 obtained using engGetVariable - this was causing a malloc error
|
samer@0
|
274 in Matlab 7.9. Also replaced -nojvm with -noawt option when starting
|
samer@0
|
275 Matlab, as -nojvm is no longer supported. Apparently they're going
|
samer@0
|
276 to withdraw support for X11 graphics at some point. I hate them.
|
samer@0
|
277 I'm not 'upgrading' any more.
|
samer@0
|
278
|
samer@0
|
279 2010-03-19
|
samer@0
|
280 Removed dependency on flists
|
samer@0
|
281
|
samer@0
|
282 2010-05-30
|
samer@0
|
283 Merged hostname module into utils.
|
samer@0
|
284
|
samer@0
|
285 2012-01
|
samer@0
|
286 Big overhaul of Prolog part to simplify and speed up.
|
samer@0
|
287 Removed unused Matlab functions from matlab/general.
|
samer@0
|
288 Version 1!
|
samer@0
|
289
|
samer@15
|
290 2012-02
|
samer@15
|
291 Some changes to mlExec and mlWSAlloc to get around a problem that
|
samer@15
|
292 was bedevilling the triserver project, which was using a Matlab
|
samer@15
|
293 engine in a separate thread, receiving requests on a message queue,
|
samer@15
|
294 and calling this library with call_with_time_limit. Something to
|
samer@15
|
295 do with this combination (threads, signals etc) was causing the
|
samer@15
|
296 Prolog system to lock up hard on returning to Prolog system
|
samer@15
|
297 after a failing call to engGetVariable (which was getting stuck and
|
samer@15
|
298 timing out).
|
samer@15
|
299
|
samer@15
|
300 I still don't know what causes the lock-up, but I was able to detect
|
samer@15
|
301 some signs that it was coming *before* calling engGetVariable by looking
|
samer@15
|
302 at the Matlab engine output buffer.
|
samer@15
|
303
|
samer@15
|
304 Eventual work around is not to use engGetVariable to get uniquevar
|
samer@15
|
305 variable names (mlWSAlloc) or lasterr (mlExec) at all, but simply
|
samer@15
|
306 to scrape these out of the output buffer. This seems to be a few
|
samer@15
|
307 milliseconds faster too.
|
samer@15
|
308
|
samer@15
|
309 other changes: removed ml_debug/1 - would rather have a flag to
|
samer@15
|
310 enable printfs in the C++ source.
|
samer@15
|
311
|
samer@18
|
312 2012-02-06
|
samer@18
|
313 Yes, finally, got to the bottom of the locking up bug.
|
samer@18
|
314 The problem was mutlithreaded access to the Matlab engine even when
|
samer@18
|
315 making explicit calls only in one thread IF this is a secondary thread.
|
samer@18
|
316 The reason? Garbage collection in the main thread. Have now added
|
samer@18
|
317 a mutex to protect the Matlab engine (currently one global mutex).
|
samer@18
|
318 If garbage collector is run on a workspace variable blob while the
|
samer@18
|
319 mutex is locked, it fails immediately without blocking. The blob
|
samer@18
|
320 is not reclaimed but will be can be reclaimed the next time gc is run.
|
samer@18
|
321
|
samer@19
|
322 2012-02-09
|
samer@19
|
323 Now using library(debug) for debugging messages. Added more helpful
|
samer@19
|
324 message if foreign library fails to load.
|
samer@19
|
325
|
samer@0
|
326 -------------------------------------------------------------------------
|
samer@0
|
327 ACKNOWLEDGMENTS
|
samer@0
|
328
|
samer@0
|
329 This work was partially supported by UK EPSRC grants GR/S84750/01 and
|
samer@0
|
330 GR/S82213/01.
|
samer@0
|
331
|