annotate prolog/plml.pl @ 3:9b16fbec2f33

Added installer for matlab scripts.
author samer
date Thu, 19 Jan 2012 14:04:55 +0000
parents 4d183f2855c2
children 60b7b78b3167
rev   line source
samer@0 1 /*
samer@0 2 * Prolog part of Prolog-Matlab interface
samer@1 3 * Version 1
samer@0 4 *
samer@0 5 * Samer Abdallah (2004-2012)
samer@0 6 * Centre for Digital Music, QMUl.
samer@0 7 */
samer@0 8
samer@0 9 :- module(plml,
samer@0 10 [ ml_open/1 % (+Id)
samer@0 11 , ml_open/2 % (+Id, +Host)
samer@0 12 , ml_open/3 % (+Id, +Host, +Options)
samer@0 13 , ml_close/1 % (+Id)
samer@0 14
samer@0 15 , ml_exec/2 % (+Id, +Expr)
samer@0 16 , ml_eval/4 % (+Id, +Expr, +Types, -Vals)
samer@0 17 , ml_test/2 % (+Id, +Expr)
samer@0 18
samer@0 19 , (??)/1 % (+Expr) ~execute Matlab expression
samer@0 20 , (???)/1 % (+Expr) ~test Matlab boolean expression
samer@0 21 , (===)/2 % (-Vals,+Expr) ~evaluate Matlab expression
samer@0 22
samer@0 23 , term_mlstring/3 % (+Id, +Expr, -String) ~Prolog term to Matlab string
samer@0 24 , term_texatom/2 % (+Expr, -Atom) ~Prolog term to TeX expression
samer@0 25 , ml_debug/1 % (+Bool)
samer@0 26 , wsvar/3 % (+WSBlob, -Name, -Id)
samer@0 27
samer@0 28 % MATBASE
samer@0 29 , persist_item/2 % (+Expr,-Expr) ~ convert volatile subterms to persistent form
samer@0 30 , matbase_mat/2 % (+Dir, -Loc) ~ Find matbase MAT files
samer@0 31 , dropmat/2 % (+Id, +Loc) ~ remove MAT file from matbase
samer@0 32 , exportmat/3 % (+Id, +Loc, +Dir) ~ export MAT file from matbase
samer@0 33
samer@0 34
samer@0 35 % Utilities
samer@0 36 , compileoptions/2
samer@0 37 , multiplot/2
samer@0 38 , mhelp/1
samer@0 39
samer@0 40 , op(650,fy,`) % quoting things
samer@0 41 , op(160,xf,``) % postfix transpose operator
samer@0 42 , op(100,fy,@) % function handles
samer@0 43
samer@0 44 % note slightly reduced precedence of array operators -
samer@0 45 % hope this doesn't break everything...
samer@0 46 , op(210,xfy,.^) % array exponentiation
samer@0 47 , op(410,yfx,.*) % array times
samer@0 48 , op(410,yfx,./) % array division
samer@0 49 , op(410,xfy,.\) % reverse array division
samer@0 50 , op(400,xfy,\) % reverse matrix division
samer@0 51 , op(700,xfx,===) % variable binding/assignment in matlab query
samer@0 52 , op(700,xfx,:==) % variable binding/assignment in matlab query
samer@0 53 , op(951,fx,??) % evaluate term as matlab
samer@0 54 , op(951,fx,???) % evaluate term as matlab boolean
samer@0 55 , op(100,yfx,#) % field indexing (note left-associativity)
samer@0 56 , op(750,fy,\\) % thunk abdstraction
samer@0 57 , op(750,xfy,\\) % lambda abdstraction
samer@0 58
samer@0 59 % exported after being imported from ops
samer@0 60 , op(1100,xfx,::) % type specification (esp for arrays)
samer@0 61 ]).
samer@0 62
samer@0 63
samer@0 64 :- multifile(user:optionset/2).
samer@0 65 :- multifile(user:matlab_path/2).
samer@0 66 :- multifile(user:matlab_init/2).
samer@0 67 :- multifile(user:pl2ml_hook/2).
samer@0 68
samer@0 69
samer@0 70 /** <module> Prolog-Matlab interface
samer@0 71
samer@0 72 ---++++ Types
samer@0 73
samer@0 74 *|ml_eng|* - Any atom identifying a Matlab engine.
samer@0 75
samer@0 76 *|ml_stmt|* - A Matlab statement
samer@0 77 ==
samer@0 78 X;Y :: ml_stmt :- X:ml_stmt, Y:ml_stmt.
samer@0 79 X,Y :: ml_stmt :- X:ml_stmt, Y:ml_stmt.
samer@0 80 X=Y :: ml_stmt :- X:ml_lval, Y:ml_expr.
samer@0 81 hide(X) :: ml_stmt :- X:ml_stmt.
samer@0 82 ==
samer@0 83
samer@0 84 ==
samer@0 85 ml_expr(A) % A Matlab expression, possibly with multiple return values
samer@0 86 ml_loc ---> mat(atom,atom). % Matbase locator
samer@0 87 ==
samer@0 88
samer@0 89 ---++++ Matlab expression syntax
samer@0 90
samer@0 91 The Matlab expression syntax adopted by this module allows Prolog terms to represent
samer@0 92 or denote Matlab expressions. Let T be the domain of recognised Prolog terms (corresponding to
samer@0 93 the type ml_expr), and M be the domain of Matlab expressions written in Matlab syntax.
samer@0 94 Then V : T->M is the valuation function which maps Prolog term X to Matlab expression V[X].
samer@0 95 These are some of the constructs it recognises:
samer@0 96
samer@0 97 Constructs valid only in top level statements, not subexpressions:
samer@0 98 ==
samer@0 99 X;Y % |--> V[X]; V[Y] (sequential evaluation hiding first result)
samer@0 100 X,Y % |--> V[X], V[Y] (sequential evaluation displaying first result)
samer@0 101 X=Y % |--> V[X]=V[Y] (assignment, X must denote a valid left-value)
samer@0 102 hide(X) % |--> V[X]; (execute X but hide return value)
samer@0 103 ==
samer@0 104
samer@0 105 Things that look and work like Matlab syntax (more or less):
samer@0 106 ==
samer@0 107 +X % |--> uplus(V[X])
samer@0 108 -X % |--> uminus(V[X])
samer@0 109 X+Y % |--> plus(V[X],V[Y])
samer@0 110 X-Y % |--> minus(V[X],V[Y])
samer@0 111 X^Y % |--> mpower(V[X],V[Y])
samer@0 112 X*Y % |--> mtimes(V[X],V[Y])
samer@0 113 X/Y % |--> mrdivide(V[X],V[Y])
samer@0 114 X\Y % |--> mldivide(V[X],V[Y])
samer@0 115 X.^Y % |--> power(V[X],V[Y])
samer@0 116 X.*Y % |--> times(V[X],V[Y])
samer@0 117 X./Y % |--> rdivide(V[X],V[Y])
samer@0 118 X.\Y % |--> ldivide(V[X],V[Y])
samer@0 119 X:Y:Z % |--> colon(V[X],V[Y],V[Z])
samer@0 120 X:Z % |--> colon(V[X],V[Z])
samer@0 121 X>Z % |--> gt(V[X],V[Y])
samer@0 122 X>=Z % |--> ge(V[X],V[Y])
samer@0 123 X<Z % |--> lt(V[X],V[Y])
samer@0 124 X=<Z % |--> le(V[X],V[Y])
samer@0 125 X==Z % |--> eq(V[X],V[Y])
samer@0 126 [X1,X2,...] % |--> [ V[X1], V[X2], ... ]
samer@0 127 [X1;X2;...] % |--> [ V[X1]; V[X2]; ... ]
samer@0 128 {X1,X2,...} % |--> { V[X1], V[X2], ... }
samer@0 129 {X1;X2;...} % |--> { V[X1]; V[X2]; ... }
samer@0 130 @X % |--> @V[X] (function handle)
samer@0 131 ==
samer@0 132
samer@0 133 Things that do not look like Matlab syntax but provide standard Matlab features:
samer@0 134 ==
samer@0 135 'Infinity' % |--> inf (positive infinity)
samer@0 136 'Nan' % |--> nan (not a number)
samer@0 137 X`` % |--> ctranpose(V[X]) (conjugate transpose, V[X]')
samer@0 138 X#Y % |--> getfield(V[X],V[q(Y)])
samer@0 139 X\\Y % |--> @(V[X])V[Y] (same as lambda(X,Y))
samer@0 140 \\Y % |--> @()V[Y] (same as thunk(Y))
samer@0 141 lambda(X,Y) % |--> @(V[X])V[Y] (anonymous function with arguments X)
samer@0 142 thunk(Y) % |--> @()V[Y] (anonymous function with no arguments)
samer@0 143 vector(X) % |--> horzcat(V[X1],V[X2], ...)
samer@0 144 atvector(X) % as vector but assumes elements of X are assumed all atomic
samer@0 145 cell(X) % construct 1xN cell array from elements of X
samer@0 146 `X % same as q(X)
samer@0 147 q(X) % wrap V[X] in single quotes (escaping internal quotes)
samer@0 148 qq(X) % wrap V[X] in double quotes (escaping internal double quotes)
samer@0 149 tq(X) % wrap TeX expression in single quotes (escape internal quotes)
samer@0 150 ==
samer@0 151
samer@0 152 Referencing different value representations.
samer@0 153 ==
samer@0 154 mat(X,Y) % denotes a value in the Matbase using a dbload expression
samer@0 155 mx(X:mx_blob) % denotes an MX Matlab array in SWI memory
samer@0 156 ws(X:ws_blob) % denotes a variable in a Matlab workspace
samer@0 157 wsseq(X:ws_blob) % workspace variable containing list as cell array.
samer@0 158 ==
samer@0 159
samer@0 160 Tricky bits.
samer@0 161 ==
samer@0 162 apply(X,AX) % X must denote a function or array, applied to list of arguments AX.
samer@0 163 cref(X,Y) % cell dereference, |--> V[X]{ V[Y1], V[Y2], ... }
samer@0 164 arr(Lists) % multidimensional array from nested lists.
samer@0 165 arr(Lists,Dims) % multidimensional array from nested lists.
samer@0 166 ==
samer@0 167
samer@0 168 Things to bypass default formatting
samer@0 169 ==
samer@0 170 noeval(_) % triggers a failure when processed
samer@0 171 atom(X) % write atom X as write/1
samer@0 172 term(X) % write term X as write/1
samer@0 173 \(P) % escape and call phrase P directly to generate Matlab string
samer@0 174 $(X) % calls pl2ml_hook/2, denotes V[Y] where plml_hook(X,Y).
samer@0 175 '$VAR'(N) % gets formatted as p_N where N is assumed to be atomic.
samer@0 176 ==
samer@0 177
samer@0 178 All other Prolog atoms are written using write/1, while other Prolog terms
samer@0 179 are assumed to be calls to Matlab functions named according to the head functor.
samer@0 180 Thus V[ <head>( <arg1>, <arg2>, ...) ] = <head>(V[<arg1>, V[<arg2>], ...).
samer@0 181
samer@0 182 There are some incompatibilities between Matlab syntax and Prolog syntax,
samer@0 183 that is, syntactic structures that Prolog cannot parse correctly:
samer@0 184
samer@0 185 * 'Command line' syntax, ie where a function of string arguments:
samer@0 186 "save('x','Y')" can be written as "save x Y" in Matlab,
samer@0 187 but in Prolog, you must use function call syntax with quoted arguments:
samer@0 188 save(`x,`'Y').
samer@0 189
samer@0 190 * Matlab's postfix transpose operator "x'" must be written using a different
samer@0 191 posfix operator "x``" or function call syntax "ctranspose(x)".
samer@0 192
samer@0 193 * Matlab cell referencing using braces, as in x{1,2} must be written
samer@0 194 as "cref(x,1,2)".
samer@0 195
samer@0 196 * Field referencing using dot (.), eg x.thing - currently resolved
samer@0 197 by using hash (#) operator, eg x#thing.
samer@0 198
samer@0 199 * Using variables as arrays and indexing them. The problem is that
samer@0 200 Prolog doesn't let you write a term with a variable as the head
samer@0 201 functor.
samer@0 202
samer@0 203
samer@0 204 @tbd
samer@0 205
samer@0 206 Use mat(I) and tmp(I) as types to include engine Id.
samer@0 207
samer@0 208 Clarify relationship between return values and valid Matlab denotation.
samer@0 209
samer@0 210 Reshape/2 array representation: reshape([ ... ],Size)
samer@0 211 Expression language: arr(Vals,Shape,InnerFunctor) - allows efficient
samer@0 212 representation of arrays of arbitrary things. Will require more strict
samer@0 213 nested list form.
samer@0 214
samer@0 215 Deprecate old array(Vals::Type) and cell(Vals::Type) left-value syntax.
samer@0 216
samer@0 217 Remove I from ml_expr//2 and add to mx type?
samer@0 218 */
samer@0 219
samer@0 220 :- use_module(library(apply_macros)).
samer@0 221 :- use_module(library(ops)).
samer@0 222 :- use_module(library(utils)).
samer@0 223 :- use_module(library(dcgu)).
samer@0 224
samer@0 225 :- load_foreign_library(foreign(plml)).
samer@0 226
samer@0 227 :- op(700,xfx,===). % variable binding/assignment in matlab query
samer@0 228 :- op(951,fx,??). % evaluate term as matlab
samer@0 229 :- op(951,fx,???). % evaluate term as matlab boolean
samer@0 230 :- op(650,fy,`). % quoting things
samer@0 231 :- op(160,xf,``). % postfix transpose operator
samer@0 232 :- op(100,fy,@). % function handles
samer@0 233 :- op(200,xfy,.^). % array exponentiation
samer@0 234 :- op(410,yfx,.*). % array times
samer@0 235 :- op(410,yfx,./). % array division
samer@0 236 :- op(410,xfy,.\). % array reverse division
samer@0 237 :- op(400,xfy,\). % matrix reverse division
samer@0 238 :- op(100,yfx,#). % field indexing (note left-associativity)
samer@0 239
samer@0 240 :- dynamic plml_flag/2.
samer@0 241
samer@0 242 set_flag(Flag,Value) :-
samer@0 243 ground(Flag),
samer@0 244 retractall(plml_flag(Flag,_)),
samer@0 245 assert(plml_flag(Flag,Value)).
samer@0 246
samer@0 247 :- at_halt(ml_closeall).
samer@0 248
samer@0 249 ml_closeall :-
samer@0 250 forall(plml_flag(ml(Id),open),
samer@0 251 ( format('Closing Matlab engine (~w)...',[Id]),
samer@0 252 ml_close(Id))).
samer@0 253
samer@0 254 %% matlab_init( -Key, -Cmd:ml_expr) is nondet.
samer@0 255 % Each user-defined clause of matlab_init/2 causes Cmd to be executed
samer@0 256 % whenever a new Matlab session is started.
samer@0 257
samer@0 258 %% matlab_path( -Key, -Path:list(atom)) is nondet.
samer@0 259 % Each user-defined clause of matlab_path/2 causes the directories in Path
samer@0 260 % to be added to the Matlab path of every new Matlab session. Directories
samer@0 261 % are relative to the root directory as returned by Matlab function proot.
samer@0 262
samer@0 263 %% pl2ml_hook(+X:term,-Y:ml_expr) is nondet.
samer@0 264 % Clauses of pl2ml_hook/2 allow for extensions to the Matlab expression
samer@0 265 % language such that =|V[$X] = V[Y]|= if =|pl2ml_hook(X,Y)|=.
samer@0 266
samer@0 267
samer@0 268
samer@0 269 %% ml_open(+Id:ml_eng,+Host:atom,+Options:list(_)) is det.
samer@0 270 %% ml_open(+Id:ml_eng, +Host:atom) is det.
samer@0 271 %% ml_open(+Id:ml_eng) is det.
samer@0 272 %
samer@0 273 % Start a Matlab session on the given host. If Host=localhost
samer@0 274 % or the name of the current current host as returned by hostname/1,
samer@0 275 % then a Matlab process is started directly. Otherwise, it is
samer@0 276 % started remotely via SSH. Options defaults to []. Host defaults to
samer@0 277 % localhost.
samer@0 278 %
samer@0 279 % Start a Matlab session on the specified host using default options.
samer@0 280 % If Host is not given, it defaults to localhost. Session will be
samer@0 281 % associated with the given Id, which should be an atom. See ml_open/3.
samer@0 282 %
samer@0 283 % Valid options are
samer@0 284 % * noinit
samer@0 285 % If present, do not run initialisation commands specified by
samer@0 286 % matlab_path/2 and matlab_init/2 clauses. Otherwise, do run them.
samer@0 287 % * debug(In,Out)
samer@0 288 % if present, Matlab is started in a script which captures standard
samer@0 289 % input and output to files In and Out respectively.
samer@0 290 %
samer@0 291 % [What if session is already open and attached to Id?]
samer@0 292
samer@0 293 ml_open(Id) :- ml_open(Id,localhost,[]).
samer@0 294 ml_open(Id,Host) :- ml_open(Id,Host,[]).
samer@0 295 ml_open(Id,Host,Options) :-
samer@0 296 options_flags(Options,Flags),
samer@0 297 ( (Host=localhost;hostname(Host))
samer@0 298 -> Exec='exec matlab' % using exec fixes Ctrl-C bug
samer@0 299 ; Exec='ssh /usr/local/bin/matlab'
samer@0 300 ),
samer@0 301 ( member(debug(In,Out),Options)
samer@0 302 -> format(atom(Exec1),'stdio_catcher ~w ~w nohup ~w',[In,Out,Exec])
samer@0 303 ; Exec1=Exec
samer@0 304 ),
samer@0 305 format(atom(Cmd),'~w ~w',[Exec,Flags]),
samer@0 306 mlOPEN(Cmd,Id),
samer@0 307 set_flag(ml(Id),open),
samer@0 308 ( member(noinit,Options) -> true
samer@0 309 ; forall( matlab_path(_,Dir), maplist(nofail(addpath),Dir)),
samer@0 310 forall( matlab_init(_,Cmd), nofail(Cmd))
samer@0 311 ).
samer@0 312
samer@0 313 addpath(local(D)) :- !, ml_exec(ml,padl(q(D))).
samer@0 314 addpath(D) :- !, ml_exec(ml,padd(q(D))).
samer@0 315
samer@0 316 %% ml_close(+Id:ml_eng) is det.
samer@0 317 % Close Matlab session associated with Id.
samer@0 318 ml_close(Id) :- mlCLOSE(Id), set_flag(ml(Id),closed).
samer@0 319
samer@0 320 nofail(P) :- catch(ignore(call(P)), E, print_message(warning,E)).
samer@0 321 nofail(P,X) :- catch(ignore(call(P,X)), E, print_message(warning,E)).
samer@0 322
samer@0 323 options_flags(_,'nodesktop -nosplash -noawt').
samer@0 324
samer@0 325
samer@0 326 %% ml_exec(+Id:ml_eng, +Expr:ml_expr) is det.
samer@0 327 %
samer@0 328 % Execute Matlab expression without returning any values.
samer@0 329 ml_exec(Id,X) :-
samer@0 330 term_mlstring(Id,X,C), !,
samer@0 331 (plml_flag(debug,true) -> format('ml_exec(~w):~s\n',[Id,C]); true),
samer@0 332 mlEXEC(Id,C).
samer@0 333
samer@0 334 %% ml_eval(+Id:ml_eng, +Expr:ml_expr, +Types:list(type), -Res:list(ml_val)) is det.
samer@0 335 %
samer@0 336 % Evaluate Matlab expression binding return values to results list Res. This new
samer@0 337 % form uses an explicit output types list, so Res can be completely unbound on entry
samer@0 338 % even when multiple values are required.
samer@0 339 ml_eval(Id,X,Types,Vals) :-
samer@0 340 maplist(alloc_ws(Id),Types,Vars),
samer@0 341 ml_exec(Id,hide(wsx(Vars)=X)),
samer@0 342 maplist(convert_ws,Types,Vars,Vals).
samer@0 343
samer@0 344 alloc_ws(I,_,Z) :- mlWSALLOC(I,Z).
samer@0 345
samer@0 346 %% ml_test(+Id:ml_eng, +X:ml_expr(bool)) is semidet.
samer@0 347 % Succeeds if X evaluates to true in Matlab session Id.
samer@0 348 ml_test(Id,X) :- ml_eval(Id,X,[bool],[1]).
samer@0 349
samer@0 350
samer@0 351
samer@0 352 %% ===(Y:ml_vals(A), X:ml_expr(A)) is det.
samer@0 353 % Evaluate Matlab expression X as in ml_eval/4, binding one or more return values
samer@0 354 % to Y. If Y is unbound or a single ml_val(_), only the first return value is bound.
samer@0 355 % If Y is a list, multiple return values are processed.
samer@0 356 Y === X :-
samer@0 357 ( is_list(Y)
samer@0 358 -> maplist(leftval,Y,TX,VX), ml_eval(ml,X,TX,VX)
samer@0 359 ; leftval(Y,T,V), ml_eval(ml,X,[T],[V])
samer@0 360 ).
samer@0 361
samer@0 362 %% leftval( +TVal:tagged(T), -T:type, -Val:T) is det.
samer@0 363 % True if TVal is a tagged value whos type is T and value is Val.
samer@0 364 leftval( ws(X), ws, ws(X)).
samer@0 365 leftval( mx(X), mx, mx(X)).
samer@0 366 leftval( float(X), float, X).
samer@0 367 leftval( int(X), int, X).
samer@0 368 leftval( bool(X), bool, X).
samer@0 369 leftval( atom(X), atom, X).
samer@0 370 leftval( term(X), term, X).
samer@0 371 leftval( string(X), string,X).
samer@0 372 leftval( mat(X), mat, X).
samer@0 373 leftval( tmp(X), tmp, X).
samer@0 374 leftval( loc(X), loc, X).
samer@0 375 leftval( wsseq(X), wsseq, wsseq(X)).
samer@0 376 leftval( list(T,X), list(T), X).
samer@0 377 leftval( array(X::[Size->Type]), array(Type,Size), X) :- !.
samer@0 378 leftval( array(X::[Size]), array(float,Size), X) :- !.
samer@0 379 leftval( cell(X::[Size->Type]), cell(Type,Size), X) :- !.
samer@0 380 leftval( cell(X::[Size]), cell(mx,Size), X) :- !.
samer@0 381 leftval( Val:Type, Type, Val).
samer@0 382
samer@0 383
samer@0 384 %% ??(X:ml_expr(_)) is det.
samer@0 385 % Execute Matlab expression X as with ml_exec/2, without returning any values.
samer@0 386 ?? X :- ml_exec(ml,X).
samer@0 387
samer@0 388 %% ???(X:ml_expr(bool)) is semidet.
samer@0 389 % Evaluate Matlab boolean expression X as with ml_test/2.
samer@0 390 ??? Q :- ml_test(ml,Q).
samer@0 391
samer@0 392
samer@0 393 %% ml_debug(+Flag:boolean) is det.
samer@0 394 % Set or reset debug state. =|ml_debug(true)|= causes formatted Matlab
samer@0 395 % statements to be printed before being sent to Matlab engine.
samer@0 396 ml_debug(F) :- set_flag(debug,F).
samer@0 397
samer@0 398 /*
samer@0 399 * DCG for term to matlab conversion
samer@0 400 * the big problem with Matlab syntax is that you cannot always replace
samer@0 401 * a name representing a value with an expression that reduces to that
samer@0 402 * value. Eg
samer@0 403 * X=magic(5), X(3,4)
samer@0 404 * is ok, but
samer@0 405 * (magic(5))(3,4)
samer@0 406 * is not. Similarly x=@sin, x(0.5) but not (@sin)(0.5)
samer@0 407 * This is really infuriating.
samer@0 408 */
samer@0 409
samer@0 410
samer@0 411 % top level statement rules
samer@0 412 stmt(I,hide(A)) --> !, stmt(I,A), ";".
samer@0 413 stmt(I,(A;B)) --> !, stmt(I,A), ";", stmt(I,B).
samer@0 414 stmt(I,(A,B)) --> !, stmt(I,A), ",", stmt(I,B).
samer@0 415 stmt(I,A=B) --> !, ml_expr(I,A), "=", ml_expr(I,B).
samer@0 416 stmt(I,Expr) --> !, ml_expr(I,Expr).
samer@0 417
samer@0 418
samer@0 419 %% ml_expr(+Id:ml_eng,+X:ml_expr(A))// is nondet.
samer@0 420 % Convert Matlab expression as a Prolog term to string representation.
samer@0 421 ml_expr(_,\X) --> !, X.
samer@0 422 ml_expr(I,$X) --> !, {pl2ml_hook(X,Y)}, ml_expr(I,Y).
samer@0 423 ml_expr(I,q(X)) --> !, q(stmt(I,X)).
samer@0 424 ml_expr(I,qq(X)) --> !, qq(stmt(I,X)).
samer@0 425 ml_expr(_,tq(X)) --> !, q(pl2tex(X)).
samer@0 426 ml_expr(_,atom(X)) --> !, atm(X).
samer@0 427 ml_expr(_,term(X)) --> !, wr(X). % this could be dangerous
samer@0 428 ml_expr(_,mat(X,Y)) --> !, "dbload(", loc(X,Y), ")".
samer@0 429 ml_expr(_,loc(L)) --> !, { L=mat(X,Y) }, loc(X,Y).
samer@0 430 ml_expr(I,mx(X)) --> !, { mlWSALLOC(I,Z), mlWSPUT(Z,X) }, ml_expr(I,ws(Z)).
samer@0 431 ml_expr(I,ws(A)) --> !, { mlWSNAME(A,N,I) }, atm(N).
samer@0 432 ml_expr(I,wsx([A|B])) --> !, { mlWSNAME(A,N,I) }, "[", atm(N), wsx(B), "]".
samer@0 433 ml_expr(I,wsseq(A)) --> !, { mlWSNAME(A,N,I) }, atm(N).
samer@0 434 ml_expr(_,noeval(_)) --> !, {fail}. % causes evaluation to fail.
samer@0 435
samer@0 436 ml_expr(_,'Infinity') --> !, "inf".
samer@0 437 ml_expr(_,'Nan') --> !, "nan".
samer@0 438
samer@0 439 ml_expr(I,A+B) --> !, "plus", args(I,A,B).
samer@0 440 ml_expr(I,A-B) --> !, "minus", args(I,A,B).
samer@0 441 ml_expr(I, -B) --> !, "uminus", args(I,B).
samer@0 442 ml_expr(I, +B) --> !, "uplus", args(I,B).
samer@0 443 ml_expr(I,A^B) --> !, "mpower", args(I,A,B).
samer@0 444 ml_expr(I,A*B) --> !, "mtimes", args(I,A,B).
samer@0 445 ml_expr(I,A/B) --> !, "mrdivide", args(I,A,B).
samer@0 446 ml_expr(I,A\B) --> !, "mldivide", args(I,A,B).
samer@0 447 ml_expr(I,A.^B)--> !, "power", args(I,A,B).
samer@0 448 ml_expr(I,A.*B)--> !, "times", args(I,A,B).
samer@0 449 ml_expr(I,A./B)--> !, "rdivide", args(I,A,B).
samer@0 450 ml_expr(I,A.\B)--> !, "ldivide", args(I,A,B).
samer@0 451 ml_expr(I,A>B) --> !, "gt",args(I,A,B).
samer@0 452 ml_expr(I,A<B) --> !, "lt",args(I,A,B).
samer@0 453 ml_expr(I,A>=B)--> !, "ge",args(I,A,B).
samer@0 454 ml_expr(I,A=<B)--> !, "le",args(I,A,B).
samer@0 455 ml_expr(I,A==B)--> !, "eq",args(I,A,B).
samer@0 456 ml_expr(I,A:B) --> !, range(I,A,B).
samer@0 457
samer@0 458 ml_expr(_,[]) --> !, "[]".
samer@0 459 ml_expr(_,{}) --> !, "{}".
samer@0 460 ml_expr(I,[X]) --> !, "[", matrix(v,I,X), "]".
samer@0 461 ml_expr(I,[X|XX]) --> !, "[", ml_expr(I,X), seqmap(do_then_call(",",ml_expr(I)),XX), "]".
samer@0 462 ml_expr(I,{X}) --> !, "{", matrix(_,I,X), "}".
samer@0 463
samer@0 464 ml_expr(I, `B) --> !, q(stmt(I,B)).
samer@0 465 ml_expr(I,A#B) --> !, "getfield", args(I,A,q(B)).
samer@0 466 ml_expr(I,B``) --> !, "ctranspose", args(I,B).
samer@0 467 ml_expr(_,@B) --> !, "@", atm(B).
samer@0 468 ml_expr(I, \\B) --> !, "@()", ml_expr(I,B).
samer@0 469 ml_expr(I, A\\B) --> !, { term_variables(A,V), varnames(V) },
samer@0 470 "@(", ml_expr(I,A), ")", ml_expr(I,B).
samer@0 471 ml_expr(I,lambda(A,B)) --> !, ml_expr(I,A\\B).
samer@0 472 ml_expr(I,thunk(B)) --> !, ml_expr(I, \\B).
samer@0 473
samer@0 474
samer@0 475 % !! This is problematic: we are using apply to represent both
samer@0 476 % function application and array dereferencing. For function
samer@0 477 % calls, A must be a function name atom or a function handle
samer@0 478 % If A is an array, it cannot be an expression, unless we
samer@0 479 % switch to using the paren Matlab function, which will be slower.
samer@0 480 ml_expr(I,apply(A,B)) --> !, ml_expr(I,A), arglist(I,B).
samer@0 481 ml_expr(I,cref(A,B)) --> !, ml_expr(I,A), "{", clist(I,B), "}".
samer@0 482
samer@0 483 % array syntax
samer@0 484 ml_expr(I,arr($X)) --> !, { pl2ml_hook(X,L) }, ml_expr(I,arr(L)).
samer@0 485 ml_expr(I,arr(L)) --> !, { array_dims(L,D) }, array(D,I,L).
samer@0 486 ml_expr(I,arr(D,L)) --> !, array(D,I,L).
samer@0 487 ml_expr(I,arr(D,L,P)) --> !, array(D,I,P,L).
samer@0 488 ml_expr(I,atvector(L))--> !, "[", clist_at(I,L), "]".
samer@0 489 ml_expr(I,vector(L)) --> !, "[", clist(I,L), "]".
samer@0 490 ml_expr(I,cell(L)) --> !, "{", clist(I,L), "}".
samer@0 491 ml_expr(_,'$VAR'(N)) --> !, "p_", atm(N).
samer@0 492
samer@0 493 % catch these and throw exception
samer@0 494 ml_expr(_,hide(A)) --> {throw(ml_illegal_expression(hide(A)))}.
samer@0 495 ml_expr(_,(A;B)) --> {throw(ml_illegal_expression((A;B)))}.
samer@0 496 ml_expr(_,(A,B)) --> {throw(ml_illegal_expression((A,B)))}.
samer@0 497 ml_expr(_,A=B) --> {throw(ml_illegal_expression(A=B))}.
samer@0 498
samer@0 499 % these are the catch-all clauses which will deal with matlab names, and literals
samer@0 500 % should we filter on the head functor?
samer@0 501 ml_expr(_,A) --> {atomic(A)}, !, atm(A).
samer@0 502 ml_expr(I,F) --> {F=..[H|AX]}, atm(H), arglist(I,AX).
samer@0 503
samer@0 504 ml_expr_with(I,Lambda,Y) --> {copy_term(Lambda,Y\\PY)}, ml_expr(I,PY).
samer@0 505
samer@0 506
samer@0 507 % dimensions implicit in nested list representation
samer@0 508 array_dims([X|_],M) :- !, array_dims(X,N), succ(N,M).
samer@0 509 array_dims(_,0).
samer@0 510
samer@0 511 % efficiently output row vector of workspace variable names
samer@0 512 wsx([]) --> [].
samer@0 513 wsx([A|AX]) --> { mlWSNAME(A,N,_) }, ",", atm(N), wsx(AX).
samer@0 514
samer@0 515 %% array(+Dims:natural, +Id:ml_eng, +Array)// is det.
samer@0 516 %
samer@0 517 % Format nested lists as Matlab multidimensional array.
samer@0 518 % Dims is the number of dimensions of the resulting array and
samer@0 519 % should equal the nesting level of Array, ie if Array=[1,2,3],
samer@0 520 % Dims=1; if Array=[[1,2],[3,4]], Dims=2, etc.
samer@0 521 array(0,I,X) --> !, ml_expr(I,X).
samer@0 522 array(1,I,L) --> !, "[", seqmap_with_sep(";",ml_expr(I),L), "]".
samer@0 523 array(2,I,L) --> !, "[", seqmap_with_sep(",",array(1,I),L), "]".
samer@0 524 array(N,I,L) --> {succ(M,N)}, "cat(", atm(N), ",", seqmap_with_sep(",",array(M,I),L), ")".
samer@0 525
samer@0 526 array(0,I,P,X) --> !, ml_expr_with(I,P,X).
samer@0 527 array(1,I,P,L) --> !, "[", seqmap_with_sep(";",ml_expr_with(I,P),L), "]".
samer@0 528 array(2,I,P,L) --> !, "[", seqmap_with_sep(",",array(1,I,P),L), "]".
samer@0 529 array(N,I,P,L) --> {succ(M,N)}, "cat(", atm(N), ",", seqmap_with_sep(",",array(M,I,P),L), ")".
samer@0 530
samer@0 531 matrix(h,I,(A,B)) --> !, ml_expr(I,A), ",", matrix(h,I,B).
samer@0 532 matrix(v,I,(A;B)) --> !, ml_expr(I,A), ";", matrix(v,I,B).
samer@0 533 matrix(_,I,A) --> !, ml_expr(I,A).
samer@0 534
samer@0 535
samer@0 536 % colon syntax for ranges
samer@0 537 range(I,A,B:C) --> !, "colon", arglist(I,[A,B,C]).
samer@0 538 range(I,A,B) --> !, "colon", args(I,A,B).
samer@0 539
samer@0 540
samer@0 541 %% clist(+Id:ml_eng, +Items:list(ml_expr))// is det.
samer@0 542 % Format list of Matlab expressions in a comma separated list.
samer@0 543 clist(_,[]) --> [].
samer@0 544 clist(I,[L1|LX]) --> ml_expr(I,L1), seqmap(do_then_call(",",ml_expr(I)),LX).
samer@0 545
samer@0 546
samer@0 547 %% clist_at(+Id:ml_eng, +Items:list(ml_expr))// is det.
samer@0 548 % Format list of atoms in a comma separated list.
samer@0 549 clist_at(_,[]) --> [].
samer@0 550 clist_at(_,[L1|LX]) --> atm(L1), seqmap(do_then_call(",",atm),LX).
samer@0 551
samer@0 552
samer@0 553 %% arglist(+Id:ml_eng, +Args:list(ml_expr))// is det.
samer@0 554 % DCG rule to format a list of Matlab expressions as function arguments
samer@0 555 % including parentheses.
samer@0 556 arglist(I,X) --> "(", clist(I,X), ")".
samer@0 557
samer@0 558
samer@0 559 %% args(+Id:ml_eng, +A1:ml_expr, +A2:ml_expr)// is det.
samer@0 560 %% args(+Id:ml_eng, +A1:ml_expr)// is det.
samer@0 561 %
samer@0 562 % DCG rule to format one or two Matlab expressions as function arguments
samer@0 563 % including parentheses.
samer@0 564 args(I,X,Y) --> "(", ml_expr(I,X), ",", ml_expr(I,Y), ")".
samer@0 565 args(I,X) --> "(", ml_expr(I,X), ")".
samer@0 566
samer@0 567
samer@0 568 %% atm(+A:atom)// is det.
samer@0 569 % DCG rule to format an atom using write/1.
samer@0 570 atm(A,C,T) :- with_output_to(codes(C,T),write(A)).
samer@0 571
samer@0 572 varnames(L) :- varnames(1,L).
samer@0 573 varnames(_,[]).
samer@0 574 varnames(N,[TN|Rest]) :-
samer@0 575 atom_concat(p_,N,TN), succ(N,M),
samer@0 576 varnames(M,Rest).
samer@0 577
samer@0 578
samer@0 579 %% term_mlstring(+Id:ml_eng,+X:ml_expr,-Y:list(code)) is det.
samer@0 580 % Convert term representing Matlab expression to a list of character codes.
samer@0 581 term_mlstring(I,Term,String) :- phrase(stmt(I,Term),String), !.
samer@0 582
samer@0 583 %% term_texatom(+X:tex_expr,-Y:atom) is det.
samer@0 584 % Convert term representing TeX expression to a string in atom form.
samer@0 585 term_texatom(Term,Atom) :- phrase(pl2tex(Term),String), !, atom_codes(Atom,String).
samer@0 586
samer@0 587
samer@0 588
samer@0 589 % Once the computation has been done, the MATLAB workspace contains
samer@0 590 % the results which must be transferred in the appropriate form the
samer@0 591 % specified left-values, in one of several forms, eg mxArray pointer,
samer@0 592 % a float, an atom, a string or a locator.
samer@0 593 %
samer@0 594 % Note that requesting a locator causes a further call
samer@0 595 % to MATLAB to do a dbsave.
samer@0 596 %
samer@0 597 % If no type requestor tag is present, then a unique variable name
samer@0 598 % is generated to store the result in the Matlab workspace. This name
samer@0 599 % is returned in the variable as a ws blob.
samer@0 600 % The idea is to avoid unnecessary traffic over the Matlab engine pipe.
samer@0 601
samer@0 602 % conversion between different representations of values
samer@0 603 % !! FIXME: check memory management of mxArrays here
samer@0 604
samer@0 605
samer@0 606 %% convert_ws( +Type:type, +In:ws_blob, -Out:Type) is det.
samer@0 607 % Convert value of Matlab workspace variable to representation
samer@0 608 % determined by Type.
samer@0 609 convert_ws(ws, Z, ws(Z)) :- !.
samer@0 610 convert_ws(wsseq, Z, wsseq(Z)) :- !.
samer@0 611 convert_ws(mx, Z, mx(Y)) :- !, mlWSGET(Z,Y).
samer@0 612
samer@0 613 % conversions that go direct from workspace variables to matbase.
samer@0 614 convert_ws(tmp, Z, Y) :- !, mlWSNAME(Z,_,I), bt_call(db_tmp(I,ws(Z),Y), db_drop(I,Y)).
samer@0 615 convert_ws(mat, Z, Y) :- !, mlWSNAME(Z,_,I), bt_call(db_save(I,ws(Z),Y), db_drop(I,Y)).
samer@0 616
samer@0 617 % return cell array as list of temporary or permanent mat file locators
samer@0 618 % (this avoids getting whole array from WS to MX).
samer@0 619 convert_ws(cell(tmp,Size), Z, L) :- !,
samer@0 620 mlWSNAME(Z,_,I),
samer@0 621 bt_call(db_tmp_all(I,ws(Z),L,Size), db_drop_all(I,L,Size)).
samer@0 622
samer@0 623 convert_ws(cell(mat,Size), Z, L) :- !,
samer@0 624 mlWSNAME(Z,_,I),
samer@0 625 bt_call(db_save_all(I,ws(Z),L,Size), db_drop_all(I,L,Size)).
samer@0 626
samer@0 627 % Most other conversions from ws(_) go via mx(_)
samer@0 628 convert_ws(T,Z,A) :- mlWSGET(Z,X), convert_mx(T,X,A).
samer@0 629
samer@0 630
samer@0 631 %% convert_mx( +Type:type, +In:mx_blob, -Out:Type) is det.
samer@0 632 % Convert value of in-process Matlab array In to representation
samer@0 633 % determined by Type.
samer@0 634 convert_mx(atom, X, Y) :- !, mlMX2ATOM(X,Y).
samer@0 635 convert_mx(bool, X, Y) :- !, mlMX2LOGICAL(X,Y).
samer@0 636 convert_mx(float, X, Y) :- !, mlMX2FLOAT(X,Y).
samer@0 637 convert_mx(int, X, Y) :- !, mlMX2FLOAT(X,Z), Y is truncate(Z).
samer@0 638 convert_mx(string, X, Y) :- !, mlMX2STRING(X,Y).
samer@0 639 convert_mx(term, X, Y) :- !, mlMX2ATOM(X,Z), term_to_atom(Y,Z).
samer@0 640 convert_mx(loc, X, mat(Y,W)) :- !, mlMX2ATOM(X,Z), term_to_atom(Y|W,Z).
samer@0 641
samer@0 642 convert_mx(mat, X, Y) :- !, % !!! use first engine to save to its matbase
samer@0 643 plml_flag(ml(I),open),
samer@0 644 bt_call( db_save(I,mx(X),Y), db_drop(I,Y)).
samer@0 645 convert_mx(tmp, X, Y) :- !, % !!! use first engine to save to its matbase
samer@0 646 plml_flag(ml(I),open),
samer@0 647 bt_call( db_tmp(I,mx(X),Y), db_drop(I,Y)).
samer@0 648
samer@0 649 convert_mx(list(float), X, Y) :- !, mlGETREALS(X,Y).
samer@0 650
samer@0 651 convert_mx(cell(Type,Size), X, L) :- !,
samer@0 652 mx_size_type(X,Size,cell),
samer@0 653 prodlist(Size,1,Elems), % total number of elements
samer@0 654 mapnats(conv_cref(Type,X),Elems,[],FL),
samer@0 655 reverse(Size,RSize),
samer@0 656 unflatten(RSize,FL,L).
samer@0 657
samer@0 658 convert_mx(array(Type,Size), X, L) :- !,
samer@0 659 mx_size_type(X,Size,MXType),
samer@0 660 compatible(MXType,Type),
samer@0 661 prodlist(Size,1,Elems), % total number of elements
samer@0 662 mapnats(conv_aref(Type,X),Elems,[],FL),
samer@0 663 reverse(Size,RSize),
samer@0 664 unflatten(RSize,FL,L).
samer@0 665
samer@0 666 compatible(double,float).
samer@0 667 compatible(double,int).
samer@0 668 compatible(double,bool).
samer@0 669 compatible(logical,float).
samer@0 670 compatible(logical,int).
samer@0 671 compatible(logical,bool).
samer@0 672
samer@0 673 % !! Need to worry about non gc mx atoms
samer@0 674 conv_aref(bool, X,I,Y) :- !, mlGETLOGICAL(X,I,Y).
samer@0 675 conv_aref(float, X,I,Y) :- !, mlGETFLOAT(X,I,Y).
samer@0 676 conv_aref(int, X,I,Y) :- !, mlGETFLOAT(X,I,W), Y is truncate(W).
samer@0 677
samer@0 678 conv_cref(mx,Z,I,Y) :- !, mlGETCELL(Z,I,Y). % !! non gc mx
samer@0 679 conv_cref(Ty,Z,I,Y) :- !, conv_cref(mx,Z,I,X), convert_mx(Ty,X,Y).
samer@0 680
samer@0 681 %convert(W, field(Z,N,I)) :- convert(mx(X),Z), mlGETFIELD(X,I,N,Y), convert_mx(W,Y).
samer@0 682 %convert(W, field(Z,N)) :- convert(mx(X),Z), mlGETFIELD(X,1,N,Y), convert_mx(W,Y).
samer@0 683
samer@0 684 % Utilities used by convert/2
samer@0 685
samer@0 686 mapnats(P,N,L1,L3) :- succ(M,N), !, call(P,N,PN), mapnats(P,M,[PN|L1],L3).
samer@0 687 mapnats(_,0,L,L) :- !.
samer@0 688
samer@0 689 prodlist([],P,P).
samer@0 690 prodlist([X1|XX],P1,P3) :- P2 is P1*X1, prodlist(XX,P2,P3).
samer@0 691
samer@0 692 concat(0,_,[]) --> !, [].
samer@0 693 concat(N,L,[X1|XX]) --> { succ(M,N), length(X1,L) }, X1, concat(M,L,XX).
samer@0 694
samer@0 695 % convert a flat list into a nested-list array representation
samer@0 696 % using given size specification
samer@0 697 unflatten([N],Y,Y) :- !, length(Y,N).
samer@0 698 unflatten([N|NX],Y,X) :-
samer@0 699 length(Y,M),
samer@0 700 L is M/N, integer(L), L>=1,
samer@0 701 phrase(concat(N,L,Z),Y),
samer@0 702 maplist(unflatten(NX),Z,X).
samer@0 703
samer@0 704 % thin wrappers
samer@0 705 mx_size_type(X,Sz,Type) :- mlMXINFO(X,Sz,Type).
samer@0 706 mx_sub2ind(X,Subs,Ind) :- mlSUB2IND(X,Subs,Ind).
samer@0 707
samer@0 708
samer@0 709 % these create memory managed arrays, which are not suitable
samer@0 710 % for putting into a cell array
samer@0 711
samer@0 712 % roughly, mx_create :: type -> mxarray.
samer@0 713 mx_create([Size],mx(X)) :- mlCREATENUMERIC(Size,Z), mlNEWREFGC(Z,X).
samer@0 714 mx_create({Size},mx(X)) :- mlCREATECELL(Size,Z), mlNEWREFGC(Z,X).
samer@0 715 mx_string(string(Y),mx(X)) :- mlCREATESTRING(Y,Z), mlNEWREFGC(Z,X).
samer@0 716
samer@0 717 % MX as MUTABLE variables
samer@0 718 mx_put(aref(mx(X),I),float(Y)) :- mlPUTFLOAT(X,I,Y).
samer@0 719 mx_put(cref(mx(X),I),mx(Y)) :- mlPUTCELL(X,I,Y). % !! ensure that Y is non gc
samer@0 720 mx_put(mx(X),list(float,Y)) :- mlPUTFLOATS(X,1,Y).
samer@0 721
samer@0 722 %% wsvar(+X:ws_blob(A), -Nm:atom, -Id:ml_eng) is semidet.
samer@0 723 % True if X is a workspace variable in Matlab session Id.
samer@0 724 % Unifies Nm with the name of the Matlab variable.
samer@0 725 wsvar(A,Name,Engine) :- mlWSNAME(A,Name,Engine).
samer@0 726
samer@0 727 /* __________________________________________________________________________________
samer@0 728 * Dealing with the Matbase
samer@0 729 *
samer@0 730 * The Matbase is a file system tree which contains lots of
samer@0 731 * MAT files which have been created by using the dbsave
samer@0 732 * Matlab function.
samer@0 733 */
samer@0 734
samer@0 735
samer@0 736 %% loc(Dir,File)// is det.
samer@0 737 % DCG rule for matbase locator strings. Dir must be an atom slash-separated
samer@0 738 % list of atoms representing a path relative to the matbase root (see Matlab
samer@0 739 % function dbroot). File must be an atom. Outputs a single-quoted locator
samer@0 740 % string acceptable to Matlab db functions.
samer@0 741 loc(X,Y) --> "'", wr(X),"|",atm(Y), "'".
samer@0 742
samer@0 743
samer@0 744 % saving and dropping matbase files
samer@0 745 db_save(I,Z,Y) :- ml_eval(I,dbsave(Z),[loc],[Y]).
samer@0 746 db_tmp(I,Z,Y) :- ml_eval(I,dbtmp(Z),[loc],[Y]).
samer@0 747 db_drop(I,mat(A,B)) :- ml_exec(I,dbdrop(\loc(A,B))).
samer@0 748
samer@0 749 db_save_all(I,Z,L,Size) :- ml_eval(I,cellmap(@dbsave,Z),[cell(loc,Size)],[L]).
samer@0 750 db_tmp_all(I,Z,L,Size) :- ml_eval(I,cellmap(@dbtmp,Z),[cell(loc,Size)],[L]).
samer@0 751 db_drop_all(I,L,Size) :-
samer@0 752 length(Size,Dims),
samer@0 753 ml_exec(I,hide(foreach(@dbdrop,arr(Dims,L,X\\{loc(X)})))).
samer@0 754
samer@0 755
samer@0 756 %% dropmat(+Id:ml_id, +Mat:ml_loc) is det.
samer@0 757 % Deleting MAT file from matbase.
samer@0 758 dropmat(Eng,mat(A,B)) :- db_drop(Eng,mat(A,B)).
samer@0 759
samer@0 760 %% exportmat(+Id:ml_id, +Mat:ml_loc, +Dir:atom) is det.
samer@0 761 % Export specified MAT file from matbase to given directory.
samer@0 762 exportmat(Eng,mat(A,B),Dir) :- ml_exec(Eng,copyfile(dbpath(\loc(A,B)),\q(wr(Dir)))).
samer@0 763
samer@0 764 %% matbase_mat(+Id:ml_eng,-X:ml_loc) is nondet.
samer@0 765 % Listing mat files actually in matbase at given root directory.
samer@0 766 matbase_mat(Id,mat(SubDir/File,x)) :-
samer@0 767 ml_eval(Id,[dbroot,q(/)],[atom],[DBRoot]), % NB with trailing slash
samer@0 768
samer@0 769 atom_concat(DBRoot,'*/d*',DirPattern),
samer@0 770 expand_file_name(DirPattern,Dirs),
samer@0 771 member(FullDir,Dirs),
samer@0 772 atom_concat( DBRoot,SubDirAtom,FullDir),
samer@0 773 term_to_atom(SubDir,SubDirAtom),
samer@0 774 atom_concat(FullDir,'/m*.mat',FilePattern),
samer@0 775 expand_file_name(FilePattern,Files),
samer@0 776 member(FullFile,Files),
samer@0 777 file_base_name(FullFile,FN),
samer@0 778 atom_concat(File,'.mat',FN).
samer@0 779
samer@0 780
samer@0 781 %% persist_item(+X:ml_expr(A),-Y:ml_expr(A)) is det.
samer@0 782 % Convert Matlab expression to persistent form not dependent on
samer@0 783 % current Matlab workspace or MX arrays in Prolog memory space.
samer@0 784 % Large values like arrays and structures are saved in the matbase
samer@0 785 % replaced with matbase locators. Scalar values are converted to
samer@0 786 % literal numeric values. Character strings are converted to Prolog atoms.
samer@0 787 % Cell arrays wrapped in the wsseq/1 functor are converted to literal
samer@0 788 % form.
samer@0 789 %
samer@0 790 % NB. any side effects are undone on backtracking -- in particular, any
samer@0 791 % files created in the matbase are deleted.
samer@0 792 persist_item($T,$T) :- !.
samer@0 793 persist_item(mat(A,B),mat(A,B)) :- !.
samer@0 794
samer@0 795 persist_item(ws(A),B) :- !,
samer@0 796 mlWSNAME(A,_,Eng),
samer@0 797 ml_eval(Eng,typecode(ws(A)),[int,bool,bool],[Numel,IsNum,IsChar]),
samer@0 798 ( Numel=1, IsNum=1
samer@0 799 -> convert_ws(float,A,B)
samer@0 800 ; IsChar=1
samer@0 801 -> convert_ws(atom,A,AA), B= `AA
samer@0 802 ; convert_ws(mat,A,B)
samer@0 803 ).
samer@0 804
samer@0 805
samer@0 806 % !! TODO -
samer@0 807 % deal with collections - we can either save the aggregate
samer@0 808 % OR save the elements individually and get a prolog list of the
samer@0 809 % locators.
samer@0 810 persist_item(wsseq(A),cell(B)) :-
samer@0 811 mlWSNAME(A,_,Eng),
samer@0 812 ml_test(Eng,iscell(ws(A))),
samer@0 813 ml_eval(Eng,wsseq(A),[cell(mat,_)],[B]).
samer@0 814
samer@0 815 persist_item(mx(X),B) :-
samer@0 816 mx_size_type(X,Size,Type),
samer@0 817 ( Size=[1], Type=double
samer@0 818 -> convert_mx(float,X,B)
samer@0 819 ; Type=char
samer@0 820 -> convert_mx(atom,X,AA), B= `AA
samer@0 821 ; convert_mx(mat,X,B)
samer@0 822 ).
samer@0 823
samer@0 824 persist_item(A,A) :- atomic(A).
samer@0 825
samer@0 826
samer@0 827 /* -----------------------------------------------------------------------
samer@0 828 * From here on, we have straight Matlab utilities
samer@0 829 * rather than basic infrastructure.
samer@0 830 */
samer@0 831
samer@0 832
samer@0 833
samer@0 834 % for dealing with option lists
samer@0 835
samer@0 836 %% mhelp(+Name:atom) is det.
samer@0 837 % Lookup Matlab help on the given name. Equivalent to executing help(`X).
samer@0 838 mhelp(X) :- ml_exec(ml,help(q(X))).
samer@0 839
samer@0 840
samer@0 841
samer@0 842 %% compileoptions(+Opts:list(ml_options), -Prefs:ml_expr(options)) is det.
samer@0 843 %
samer@0 844 % Convert list of option specifiers into a Matlab expression representing
samer@0 845 % options (ie a struct). Each specifier can be a Name:Value pair, a name
samer@0 846 % to be looked up in the optionset/2 predicate, a nested list of ml_options
samer@0 847 % compileoptions :: list (optionset | atom:value | struct) -> struct.
samer@0 848 % NB. option types are as follows:
samer@0 849 % ==
samer@0 850 % X :: ml_options :- optionset(X,_).
samer@0 851 % X :: ml_options :- X :: ml_option(_).
samer@0 852 % X :: ml_options :- X :: list(ml_options).
samer@0 853 % X :: ml_options :- X :: ml_expr(struct(_)).
samer@0 854 %
samer@0 855 % ml_option(A) ---> atom:ml_expr(A).
samer@0 856 % ==
samer@0 857 compileoptions(Opts,Prefs) :-
samer@0 858 rec_optslist(Opts,OptsList),
samer@0 859 Prefs=..[prefs|OptsList].
samer@0 860
samer@0 861 rec_optslist([],[]).
samer@0 862 rec_optslist([H|T],L) :-
samer@0 863 ( % mutually exclusive types for H
samer@0 864 optionset(H,Opts1) -> rec_optslist(Opts1,Opts)
samer@0 865 ; H=Name:Value -> Opts=[`Name,Value]
samer@0 866 ; is_list(H) -> rec_optslist(H,Opts)
samer@0 867 ; /* assume struct */ Opts=[H]
samer@0 868 ),
samer@0 869 rec_optslist(T,TT),
samer@0 870 append(Opts,TT,L).
samer@0 871
samer@0 872 rtimes(X,Y,Z) :-
samer@0 873 ( var(X) -> X is Z/Y
samer@0 874 ; var(Y) -> Y is Z/X
samer@0 875 ; Z is X*Y).
samer@0 876
samer@0 877
samer@0 878 % Execute several plots as subplots. The layout can be
samer@0 879 % vertical, horizontal, or explicity given as Rows*Columns.
samer@0 880
samer@0 881
samer@0 882 % mplot is a private procedure used by multiplot
samer@0 883 mplot(subplot(H,W),N,Plot,Ax) :- ?? (subplot(H,W,N); Plot), Ax===gca.
samer@0 884 mplot(figure,N,Plot,Ax) :- ?? (figure(N); Plot), Ax===gca.
samer@0 885
samer@0 886 %% multiplot(+Type:ml_plot, +Cmds:list(ml_expr(_))) is det.
samer@0 887 %% multiplot(+Type:ml_plot, +Cmds:list(ml_expr(_)), -Axes:list(ml_val(handle))) is det.
samer@0 888 %
samer@0 889 % Executes plotting commands in Cmds in multiple figures or axes as determined
samer@0 890 % by Type. Valid types are:
samer@0 891 % * figs(Range)
samer@0 892 % Executes each plot in a separate figure, Range must be P..Q where P
samer@0 893 % and Q are figure numbers.
samer@0 894 % * vertical
samer@0 895 % Executes each plot in a subplot;
samer@0 896 % subplots are arranged vertically top to bottom in the current figure.
samer@0 897 % * horizontal
samer@0 898 % Executes each plot in a subplot;
samer@0 899 % subplots are arranged horizontally left to right in the current figure.
samer@0 900 % * [Type, link(Axis)]
samer@0 901 % As for multplot type Type, but link X or Y axis scales as determined by Axis,
samer@0 902 % which can be `x, `y, or `xy.
samer@0 903 %
samer@0 904 % Three argument form returns a list containing the Matlab handles to axes objects,
samer@0 905 % one for each plot.
samer@0 906 multiplot(Type,Plots) :- multiplot(Type,Plots,_).
samer@0 907
samer@0 908 multiplot([Layout|Opts],Plots,Axes) :- !,
samer@0 909 multiplot(Layout,Plots,Axes),
samer@0 910 member(link(A),Opts) ->
samer@0 911 ?? (linkaxes(Axes,`off); hide(linkaxes(Axes,`A)))
samer@0 912 ; true.
samer@0 913
samer@0 914 multiplot(figs(P..Q),Plots,Axes) :- !,
samer@0 915 length(Plots,N),
samer@0 916 between(1,inf,P), Q is P+N-1,
samer@0 917 numlist(P,Q,PlotNums),
samer@0 918 maplist(mplot(figure),PlotNums,Plots,Axes).
samer@0 919
samer@0 920 multiplot(Layout,Plots,Axes) :-
samer@0 921 length(Plots,N),
samer@0 922 member(Layout:H*W,[vertical:N*1, horizontal:1*N, H*W:H*W]),
samer@0 923 rtimes(H,W,N), % bind any remaining variables
samer@0 924 numlist(1,N,PlotNums),
samer@0 925 maplist(mplot(subplot(H,W)),PlotNums,Plots,Axes).
samer@0 926
samer@0 927
samer@0 928 %% optionset( +Key:term, -Opts:list(ml_options)) is semidet.
samer@0 929 %
samer@0 930 % Extensible predicate for mapping arbitrary terms to a list of options
samer@0 931 % to be processed by compileoptions/2.
samer@0 932
samer@0 933 %user:portray(A|B) :- print(A), write('|'), print(B).
samer@0 934 user:portray(Z) :- mlWSNAME(Z,N,ID), format('<~w:~w>',[ID,N]).
samer@0 935
samer@0 936 prolog:message(ml_illegal_expression(Expr),[ 'Illegal Matlab expression: ~w'-[Expr] | Z], Z).
samer@0 937 prolog:message(mlerror(Eng,Msg,Cmd),[
samer@0 938 'Error in Matlab engine (~w):\n * ~w\n * while executing "~w"'-[Eng,Msg,Cmd] | Z], Z).
samer@0 939
samer@0 940
samer@0 941 %% pl2tex(+Exp:tex_expr)// is det.
samer@0 942 %
samer@0 943 % DCG for texifying expressions (useful for matlab text)
samer@0 944 pl2tex(A=B) --> !, pl2tex(A), "=", pl2tex(B).
samer@0 945 pl2tex(A+B) --> !, pl2tex(A), "+", pl2tex(B).
samer@0 946 pl2tex(A-B) --> !, pl2tex(A), "-", pl2tex(B).
samer@0 947 pl2tex(A*B) --> !, pl2tex(A), "*", pl2tex(B).
samer@0 948 pl2tex(A.*B) --> !, pl2tex(A), "*", pl2tex(B).
samer@0 949 pl2tex(A/B) --> !, pl2tex(A), "/", pl2tex(B).
samer@0 950 pl2tex(A./B) --> !, pl2tex(A), "/", pl2tex(B).
samer@0 951 pl2tex(A\B) --> !, pl2tex(A), "\\", pl2tex(B).
samer@0 952 pl2tex(A.\B) --> !, pl2tex(A), "\\", pl2tex(B).
samer@0 953 pl2tex(A^B) --> !, pl2tex(A), "^", brace(pl2tex(B)).
samer@0 954 pl2tex(A.^B) --> !, pl2tex(A), "^", brace(pl2tex(B)).
samer@0 955 pl2tex((A,B))--> !, pl2tex(A), ", ", pl2tex(B).
samer@0 956 pl2tex(A;B)--> !, pl2tex(A), "; ", pl2tex(B).
samer@0 957 pl2tex(A:B)--> !, pl2tex(A), ": ", pl2tex(B).
samer@0 958 pl2tex({A}) --> !, "\\{", pl2tex(A), "\\}".
samer@0 959 pl2tex([]) --> !, "[]".
samer@0 960 pl2tex([X|XS]) --> !, "[", seqmap_with_sep(", ",pl2tex,[X|XS]), "]".
samer@0 961
samer@0 962 pl2tex(A\\B) --> !, "\\lambda ", pl2tex(A), ".", pl2tex(B).
samer@0 963 pl2tex(@A) --> !, "@", pl2tex(A).
samer@0 964 pl2tex(abs(A)) --> !, "|", pl2tex(A), "|".
samer@0 965 pl2tex(A) --> {atomic(A)}, escape_with(0'\\,0'_,at(A)).
samer@0 966 pl2tex(A) -->
samer@0 967 {compound(A), A=..[H|T] },
samer@0 968 pl2tex(H), paren(seqmap_with_sep(", ",pl2tex,T)).
samer@0 969