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