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