samer@0: /* samer@0: * Prolog part of Prolog-Matlab interface samer@1: * Version 1 samer@0: * samer@0: * Samer Abdallah (2004-2012) samer@0: * Centre for Digital Music, QMUl. samer@0: */ samer@0: samer@0: :- module(plml, samer@0: [ ml_open/1 % (+Id) samer@0: , ml_open/2 % (+Id, +Host) samer@0: , ml_open/3 % (+Id, +Host, +Options) samer@0: , ml_close/1 % (+Id) samer@0: samer@0: , ml_exec/2 % (+Id, +Expr) samer@0: , ml_eval/4 % (+Id, +Expr, +Types, -Vals) samer@0: , ml_test/2 % (+Id, +Expr) samer@0: samer@0: , (??)/1 % (+Expr) ~execute Matlab expression samer@0: , (???)/1 % (+Expr) ~test Matlab boolean expression samer@0: , (===)/2 % (-Vals,+Expr) ~evaluate Matlab expression samer@0: samer@0: , term_mlstring/3 % (+Id, +Expr, -String) ~Prolog term to Matlab string samer@0: , term_texatom/2 % (+Expr, -Atom) ~Prolog term to TeX expression samer@0: , wsvar/3 % (+WSBlob, -Name, -Id) samer@0: samer@0: % MATBASE samer@0: , persist_item/2 % (+Expr,-Expr) ~ convert volatile subterms to persistent form samer@0: , matbase_mat/2 % (+Dir, -Loc) ~ Find matbase MAT files samer@0: , dropmat/2 % (+Id, +Loc) ~ remove MAT file from matbase samer@0: , exportmat/3 % (+Id, +Loc, +Dir) ~ export MAT file from matbase samer@0: samer@0: samer@0: % Utilities samer@0: , compileoptions/2 samer@0: , multiplot/2 samer@0: , mhelp/1 samer@0: samer@37: , op(650,fy,'`') % quoting things samer@37: , op(160,xf,'``') % postfix transpose operator samer@0: , op(100,fy,@) % function handles samer@0: samer@0: % note slightly reduced precedence of array operators - samer@0: % hope this doesn't break everything... samer@0: , op(210,xfy,.^) % array exponentiation samer@0: , op(410,yfx,.*) % array times samer@0: , op(410,yfx,./) % array division samer@0: , op(410,xfy,.\) % reverse array division samer@0: , op(400,xfy,\) % reverse matrix division samer@0: , op(700,xfx,===) % variable binding/assignment in matlab query samer@0: , op(700,xfx,:==) % variable binding/assignment in matlab query samer@0: , op(951,fx,??) % evaluate term as matlab samer@0: , op(951,fx,???) % evaluate term as matlab boolean samer@0: , op(100,yfx,#) % field indexing (note left-associativity) samer@0: , op(750,fy,\\) % thunk abdstraction samer@0: , op(750,xfy,\\) % lambda abdstraction samer@0: samer@0: % exported after being imported from ops samer@0: , op(1100,xfx,::) % type specification (esp for arrays) samer@37: , op(550,xfx,..) % range of integers samer@0: ]). samer@0: samer@0: samer@0: :- multifile(user:optionset/2). samer@0: :- multifile(user:matlab_path/2). samer@0: :- multifile(user:matlab_init/2). samer@0: :- multifile(user:pl2ml_hook/2). samer@0: samer@0: samer@0: /** Prolog-Matlab interface samer@0: samer@0: ---++++ Types samer@0: samer@0: *|ml_eng|* - Any atom identifying a Matlab engine. samer@0: samer@0: *|ml_stmt|* - A Matlab statement samer@0: == samer@0: X;Y :: ml_stmt :- X:ml_stmt, Y:ml_stmt. samer@0: X,Y :: ml_stmt :- X:ml_stmt, Y:ml_stmt. samer@0: X=Y :: ml_stmt :- X:ml_lval, Y:ml_expr. samer@0: hide(X) :: ml_stmt :- X:ml_stmt. samer@0: == samer@0: samer@0: == samer@0: ml_expr(A) % A Matlab expression, possibly with multiple return values samer@0: ml_loc ---> mat(atom,atom). % Matbase locator samer@0: == samer@0: samer@0: ---++++ Matlab expression syntax samer@0: samer@0: The Matlab expression syntax adopted by this module allows Prolog terms to represent samer@0: or denote Matlab expressions. Let T be the domain of recognised Prolog terms (corresponding to samer@0: the type ml_expr), and M be the domain of Matlab expressions written in Matlab syntax. samer@0: Then V : T->M is the valuation function which maps Prolog term X to Matlab expression V[X]. samer@0: These are some of the constructs it recognises: samer@0: samer@0: Constructs valid only in top level statements, not subexpressions: samer@0: == samer@0: X;Y % |--> V[X]; V[Y] (sequential evaluation hiding first result) samer@0: X,Y % |--> V[X], V[Y] (sequential evaluation displaying first result) samer@0: X=Y % |--> V[X]=V[Y] (assignment, X must denote a valid left-value) samer@0: hide(X) % |--> V[X]; (execute X but hide return value) samer@32: if(X,Y) % |--> if V[X], V[Y], end samer@32: if(X,Y,Z) % |--> if V[X], V[Y], else V[Z], end samer@0: == samer@0: samer@0: Things that look and work like Matlab syntax (more or less): samer@0: == samer@0: +X % |--> uplus(V[X]) samer@0: -X % |--> uminus(V[X]) samer@0: X+Y % |--> plus(V[X],V[Y]) samer@0: X-Y % |--> minus(V[X],V[Y]) samer@0: X^Y % |--> mpower(V[X],V[Y]) samer@0: X*Y % |--> mtimes(V[X],V[Y]) samer@0: X/Y % |--> mrdivide(V[X],V[Y]) samer@0: X\Y % |--> mldivide(V[X],V[Y]) samer@0: X.^Y % |--> power(V[X],V[Y]) samer@0: X.*Y % |--> times(V[X],V[Y]) samer@0: X./Y % |--> rdivide(V[X],V[Y]) samer@0: X.\Y % |--> ldivide(V[X],V[Y]) samer@0: X:Y:Z % |--> colon(V[X],V[Y],V[Z]) samer@0: X:Z % |--> colon(V[X],V[Z]) samer@0: X>Z % |--> gt(V[X],V[Y]) samer@0: X>=Z % |--> ge(V[X],V[Y]) samer@0: X lt(V[X],V[Y]) samer@0: X= le(V[X],V[Y]) samer@0: X==Z % |--> eq(V[X],V[Y]) samer@0: [X1,X2,...] % |--> [ V[X1], V[X2], ... ] samer@0: [X1;X2;...] % |--> [ V[X1]; V[X2]; ... ] samer@0: {X1,X2,...} % |--> { V[X1], V[X2], ... } samer@0: {X1;X2;...} % |--> { V[X1]; V[X2]; ... } samer@0: @X % |--> @V[X] (function handle) samer@0: == samer@0: samer@0: Things that do not look like Matlab syntax but provide standard Matlab features: samer@0: == samer@0: 'Infinity' % |--> inf (positive infinity) samer@0: 'Nan' % |--> nan (not a number) samer@0: X`` % |--> ctranpose(V[X]) (conjugate transpose, V[X]') samer@0: X#Y % |--> getfield(V[X],V[q(Y)]) samer@0: X\\Y % |--> @(V[X])V[Y] (same as lambda(X,Y)) samer@0: \\Y % |--> @()V[Y] (same as thunk(Y)) samer@0: lambda(X,Y) % |--> @(V[X])V[Y] (anonymous function with arguments X) samer@0: thunk(Y) % |--> @()V[Y] (anonymous function with no arguments) samer@0: vector(X) % |--> horzcat(V[X1],V[X2], ...) samer@0: atvector(X) % as vector but assumes elements of X are assumed all atomic samer@0: cell(X) % construct 1xN cell array from elements of X samer@0: `X % same as q(X) samer@0: q(X) % wrap V[X] in single quotes (escaping internal quotes) samer@0: qq(X) % wrap V[X] in double quotes (escaping internal double quotes) samer@0: tq(X) % wrap TeX expression in single quotes (escape internal quotes) samer@0: == samer@0: samer@0: Referencing different value representations. samer@0: == samer@0: mat(X,Y) % denotes a value in the Matbase using a dbload expression samer@0: mx(X:mx_blob) % denotes an MX Matlab array in SWI memory samer@0: ws(X:ws_blob) % denotes a variable in a Matlab workspace samer@0: wsseq(X:ws_blob) % workspace variable containing list as cell array. samer@0: == samer@0: samer@0: Tricky bits. samer@0: == samer@0: apply(X,AX) % X must denote a function or array, applied to list of arguments AX. samer@0: cref(X,Y) % cell dereference, |--> V[X]{ V[Y1], V[Y2], ... } samer@0: arr(Lists) % multidimensional array from nested lists. samer@0: arr(Lists,Dims) % multidimensional array from nested lists. samer@0: == samer@0: samer@0: Things to bypass default formatting samer@0: == samer@0: noeval(_) % triggers a failure when processed samer@0: atom(X) % write atom X as write/1 samer@0: term(X) % write term X as write/1 samer@0: \(P) % escape and call phrase P directly to generate Matlab string samer@0: $(X) % calls pl2ml_hook/2, denotes V[Y] where plml_hook(X,Y). samer@0: '$VAR'(N) % gets formatted as p_N where N is assumed to be atomic. samer@0: == samer@0: samer@0: All other Prolog atoms are written using write/1, while other Prolog terms samer@0: are assumed to be calls to Matlab functions named according to the head functor. samer@0: Thus V[ ( , , ...) ] = (V[, V[], ...). samer@0: samer@0: There are some incompatibilities between Matlab syntax and Prolog syntax, samer@0: that is, syntactic structures that Prolog cannot parse correctly: samer@0: samer@0: * 'Command line' syntax, ie where a function of string arguments: samer@0: "save('x','Y')" can be written as "save x Y" in Matlab, samer@0: but in Prolog, you must use function call syntax with quoted arguments: samer@0: save(`x,`'Y'). samer@0: samer@0: * Matlab's postfix transpose operator "x'" must be written using a different samer@0: posfix operator "x``" or function call syntax "ctranspose(x)". samer@0: samer@0: * Matlab cell referencing using braces, as in x{1,2} must be written samer@0: as "cref(x,1,2)". samer@0: samer@0: * Field referencing using dot (.), eg x.thing - currently resolved samer@0: by using hash (#) operator, eg x#thing. samer@0: samer@0: * Using variables as arrays and indexing them. The problem is that samer@0: Prolog doesn't let you write a term with a variable as the head samer@0: functor. samer@0: samer@0: samer@0: @tbd samer@0: samer@0: Use mat(I) and tmp(I) as types to include engine Id. samer@0: samer@0: Clarify relationship between return values and valid Matlab denotation. samer@0: samer@0: Reshape/2 array representation: reshape([ ... ],Size) samer@0: Expression language: arr(Vals,Shape,InnerFunctor) - allows efficient samer@0: representation of arrays of arbitrary things. Will require more strict samer@0: nested list form. samer@0: samer@0: Deprecate old array(Vals::Type) and cell(Vals::Type) left-value syntax. samer@0: samer@0: Remove I from ml_expr//2 and add to mx type? samer@0: */ samer@0: samer@0: :- use_module(library(apply_macros)). samer@37: :- use_module(library(dcg_core)). samer@37: :- use_module(library(dcg_codes)). samer@37: samer@37: :- set_prolog_flag(back_quotes,symbol_char). samer@37: :- set_prolog_flag(double_quotes,codes). samer@0: samer@0: :- op(700,xfx,===). % variable binding/assignment in matlab query samer@0: :- op(951,fx,??). % evaluate term as matlab samer@0: :- op(951,fx,???). % evaluate term as matlab boolean samer@0: :- op(650,fy,`). % quoting things samer@0: :- op(160,xf,``). % postfix transpose operator samer@0: :- op(100,fy,@). % function handles samer@0: :- op(200,xfy,.^). % array exponentiation samer@0: :- op(410,yfx,.*). % array times samer@0: :- op(410,yfx,./). % array division samer@0: :- op(410,xfy,.\). % array reverse division samer@0: :- op(400,xfy,\). % matrix reverse division samer@0: :- op(100,yfx,#). % field indexing (note left-associativity) samer@0: samer@27: :- dynamic current_engine/1. samer@0: samer@27: :- use_foreign_library(foreign(plml)). samer@27: :- initialization(at_halt(ml_closeall)). samer@19: samer@19: ml_closeall :- samer@27: forall(current_engine(Id), ml_close(Id)). samer@0: samer@0: samer@37: % from utils.pl samer@37: bt_call(Do,Undo) :- Do, (true ; once(Undo), fail). samer@37: user:goal_expansion( bt_call(Do,Undo), (Do, (true; once(Undo), fail))). samer@37: samer@0: %% matlab_init( -Key, -Cmd:ml_expr) is nondet. samer@0: % Each user-defined clause of matlab_init/2 causes Cmd to be executed samer@0: % whenever a new Matlab session is started. samer@0: samer@0: %% matlab_path( -Key, -Path:list(atom)) is nondet. samer@0: % Each user-defined clause of matlab_path/2 causes the directories in Path samer@0: % to be added to the Matlab path of every new Matlab session. Directories samer@24: % are relative to the root directory where padd.m is found. samer@0: samer@0: %% pl2ml_hook(+X:term,-Y:ml_expr) is nondet. samer@0: % Clauses of pl2ml_hook/2 allow for extensions to the Matlab expression samer@0: % language such that =|V[$X] = V[Y]|= if =|pl2ml_hook(X,Y)|=. samer@0: samer@0: samer@0: samer@0: %% ml_open(+Id:ml_eng,+Host:atom,+Options:list(_)) is det. samer@0: %% ml_open(+Id:ml_eng, +Host:atom) is det. samer@0: %% ml_open(+Id:ml_eng) is det. samer@0: % samer@0: % Start a Matlab session on the given host. If Host=localhost samer@0: % or the name of the current current host as returned by hostname/1, samer@0: % then a Matlab process is started directly. Otherwise, it is samer@0: % started remotely via SSH. Options defaults to []. Host defaults to samer@0: % localhost. samer@0: % samer@0: % Start a Matlab session on the specified host using default options. samer@0: % If Host is not given, it defaults to localhost. Session will be samer@0: % associated with the given Id, which should be an atom. See ml_open/3. samer@0: % samer@31: % Valid options are below. Note that matlab is always called with samer@31: % the -nodesktop and -nosplash options. samer@31: % * noinit samer@31: % If present, do not run initialisation commands specified by samer@31: % matlab_path/2 and matlab_init/2 clauses. Otherwise, do run them. samer@0: % * debug(In,Out) samer@0: % if present, Matlab is started in a script which captures standard samer@15: % input and output to files In and Out respectively. (tbd) samer@31: % * cmd(Cmd:atom) samer@31: % Call Cmd as the matlab executable. Default is 'matlab' (i.e. search samer@37: % for matlab on the PATH). Can be used to select a different executable samer@31: % or to add command line options. samer@31: % * awt(Flag:bool) samer@31: % If false (default), call Matlab with -noawt option. Otherwise, Java graphics samer@31: % will be available. samer@0: samer@0: ml_open(Id) :- ml_open(Id,localhost,[]). samer@0: ml_open(Id,Host) :- ml_open(Id,Host,[]). samer@0: ml_open(Id,Host,Options) :- samer@27: ground(Id), samer@0: options_flags(Options,Flags), samer@30: option(cmd(Bin),Options,matlab), samer@35: ( (Host=localhost;hostname(Host)) samer@30: -> format(atom(Exec),'exec ~w',[Bin]) % using exec fixes Ctrl-C bug samer@35: ; format(atom(Exec),'ssh ~w ~w',[Host,Bin]) samer@0: ), samer@0: ( member(debug(In,Out),Options) samer@19: -> debug(plml,'Running Matlab with protocol logging.',[]), samer@19: debug(plml,'| Prolog > Matlab logged to "~w"',[In]), samer@19: debug(plml,'| Prolog < Matlab logged to "~w"',[Out]), samer@19: absolute_file_name(foreign(logio),Spy,[access(read)]), samer@19: format(atom(Exec1),'~w ~w ~w ~w',[Spy,In,Out,Exec]) samer@0: ; Exec1=Exec samer@0: ), samer@18: format(atom(Cmd),'~w ~w',[Exec1,Flags]), samer@20: debug(plml,'About to start Matlab with: ~w',[Cmd]), samer@0: mlOPEN(Cmd,Id), samer@24: addpath(db), samer@27: assert(current_engine(Id)), samer@0: ( member(noinit,Options) -> true samer@0: ; forall( matlab_path(_,Dir), maplist(nofail(addpath),Dir)), samer@0: forall( matlab_init(_,Cmd), nofail(Cmd)) samer@0: ). samer@0: samer@0: addpath(local(D)) :- !, ml_exec(ml,padl(q(D))). samer@0: addpath(D) :- !, ml_exec(ml,padd(q(D))). samer@0: samer@0: %% ml_close(+Id:ml_eng) is det. samer@0: % Close Matlab session associated with Id. samer@27: ml_close(Id) :- ground(Id), mlCLOSE(Id), retract(current_engine(Id)). samer@0: samer@0: nofail(P) :- catch(ignore(call(P)), E, print_message(warning,E)). samer@0: nofail(P,X) :- catch(ignore(call(P,X)), E, print_message(warning,E)). samer@0: samer@31: options_flags(Opts,Flags) :- samer@31: option(awt(AWT),Opts,false), samer@31: ( AWT=true samer@31: -> Flags='-nodesktop -nosplash' samer@31: ; Flags='-nodesktop -nosplash -noawt' samer@31: ). samer@0: samer@0: samer@0: %% ml_exec(+Id:ml_eng, +Expr:ml_expr) is det. samer@0: % samer@0: % Execute Matlab expression without returning any values. samer@0: ml_exec(Id,X) :- samer@0: term_mlstring(Id,X,C), !, samer@20: debug(plml,'plml:ml_exec>> ~s',[C]), samer@0: mlEXEC(Id,C). samer@0: samer@0: %% ml_eval(+Id:ml_eng, +Expr:ml_expr, +Types:list(type), -Res:list(ml_val)) is det. samer@0: % samer@0: % Evaluate Matlab expression binding return values to results list Res. This new samer@0: % form uses an explicit output types list, so Res can be completely unbound on entry samer@0: % even when multiple values are required. samer@0: ml_eval(Id,X,Types,Vals) :- samer@0: maplist(alloc_ws(Id),Types,Vars), samer@13: ml_exec(Id,hide(wsx(Vars)=X)), samer@0: maplist(convert_ws,Types,Vars,Vals). samer@0: samer@0: alloc_ws(I,_,Z) :- mlWSALLOC(I,Z). samer@0: samer@0: %% ml_test(+Id:ml_eng, +X:ml_expr(bool)) is semidet. samer@0: % Succeeds if X evaluates to true in Matlab session Id. samer@0: ml_test(Id,X) :- ml_eval(Id,X,[bool],[1]). samer@0: samer@0: samer@0: samer@0: %% ===(Y:ml_vals(A), X:ml_expr(A)) is det. samer@0: % Evaluate Matlab expression X as in ml_eval/4, binding one or more return values samer@0: % to Y. If Y is unbound or a single ml_val(_), only the first return value is bound. samer@0: % If Y is a list, multiple return values are processed. samer@0: Y === X :- samer@0: ( is_list(Y) samer@0: -> maplist(leftval,Y,TX,VX), ml_eval(ml,X,TX,VX) samer@0: ; leftval(Y,T,V), ml_eval(ml,X,[T],[V]) samer@0: ). samer@0: samer@0: %% leftval( +TVal:tagged(T), -T:type, -Val:T) is det. samer@0: % True if TVal is a tagged value whos type is T and value is Val. samer@0: leftval( ws(X), ws, ws(X)). samer@0: leftval( mx(X), mx, mx(X)). samer@0: leftval( float(X), float, X). samer@0: leftval( int(X), int, X). samer@0: leftval( bool(X), bool, X). samer@0: leftval( atom(X), atom, X). samer@0: leftval( term(X), term, X). samer@0: leftval( string(X), string,X). samer@0: leftval( mat(X), mat, X). samer@0: leftval( tmp(X), tmp, X). samer@0: leftval( loc(X), loc, X). samer@0: leftval( wsseq(X), wsseq, wsseq(X)). samer@0: leftval( list(T,X), list(T), X). samer@0: leftval( array(X::[Size->Type]), array(Type,Size), X) :- !. samer@0: leftval( array(X::[Size]), array(float,Size), X) :- !. samer@0: leftval( cell(X::[Size->Type]), cell(Type,Size), X) :- !. samer@0: leftval( cell(X::[Size]), cell(mx,Size), X) :- !. samer@0: leftval( Val:Type, Type, Val). samer@0: samer@0: samer@0: %% ??(X:ml_expr(_)) is det. samer@0: % Execute Matlab expression X as with ml_exec/2, without returning any values. samer@0: ?? X :- ml_exec(ml,X). samer@0: samer@0: %% ???(X:ml_expr(bool)) is semidet. samer@0: % Evaluate Matlab boolean expression X as with ml_test/2. samer@0: ??? Q :- ml_test(ml,Q). samer@0: samer@0: samer@0: /* samer@0: * DCG for term to matlab conversion samer@0: * the big problem with Matlab syntax is that you cannot always replace samer@0: * a name representing a value with an expression that reduces to that samer@0: * value. Eg samer@0: * X=magic(5), X(3,4) samer@0: * is ok, but samer@0: * (magic(5))(3,4) samer@0: * is not. Similarly x=@sin, x(0.5) but not (@sin)(0.5) samer@0: * This is really infuriating. samer@0: */ samer@0: samer@0: samer@0: % top level statement rules samer@0: stmt(I,hide(A)) --> !, stmt(I,A), ";". samer@0: stmt(I,(A;B)) --> !, stmt(I,A), ";", stmt(I,B). samer@0: stmt(I,(A,B)) --> !, stmt(I,A), ",", stmt(I,B). samer@0: stmt(I,A=B) --> !, ml_expr(I,A), "=", ml_expr(I,B). samer@32: stmt(I,if(A,B)) --> !, "if ",ml_expr(I,A), ", ", stmt(I,B), ", end". samer@32: stmt(I,if(A,B,C)) --> !, "if ",ml_expr(I,A), ", ", stmt(I,B), ", else ", stmt(I,C), ", end". samer@0: stmt(I,Expr) --> !, ml_expr(I,Expr). samer@0: samer@0: samer@0: %% ml_expr(+Id:ml_eng,+X:ml_expr(A))// is nondet. samer@0: % Convert Matlab expression as a Prolog term to string representation. samer@37: ml_expr(_,\X) --> !, phrase(X). samer@0: ml_expr(I,$X) --> !, {pl2ml_hook(X,Y)}, ml_expr(I,Y). samer@0: ml_expr(I,q(X)) --> !, q(stmt(I,X)). samer@0: ml_expr(I,qq(X)) --> !, qq(stmt(I,X)). samer@0: ml_expr(_,tq(X)) --> !, q(pl2tex(X)). samer@0: ml_expr(_,atom(X)) --> !, atm(X). samer@0: ml_expr(_,term(X)) --> !, wr(X). % this could be dangerous samer@0: ml_expr(_,mat(X,Y)) --> !, "dbload(", loc(X,Y), ")". samer@0: ml_expr(_,loc(L)) --> !, { L=mat(X,Y) }, loc(X,Y). samer@0: ml_expr(I,mx(X)) --> !, { mlWSALLOC(I,Z), mlWSPUT(Z,X) }, ml_expr(I,ws(Z)). samer@0: ml_expr(I,ws(A)) --> !, { mlWSNAME(A,N,I) }, atm(N). samer@0: ml_expr(I,wsx([A|B])) --> !, { mlWSNAME(A,N,I) }, "[", atm(N), wsx(B), "]". samer@0: ml_expr(I,wsseq(A)) --> !, { mlWSNAME(A,N,I) }, atm(N). samer@0: ml_expr(_,noeval(_)) --> !, {fail}. % causes evaluation to fail. samer@0: samer@0: ml_expr(_,'Infinity') --> !, "inf". samer@0: ml_expr(_,'Nan') --> !, "nan". samer@0: samer@0: ml_expr(I,A+B) --> !, "plus", args(I,A,B). samer@0: ml_expr(I,A-B) --> !, "minus", args(I,A,B). samer@0: ml_expr(I, -B) --> !, "uminus", args(I,B). samer@0: ml_expr(I, +B) --> !, "uplus", args(I,B). samer@0: ml_expr(I,A^B) --> !, "mpower", args(I,A,B). samer@0: ml_expr(I,A*B) --> !, "mtimes", args(I,A,B). samer@0: ml_expr(I,A/B) --> !, "mrdivide", args(I,A,B). samer@0: ml_expr(I,A\B) --> !, "mldivide", args(I,A,B). samer@0: ml_expr(I,A.^B)--> !, "power", args(I,A,B). samer@0: ml_expr(I,A.*B)--> !, "times", args(I,A,B). samer@0: ml_expr(I,A./B)--> !, "rdivide", args(I,A,B). samer@0: ml_expr(I,A.\B)--> !, "ldivide", args(I,A,B). samer@0: ml_expr(I,A>B) --> !, "gt",args(I,A,B). samer@0: ml_expr(I,A !, "lt",args(I,A,B). samer@0: ml_expr(I,A>=B)--> !, "ge",args(I,A,B). samer@0: ml_expr(I,A= !, "le",args(I,A,B). samer@0: ml_expr(I,A==B)--> !, "eq",args(I,A,B). samer@0: ml_expr(I,A:B) --> !, range(I,A,B). samer@0: samer@0: ml_expr(_,[]) --> !, "[]". samer@0: ml_expr(_,{}) --> !, "{}". samer@0: ml_expr(I,[X]) --> !, "[", matrix(v,I,X), "]". samer@0: ml_expr(I,[X|XX]) --> !, "[", ml_expr(I,X), seqmap(do_then_call(",",ml_expr(I)),XX), "]". samer@0: ml_expr(I,{X}) --> !, "{", matrix(_,I,X), "}". samer@0: samer@0: ml_expr(I, `B) --> !, q(stmt(I,B)). samer@0: ml_expr(I,A#B) --> !, "getfield", args(I,A,q(B)). samer@0: ml_expr(I,B``) --> !, "ctranspose", args(I,B). samer@0: ml_expr(_,@B) --> !, "@", atm(B). samer@0: ml_expr(I, \\B) --> !, "@()", ml_expr(I,B). samer@0: ml_expr(I, A\\B) --> !, { term_variables(A,V), varnames(V) }, samer@33: "@(", varlist(A), ")", ml_expr(I,B). samer@0: ml_expr(I,lambda(A,B)) --> !, ml_expr(I,A\\B). samer@0: ml_expr(I,thunk(B)) --> !, ml_expr(I, \\B). samer@0: samer@0: samer@0: % !! This is problematic: we are using apply to represent both samer@0: % function application and array dereferencing. For function samer@0: % calls, A must be a function name atom or a function handle samer@0: % If A is an array, it cannot be an expression, unless we samer@0: % switch to using the paren Matlab function, which will be slower. samer@0: ml_expr(I,apply(A,B)) --> !, ml_expr(I,A), arglist(I,B). samer@0: ml_expr(I,cref(A,B)) --> !, ml_expr(I,A), "{", clist(I,B), "}". samer@0: samer@0: % array syntax samer@0: ml_expr(I,arr($X)) --> !, { pl2ml_hook(X,L) }, ml_expr(I,arr(L)). samer@0: ml_expr(I,arr(L)) --> !, { array_dims(L,D) }, array(D,I,L). samer@0: ml_expr(I,arr(D,L)) --> !, array(D,I,L). samer@0: ml_expr(I,arr(D,L,P)) --> !, array(D,I,P,L). samer@0: ml_expr(I,atvector(L))--> !, "[", clist_at(I,L), "]". samer@0: ml_expr(I,vector(L)) --> !, "[", clist(I,L), "]". samer@0: ml_expr(I,cell(L)) --> !, "{", clist(I,L), "}". samer@0: ml_expr(_,'$VAR'(N)) --> !, "p_", atm(N). samer@0: samer@0: % catch these and throw exception samer@0: ml_expr(_,hide(A)) --> {throw(ml_illegal_expression(hide(A)))}. samer@0: ml_expr(_,(A;B)) --> {throw(ml_illegal_expression((A;B)))}. samer@0: ml_expr(_,(A,B)) --> {throw(ml_illegal_expression((A,B)))}. samer@0: ml_expr(_,A=B) --> {throw(ml_illegal_expression(A=B))}. samer@0: samer@0: % these are the catch-all clauses which will deal with matlab names, and literals samer@0: % should we filter on the head functor? samer@37: ml_expr(_,A) --> {string(A)}, !, q(str(A)). samer@0: ml_expr(_,A) --> {atomic(A)}, !, atm(A). samer@0: ml_expr(I,F) --> {F=..[H|AX]}, atm(H), arglist(I,AX). samer@0: samer@0: ml_expr_with(I,Lambda,Y) --> {copy_term(Lambda,Y\\PY)}, ml_expr(I,PY). samer@0: samer@0: samer@0: % dimensions implicit in nested list representation samer@0: array_dims([X|_],M) :- !, array_dims(X,N), succ(N,M). samer@0: array_dims(_,0). samer@0: samer@0: % efficiently output row vector of workspace variable names samer@0: wsx([]) --> []. samer@0: wsx([A|AX]) --> { mlWSNAME(A,N,_) }, ",", atm(N), wsx(AX). samer@0: samer@0: %% array(+Dims:natural, +Id:ml_eng, +Array)// is det. samer@0: % samer@0: % Format nested lists as Matlab multidimensional array. samer@0: % Dims is the number of dimensions of the resulting array and samer@0: % should equal the nesting level of Array, ie if Array=[1,2,3], samer@0: % Dims=1; if Array=[[1,2],[3,4]], Dims=2, etc. samer@0: array(0,I,X) --> !, ml_expr(I,X). samer@0: array(1,I,L) --> !, "[", seqmap_with_sep(";",ml_expr(I),L), "]". samer@0: array(2,I,L) --> !, "[", seqmap_with_sep(",",array(1,I),L), "]". samer@0: array(N,I,L) --> {succ(M,N)}, "cat(", atm(N), ",", seqmap_with_sep(",",array(M,I),L), ")". samer@0: samer@0: array(0,I,P,X) --> !, ml_expr_with(I,P,X). samer@0: array(1,I,P,L) --> !, "[", seqmap_with_sep(";",ml_expr_with(I,P),L), "]". samer@0: array(2,I,P,L) --> !, "[", seqmap_with_sep(",",array(1,I,P),L), "]". samer@0: array(N,I,P,L) --> {succ(M,N)}, "cat(", atm(N), ",", seqmap_with_sep(",",array(M,I,P),L), ")". samer@0: samer@0: matrix(h,I,(A,B)) --> !, ml_expr(I,A), ",", matrix(h,I,B). samer@0: matrix(v,I,(A;B)) --> !, ml_expr(I,A), ";", matrix(v,I,B). samer@0: matrix(_,I,A) --> !, ml_expr(I,A). samer@0: samer@0: samer@0: % colon syntax for ranges samer@0: range(I,A,B:C) --> !, "colon", arglist(I,[A,B,C]). samer@0: range(I,A,B) --> !, "colon", args(I,A,B). samer@0: samer@0: samer@33: %% varlist(+Term)// is det. samer@33: % Format comma separated list of lambda expression arguments. samer@33: varlist((A,B)) --> !, atm(A), ",", varlist(B). samer@33: varlist(A) --> !, atm(A). samer@33: samer@33: samer@0: %% clist(+Id:ml_eng, +Items:list(ml_expr))// is det. samer@0: % Format list of Matlab expressions in a comma separated list. samer@0: clist(_,[]) --> []. samer@0: clist(I,[L1|LX]) --> ml_expr(I,L1), seqmap(do_then_call(",",ml_expr(I)),LX). samer@0: samer@0: samer@0: %% clist_at(+Id:ml_eng, +Items:list(ml_expr))// is det. samer@0: % Format list of atoms in a comma separated list. samer@0: clist_at(_,[]) --> []. samer@0: clist_at(_,[L1|LX]) --> atm(L1), seqmap(do_then_call(",",atm),LX). samer@0: samer@0: samer@0: %% arglist(+Id:ml_eng, +Args:list(ml_expr))// is det. samer@0: % DCG rule to format a list of Matlab expressions as function arguments samer@0: % including parentheses. samer@0: arglist(I,X) --> "(", clist(I,X), ")". samer@0: samer@0: samer@0: %% args(+Id:ml_eng, +A1:ml_expr, +A2:ml_expr)// is det. samer@0: %% args(+Id:ml_eng, +A1:ml_expr)// is det. samer@0: % samer@0: % DCG rule to format one or two Matlab expressions as function arguments samer@0: % including parentheses. samer@0: args(I,X,Y) --> "(", ml_expr(I,X), ",", ml_expr(I,Y), ")". samer@0: args(I,X) --> "(", ml_expr(I,X), ")". samer@0: samer@0: samer@0: %% atm(+A:atom)// is det. samer@0: % DCG rule to format an atom using write/1. samer@0: atm(A,C,T) :- with_output_to(codes(C,T),write(A)). samer@0: samer@0: varnames(L) :- varnames(1,L). samer@0: varnames(_,[]). samer@0: varnames(N,[TN|Rest]) :- samer@0: atom_concat(p_,N,TN), succ(N,M), samer@0: varnames(M,Rest). samer@0: samer@0: samer@0: %% term_mlstring(+Id:ml_eng,+X:ml_expr,-Y:list(code)) is det. samer@0: % Convert term representing Matlab expression to a list of character codes. samer@0: term_mlstring(I,Term,String) :- phrase(stmt(I,Term),String), !. samer@0: samer@0: %% term_texatom(+X:tex_expr,-Y:atom) is det. samer@0: % Convert term representing TeX expression to a string in atom form. samer@0: term_texatom(Term,Atom) :- phrase(pl2tex(Term),String), !, atom_codes(Atom,String). samer@0: samer@0: samer@0: samer@0: % Once the computation has been done, the MATLAB workspace contains samer@0: % the results which must be transferred in the appropriate form the samer@0: % specified left-values, in one of several forms, eg mxArray pointer, samer@0: % a float, an atom, a string or a locator. samer@0: % samer@0: % Note that requesting a locator causes a further call samer@0: % to MATLAB to do a dbsave. samer@0: % samer@0: % If no type requestor tag is present, then a unique variable name samer@0: % is generated to store the result in the Matlab workspace. This name samer@0: % is returned in the variable as a ws blob. samer@0: % The idea is to avoid unnecessary traffic over the Matlab engine pipe. samer@0: samer@0: % conversion between different representations of values samer@0: % !! FIXME: check memory management of mxArrays here samer@0: samer@0: samer@0: %% convert_ws( +Type:type, +In:ws_blob, -Out:Type) is det. samer@0: % Convert value of Matlab workspace variable to representation samer@0: % determined by Type. samer@0: convert_ws(ws, Z, ws(Z)) :- !. samer@0: convert_ws(wsseq, Z, wsseq(Z)) :- !. samer@0: convert_ws(mx, Z, mx(Y)) :- !, mlWSGET(Z,Y). samer@0: samer@0: % conversions that go direct from workspace variables to matbase. samer@0: convert_ws(tmp, Z, Y) :- !, mlWSNAME(Z,_,I), bt_call(db_tmp(I,ws(Z),Y), db_drop(I,Y)). samer@0: convert_ws(mat, Z, Y) :- !, mlWSNAME(Z,_,I), bt_call(db_save(I,ws(Z),Y), db_drop(I,Y)). samer@0: samer@0: % return cell array as list of temporary or permanent mat file locators samer@0: % (this avoids getting whole array from WS to MX). samer@0: convert_ws(cell(tmp,Size), Z, L) :- !, samer@0: mlWSNAME(Z,_,I), samer@0: bt_call(db_tmp_all(I,ws(Z),L,Size), db_drop_all(I,L,Size)). samer@0: samer@0: convert_ws(cell(mat,Size), Z, L) :- !, samer@0: mlWSNAME(Z,_,I), samer@0: bt_call(db_save_all(I,ws(Z),L,Size), db_drop_all(I,L,Size)). samer@0: samer@0: % Most other conversions from ws(_) go via mx(_) samer@13: convert_ws(T,Z,A) :- samer@17: mlWSGET(Z,X), samer@17: convert_mx(T,X,A). samer@0: samer@0: samer@0: %% convert_mx( +Type:type, +In:mx_blob, -Out:Type) is det. samer@0: % Convert value of in-process Matlab array In to representation samer@0: % determined by Type. samer@0: convert_mx(atom, X, Y) :- !, mlMX2ATOM(X,Y). samer@0: convert_mx(bool, X, Y) :- !, mlMX2LOGICAL(X,Y). samer@0: convert_mx(float, X, Y) :- !, mlMX2FLOAT(X,Y). samer@0: convert_mx(int, X, Y) :- !, mlMX2FLOAT(X,Z), Y is truncate(Z). samer@0: convert_mx(string, X, Y) :- !, mlMX2STRING(X,Y). samer@0: convert_mx(term, X, Y) :- !, mlMX2ATOM(X,Z), term_to_atom(Y,Z). samer@0: convert_mx(loc, X, mat(Y,W)) :- !, mlMX2ATOM(X,Z), term_to_atom(Y|W,Z). samer@0: samer@0: convert_mx(mat, X, Y) :- !, % !!! use first engine to save to its matbase samer@28: current_engine(I), samer@0: bt_call( db_save(I,mx(X),Y), db_drop(I,Y)). samer@0: convert_mx(tmp, X, Y) :- !, % !!! use first engine to save to its matbase samer@28: current_engine(I), samer@0: bt_call( db_tmp(I,mx(X),Y), db_drop(I,Y)). samer@0: samer@0: convert_mx(list(float), X, Y) :- !, mlGETREALS(X,Y). samer@0: samer@0: convert_mx(cell(Type,Size), X, L) :- !, samer@0: mx_size_type(X,Size,cell), samer@0: prodlist(Size,1,Elems), % total number of elements samer@0: mapnats(conv_cref(Type,X),Elems,[],FL), samer@0: reverse(Size,RSize), samer@0: unflatten(RSize,FL,L). samer@0: samer@0: convert_mx(array(Type,Size), X, L) :- !, samer@0: mx_size_type(X,Size,MXType), samer@0: compatible(MXType,Type), samer@0: prodlist(Size,1,Elems), % total number of elements samer@0: mapnats(conv_aref(Type,X),Elems,[],FL), samer@0: reverse(Size,RSize), samer@0: unflatten(RSize,FL,L). samer@0: samer@0: compatible(double,float). samer@0: compatible(double,int). samer@0: compatible(double,bool). samer@0: compatible(logical,float). samer@0: compatible(logical,int). samer@0: compatible(logical,bool). samer@0: samer@0: % !! Need to worry about non gc mx atoms samer@0: conv_aref(bool, X,I,Y) :- !, mlGETLOGICAL(X,I,Y). samer@0: conv_aref(float, X,I,Y) :- !, mlGETFLOAT(X,I,Y). samer@0: conv_aref(int, X,I,Y) :- !, mlGETFLOAT(X,I,W), Y is truncate(W). samer@0: samer@0: conv_cref(mx,Z,I,Y) :- !, mlGETCELL(Z,I,Y). % !! non gc mx samer@0: conv_cref(Ty,Z,I,Y) :- !, conv_cref(mx,Z,I,X), convert_mx(Ty,X,Y). samer@0: samer@0: %convert(W, field(Z,N,I)) :- convert(mx(X),Z), mlGETFIELD(X,I,N,Y), convert_mx(W,Y). samer@0: %convert(W, field(Z,N)) :- convert(mx(X),Z), mlGETFIELD(X,1,N,Y), convert_mx(W,Y). samer@0: samer@0: % Utilities used by convert/2 samer@0: samer@0: mapnats(P,N,L1,L3) :- succ(M,N), !, call(P,N,PN), mapnats(P,M,[PN|L1],L3). samer@0: mapnats(_,0,L,L) :- !. samer@0: samer@0: prodlist([],P,P). samer@0: prodlist([X1|XX],P1,P3) :- P2 is P1*X1, prodlist(XX,P2,P3). samer@0: samer@0: concat(0,_,[]) --> !, []. samer@0: concat(N,L,[X1|XX]) --> { succ(M,N), length(X1,L) }, X1, concat(M,L,XX). samer@0: samer@0: % convert a flat list into a nested-list array representation samer@0: % using given size specification samer@0: unflatten([N],Y,Y) :- !, length(Y,N). samer@0: unflatten([N|NX],Y,X) :- samer@0: length(Y,M), samer@0: L is M/N, integer(L), L>=1, samer@0: phrase(concat(N,L,Z),Y), samer@0: maplist(unflatten(NX),Z,X). samer@0: samer@0: % thin wrappers samer@0: mx_size_type(X,Sz,Type) :- mlMXINFO(X,Sz,Type). samer@0: mx_sub2ind(X,Subs,Ind) :- mlSUB2IND(X,Subs,Ind). samer@0: samer@0: samer@0: % these create memory managed arrays, which are not suitable samer@0: % for putting into a cell array samer@0: samer@0: % roughly, mx_create :: type -> mxarray. samer@0: mx_create([Size],mx(X)) :- mlCREATENUMERIC(Size,Z), mlNEWREFGC(Z,X). samer@0: mx_create({Size},mx(X)) :- mlCREATECELL(Size,Z), mlNEWREFGC(Z,X). samer@0: mx_string(string(Y),mx(X)) :- mlCREATESTRING(Y,Z), mlNEWREFGC(Z,X). samer@0: samer@0: % MX as MUTABLE variables samer@0: mx_put(aref(mx(X),I),float(Y)) :- mlPUTFLOAT(X,I,Y). samer@0: mx_put(cref(mx(X),I),mx(Y)) :- mlPUTCELL(X,I,Y). % !! ensure that Y is non gc samer@0: mx_put(mx(X),list(float,Y)) :- mlPUTFLOATS(X,1,Y). samer@0: samer@0: %% wsvar(+X:ws_blob(A), -Nm:atom, -Id:ml_eng) is semidet. samer@0: % True if X is a workspace variable in Matlab session Id. samer@0: % Unifies Nm with the name of the Matlab variable. samer@0: wsvar(A,Name,Engine) :- mlWSNAME(A,Name,Engine). samer@0: samer@0: /* __________________________________________________________________________________ samer@0: * Dealing with the Matbase samer@0: * samer@0: * The Matbase is a file system tree which contains lots of samer@0: * MAT files which have been created by using the dbsave samer@0: * Matlab function. samer@0: */ samer@0: samer@0: samer@0: %% loc(Dir,File)// is det. samer@0: % DCG rule for matbase locator strings. Dir must be an atom slash-separated samer@0: % list of atoms representing a path relative to the matbase root (see Matlab samer@0: % function dbroot). File must be an atom. Outputs a single-quoted locator samer@0: % string acceptable to Matlab db functions. samer@0: loc(X,Y) --> "'", wr(X),"|",atm(Y), "'". samer@0: samer@0: samer@0: % saving and dropping matbase files samer@0: db_save(I,Z,Y) :- ml_eval(I,dbsave(Z),[loc],[Y]). samer@0: db_tmp(I,Z,Y) :- ml_eval(I,dbtmp(Z),[loc],[Y]). samer@0: db_drop(I,mat(A,B)) :- ml_exec(I,dbdrop(\loc(A,B))). samer@0: samer@9: db_save_all(I,Z,L,Size) :- ml_eval(I,dbcellmap(@dbsave,Z),[cell(loc,Size)],[L]). samer@9: db_tmp_all(I,Z,L,Size) :- ml_eval(I,dbcellmap(@dbtmp,Z),[cell(loc,Size)],[L]). samer@0: db_drop_all(I,L,Size) :- samer@0: length(Size,Dims), samer@0: ml_exec(I,hide(foreach(@dbdrop,arr(Dims,L,X\\{loc(X)})))). samer@0: samer@0: samer@0: %% dropmat(+Id:ml_id, +Mat:ml_loc) is det. samer@0: % Deleting MAT file from matbase. samer@0: dropmat(Eng,mat(A,B)) :- db_drop(Eng,mat(A,B)). samer@0: samer@0: %% exportmat(+Id:ml_id, +Mat:ml_loc, +Dir:atom) is det. samer@0: % Export specified MAT file from matbase to given directory. samer@0: exportmat(Eng,mat(A,B),Dir) :- ml_exec(Eng,copyfile(dbpath(\loc(A,B)),\q(wr(Dir)))). samer@0: samer@0: %% matbase_mat(+Id:ml_eng,-X:ml_loc) is nondet. samer@0: % Listing mat files actually in matbase at given root directory. samer@0: matbase_mat(Id,mat(SubDir/File,x)) :- samer@0: ml_eval(Id,[dbroot,q(/)],[atom],[DBRoot]), % NB with trailing slash samer@0: samer@0: atom_concat(DBRoot,'*/d*',DirPattern), samer@0: expand_file_name(DirPattern,Dirs), samer@0: member(FullDir,Dirs), samer@0: atom_concat( DBRoot,SubDirAtom,FullDir), samer@0: term_to_atom(SubDir,SubDirAtom), samer@0: atom_concat(FullDir,'/m*.mat',FilePattern), samer@0: expand_file_name(FilePattern,Files), samer@0: member(FullFile,Files), samer@0: file_base_name(FullFile,FN), samer@0: atom_concat(File,'.mat',FN). samer@0: samer@0: samer@0: %% persist_item(+X:ml_expr(A),-Y:ml_expr(A)) is det. samer@0: % Convert Matlab expression to persistent form not dependent on samer@0: % current Matlab workspace or MX arrays in Prolog memory space. samer@0: % Large values like arrays and structures are saved in the matbase samer@0: % replaced with matbase locators. Scalar values are converted to samer@0: % literal numeric values. Character strings are converted to Prolog atoms. samer@0: % Cell arrays wrapped in the wsseq/1 functor are converted to literal samer@0: % form. samer@0: % samer@0: % NB. any side effects are undone on backtracking -- in particular, any samer@0: % files created in the matbase are deleted. samer@0: persist_item($T,$T) :- !. samer@0: persist_item(mat(A,B),mat(A,B)) :- !. samer@0: samer@0: persist_item(ws(A),B) :- !, samer@0: mlWSNAME(A,_,Eng), samer@0: ml_eval(Eng,typecode(ws(A)),[int,bool,bool],[Numel,IsNum,IsChar]), samer@0: ( Numel=1, IsNum=1 samer@0: -> convert_ws(float,A,B) samer@0: ; IsChar=1 samer@0: -> convert_ws(atom,A,AA), B= `AA samer@0: ; convert_ws(mat,A,B) samer@0: ). samer@0: samer@0: samer@0: % !! TODO - samer@0: % deal with collections - we can either save the aggregate samer@0: % OR save the elements individually and get a prolog list of the samer@0: % locators. samer@0: persist_item(wsseq(A),cell(B)) :- samer@0: mlWSNAME(A,_,Eng), samer@0: ml_test(Eng,iscell(ws(A))), samer@0: ml_eval(Eng,wsseq(A),[cell(mat,_)],[B]). samer@0: samer@0: persist_item(mx(X),B) :- samer@0: mx_size_type(X,Size,Type), samer@0: ( Size=[1], Type=double samer@0: -> convert_mx(float,X,B) samer@0: ; Type=char samer@0: -> convert_mx(atom,X,AA), B= `AA samer@0: ; convert_mx(mat,X,B) samer@0: ). samer@0: samer@0: persist_item(A,A) :- atomic(A). samer@0: samer@0: samer@0: /* ----------------------------------------------------------------------- samer@0: * From here on, we have straight Matlab utilities samer@0: * rather than basic infrastructure. samer@0: */ samer@0: samer@0: samer@0: samer@0: % for dealing with option lists samer@0: samer@0: %% mhelp(+Name:atom) is det. samer@0: % Lookup Matlab help on the given name. Equivalent to executing help(`X). samer@0: mhelp(X) :- ml_exec(ml,help(q(X))). samer@0: samer@0: samer@0: samer@0: %% compileoptions(+Opts:list(ml_options), -Prefs:ml_expr(options)) is det. samer@0: % samer@0: % Convert list of option specifiers into a Matlab expression representing samer@0: % options (ie a struct). Each specifier can be a Name:Value pair, a name samer@0: % to be looked up in the optionset/2 predicate, a nested list of ml_options samer@0: % compileoptions :: list (optionset | atom:value | struct) -> struct. samer@0: % NB. option types are as follows: samer@0: % == samer@0: % X :: ml_options :- optionset(X,_). samer@0: % X :: ml_options :- X :: ml_option(_). samer@0: % X :: ml_options :- X :: list(ml_options). samer@0: % X :: ml_options :- X :: ml_expr(struct(_)). samer@0: % samer@0: % ml_option(A) ---> atom:ml_expr(A). samer@0: % == samer@0: compileoptions(Opts,Prefs) :- samer@0: rec_optslist(Opts,OptsList), samer@0: Prefs=..[prefs|OptsList]. samer@0: samer@0: rec_optslist([],[]). samer@0: rec_optslist([H|T],L) :- samer@0: ( % mutually exclusive types for H samer@0: optionset(H,Opts1) -> rec_optslist(Opts1,Opts) samer@0: ; H=Name:Value -> Opts=[`Name,Value] samer@0: ; is_list(H) -> rec_optslist(H,Opts) samer@0: ; /* assume struct */ Opts=[H] samer@0: ), samer@0: rec_optslist(T,TT), samer@0: append(Opts,TT,L). samer@0: samer@0: rtimes(X,Y,Z) :- samer@0: ( var(X) -> X is Z/Y samer@0: ; var(Y) -> Y is Z/X samer@0: ; Z is X*Y). samer@0: samer@0: samer@0: % Execute several plots as subplots. The layout can be samer@0: % vertical, horizontal, or explicity given as Rows*Columns. samer@0: samer@0: samer@0: % mplot is a private procedure used by multiplot samer@0: mplot(subplot(H,W),N,Plot,Ax) :- ?? (subplot(H,W,N); Plot), Ax===gca. samer@0: mplot(figure,N,Plot,Ax) :- ?? (figure(N); Plot), Ax===gca. samer@0: samer@0: %% multiplot(+Type:ml_plot, +Cmds:list(ml_expr(_))) is det. samer@0: %% multiplot(+Type:ml_plot, +Cmds:list(ml_expr(_)), -Axes:list(ml_val(handle))) is det. samer@0: % samer@0: % Executes plotting commands in Cmds in multiple figures or axes as determined samer@0: % by Type. Valid types are: samer@0: % * figs(Range) samer@0: % Executes each plot in a separate figure, Range must be P..Q where P samer@0: % and Q are figure numbers. samer@0: % * vertical samer@0: % Executes each plot in a subplot; samer@0: % subplots are arranged vertically top to bottom in the current figure. samer@0: % * horizontal samer@0: % Executes each plot in a subplot; samer@0: % subplots are arranged horizontally left to right in the current figure. samer@0: % * [Type, link(Axis)] samer@0: % As for multplot type Type, but link X or Y axis scales as determined by Axis, samer@0: % which can be `x, `y, or `xy. samer@0: % samer@0: % Three argument form returns a list containing the Matlab handles to axes objects, samer@0: % one for each plot. samer@0: multiplot(Type,Plots) :- multiplot(Type,Plots,_). samer@0: samer@0: multiplot([Layout|Opts],Plots,Axes) :- !, samer@0: multiplot(Layout,Plots,Axes), samer@0: member(link(A),Opts) -> samer@0: ?? (linkaxes(Axes,`off); hide(linkaxes(Axes,`A))) samer@0: ; true. samer@0: samer@0: multiplot(figs(P..Q),Plots,Axes) :- !, samer@0: length(Plots,N), samer@0: between(1,inf,P), Q is P+N-1, samer@0: numlist(P,Q,PlotNums), samer@0: maplist(mplot(figure),PlotNums,Plots,Axes). samer@0: samer@0: multiplot(Layout,Plots,Axes) :- samer@0: length(Plots,N), samer@0: member(Layout:H*W,[vertical:N*1, horizontal:1*N, H*W:H*W]), samer@0: rtimes(H,W,N), % bind any remaining variables samer@0: numlist(1,N,PlotNums), samer@0: maplist(mplot(subplot(H,W)),PlotNums,Plots,Axes). samer@0: samer@0: samer@0: %% optionset( +Key:term, -Opts:list(ml_options)) is semidet. samer@0: % samer@0: % Extensible predicate for mapping arbitrary terms to a list of options samer@0: % to be processed by compileoptions/2. samer@0: samer@0: %user:portray(A|B) :- print(A), write('|'), print(B). samer@0: user:portray(Z) :- mlWSNAME(Z,N,ID), format('<~w:~w>',[ID,N]). samer@0: samer@0: prolog:message(ml_illegal_expression(Expr),[ 'Illegal Matlab expression: ~w'-[Expr] | Z], Z). samer@0: prolog:message(mlerror(Eng,Msg,Cmd),[ samer@0: 'Error in Matlab engine (~w):\n * ~w\n * while executing "~w"'-[Eng,Msg,Cmd] | Z], Z). samer@0: samer@0: samer@0: %% pl2tex(+Exp:tex_expr)// is det. samer@0: % samer@0: % DCG for texifying expressions (useful for matlab text) samer@0: pl2tex(A=B) --> !, pl2tex(A), "=", pl2tex(B). samer@0: pl2tex(A+B) --> !, pl2tex(A), "+", pl2tex(B). samer@0: pl2tex(A-B) --> !, pl2tex(A), "-", pl2tex(B). samer@0: pl2tex(A*B) --> !, pl2tex(A), "*", pl2tex(B). samer@0: pl2tex(A.*B) --> !, pl2tex(A), "*", pl2tex(B). samer@0: pl2tex(A/B) --> !, pl2tex(A), "/", pl2tex(B). samer@0: pl2tex(A./B) --> !, pl2tex(A), "/", pl2tex(B). samer@0: pl2tex(A\B) --> !, pl2tex(A), "\\", pl2tex(B). samer@0: pl2tex(A.\B) --> !, pl2tex(A), "\\", pl2tex(B). samer@0: pl2tex(A^B) --> !, pl2tex(A), "^", brace(pl2tex(B)). samer@0: pl2tex(A.^B) --> !, pl2tex(A), "^", brace(pl2tex(B)). samer@0: pl2tex((A,B))--> !, pl2tex(A), ", ", pl2tex(B). samer@0: pl2tex(A;B)--> !, pl2tex(A), "; ", pl2tex(B). samer@0: pl2tex(A:B)--> !, pl2tex(A), ": ", pl2tex(B). samer@0: pl2tex({A}) --> !, "\\{", pl2tex(A), "\\}". samer@0: pl2tex([]) --> !, "[]". samer@0: pl2tex([X|XS]) --> !, "[", seqmap_with_sep(", ",pl2tex,[X|XS]), "]". samer@0: samer@0: pl2tex(A\\B) --> !, "\\lambda ", pl2tex(A), ".", pl2tex(B). samer@0: pl2tex(@A) --> !, "@", pl2tex(A). samer@0: pl2tex(abs(A)) --> !, "|", pl2tex(A), "|". samer@0: pl2tex(A) --> {atomic(A)}, escape_with(0'\\,0'_,at(A)). samer@0: pl2tex(A) --> samer@0: {compound(A), A=..[H|T] }, samer@0: pl2tex(H), paren(seqmap_with_sep(", ",pl2tex,T)). samer@0: samer@37: hostname(H) :- samer@37: ( getenv('HOSTNAME',H) -> true samer@37: ; setup_call_cleanup(open(pipe(hostname),read,S), samer@37: read_line_to_codes(S,Codes), samer@37: close(S)), atom_codes(H,Codes) samer@37: ).