Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pymatbridge/examples/example_func.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function lol = bar_func(name)
lol=['hello from ' name];
end
3 changes: 3 additions & 0 deletions pymatbridge/examples/example_script.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
q=1:10;
f=45;

103 changes: 103 additions & 0 deletions pymatbridge/examples/outvariables.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import time
import numpy as np
import numpy.testing as npt
import os.path
import pymatbridge

_dir = os.path.dirname(os.path.abspath(__file__))

mlab = pymatbridge.Matlab(matlab='/opt/matlab/R2013a/bin/matlab', log=True, capture_stdout=True)
mlab.start()

if 1:
with mlab.fig('/tmp/bob/test.png') as f:
mlab.plot(range(1000))
mlab.suptitle('test')
mlab.ylabel('y label')

if 1:
px = mlab.set_variable('v1',5)
assert repr(px) == '<ProxyVariable __VAR=v1|double([1 1])>'
assert str(px) == 'v1'
assert px() == 5

if 1:
#conventional behaviour, perform the matlab command and return the result
z = mlab.zeros(5)
npt.assert_equal(z, np.zeros((5,5)))

#perform the same command, and save the 1 output variable on the matlab side
#with the name 'z'. return a placeholder containing some metadata about it
_z = mlab.zeros(5,nout=1,saveout=('z',))
assert repr(_z) == '<ProxyVariable __VAR=z|double([5 5])>'

#now return the real variable, not the proxy
z = _z()
npt.assert_equal(z, np.zeros((5,5)))

#now return the result
z = mlab.get_variable('z')
npt.assert_equal(z, np.zeros((5,5)))

if 1:
#this time the matlab command returns two variables
x,y = mlab.meshgrid(range(1,4),range(10,15),nout=2)
npx,npy = np.meshgrid(range(1,4),range(10,15))
npt.assert_equal(x,npx); npt.assert_equal(y,npy)

#perform the same command, but leave the result in matlab
_x,_y = mlab.meshgrid(range(1,4),range(10,15),nout=2,saveout=('X','Y'))
assert repr(_x) == '<ProxyVariable __VAR=X|double([5 3])>'
assert repr(_y) == '<ProxyVariable __VAR=Y|double([5 3])>'

#now return the real variable, not the proxy
x = _x()
npt.assert_equal(x,npx)

#now return the result
x = mlab.get_variable('X')
npt.assert_equal(x,npx)

#or pass the proxy to get_variable to do the same
x = mlab.get_variable(_x)
npt.assert_equal(x,npx)

#now pass the proxy to a matlab command and check it decodes it
z = mlab.cat(3,_x,_y)
npz = np.dstack((npx,npy))
npt.assert_equal(z,npz)

#annother approach, mixing variables on the matlab side
#(calling str(proxy) will return the name)
mlab.run_code('zb=cat(3,%s,%s);' % (_x, _y))
zb = mlab.get_variable('zb')
npt.assert_equal(z,zb)

if 1:
mlab.run_func(os.path.join(_dir,'example_func.m'), 'john', nout=1, saveout=('lol',))
assert 'hello from john' == mlab.get_variable('lol')

if 1:
mlab.run_script(os.path.join(_dir,'example_script.m'))
q = mlab.get_variable('q')
npt.assert_equal(q,range(1,11))
f = mlab.get_variable('f')
npt.assert_equal(f,45)

if 1:
mlab.run_code('foo=1:100;')
m = mlab.get_variable('foo')
npt.assert_equal(m,range(1,101))

mlab.run_code('foo=1:100;')

if 1:
a,b = mlab.run_code("""
function [a, b] = cheese(c, d)
a = 2;
b = c + d;
end""", 3, 9, nout=2)
print a
print b

mlab.stop()
165 changes: 137 additions & 28 deletions pymatbridge/matlab/matlabserver.m
Original file line number Diff line number Diff line change
@@ -1,41 +1,150 @@
function matlabserver(socket_address)
% This function takes a socket address as input and initiates a ZMQ session
% over the socket. I then enters the listen-respond mode until it gets an
% "exit" command
% MATLABSERVER Run a Matlab server to handle requests from Python over ZMQ
%
% MATLABSERVER(SOCKET_ADDRESS) initiates a ZMQ session over the provided
% SOCKET_ADDRESS. Once started, it executes client requests and returns
% the response.
%
% The recognized requests are:
% 'ping': Elicit a response from the server
% 'exit': Request the server to shutdown
% 'call': Call a Matlab function with the provdided arguments

json.startup
messenger('init', socket_address);
json.startup
messenger('init', socket_address);

while(1)
msg_in = messenger('listen');
req = json.load(msg_in);
state.tmp_mat_dir = tempname;
mkdir(state.tmp_mat_dir);

switch(req.cmd)
case {'connect'}
messenger('respond', 'connected');
while true
% don't let any errors escape (and crash the server)
try
msg_in = messenger('listen');
req = json.load(msg_in);

case {'exit'}
messenger('exit');
clear mex;
break;
switch(req.cmd)
case {'ping'}
messenger('respond', 'pong');

case {'run_function'}
fhandle = str2func('pymat_feval');
resp = feval(fhandle, req);
messenger('respond', resp);
case {'exit'}
messenger('exit');
clear mex;
break;

case {'run_code'}
fhandle = str2func('pymat_eval');
resp = feval(fhandle, req);
messenger('respond', resp);
case {'call'}
resp = call(req, state);
resp.state = state;
json_resp = json.dump(resp);
messenger('respond', json_resp);

case {'get_var'}
fhandle = str2func('pymat_get_variable');
resp = feval(fhandle, req);
messenger('respond', resp);
otherwise
throw(MException('MATLAB:matlabserver', ['Unrecognized command ' req.cmd]))
end

catch exception
% format the exception and pass it back to the client
resp.success = false;
resp.result = exception.identifier;
resp.message = getReport(exception)

json_resp = json.dump(resp);
messenger('respond', json_resp);
end
end

rmdir(state.tmp_mat_dir, 's');

end


function resp = call(req, state)
% CALL Call a Matlab function
%
% RESPONSE = CALL(REQUEST) calls Matlab's FEVAL function, intelligently
% handling the number of input and output arguments so that the argument
% spec is satisfied.
%
% The REQUEST is a struct with three fields:
% 'func': The name of the function to execute
% 'args': A cell array of args to expand into the function arguments
% 'nout': The number of output arguments requested

args = {};
if req.nin > 0
for i=1:req.nin
fn = num2str(i-1);
if isfield(req.args,['a' fn])
args{i} = req.args.(['a' num2str(i-1)]);
elseif isfield(req.args,['b' fn])
data = load(req.args.(['b' fn]), 'v');
args{i} = data.v;
elseif isfield(req.args,['p' fn])
args{i} = evalin('base',req.args.(['p' fn]));
else
throw(MException('MATLAB:matlabserver', ['Unrecognized field ' 'b' fn]));
end
end
end

% determine the number of output arguments
% TODO: What should the default behaviour be?
func = str2func(req.func);
nout = req.nout;
[saveout nsaveout] = regexp(req.saveout, '(?:;)+', 'split', 'match');
if isempty(nout)
try
nout = min(abs(nargout(func)), 1);
catch
nout = 1;
end
end

% call the function, taking care of broadcasting outputs
switch nout
case 0
func(args{:});
case 1
resp.result = func(args{:});
otherwise
messenger('respond', 'i dont know what you want');
[resp.result{1:nout}] = func(args{:});
if ~length(nsaveout)
%because of ambiguity of json encoding arrays of matrices
%convert multiple output arguments into a structure with
%fields name a0..aN. Convert these back to a list of matrices
%at the python end
result_struct = struct('nout',nout);
for i=1:nout
v = resp.result{i};
if isa(v,'numeric') & (numel(v) > 100)
key = ['b' num2str(i-1)];
val = fullfile(state.tmp_mat_dir, [key '.mat']);
save(val,'v','-v6');
clear v;
else
key = ['a' num2str(i-1)];
val = v;
end
result_struct.(key) = val;
end
resp.result = result_struct;
end
end

if length(nsaveout)
if nout == 1
assignin('base',saveout{1},resp.result);
resp.result = ['__VAR=' saveout{1} '|' class(resp.result) '(' mat2str(size(resp.result)) ')'];
elseif nout > 1
tmp_result = '';
for i=1:nout
assignin('base',saveout{i},resp.result{i});
tmp_result = ['__VAR=' saveout{i} '|' class(resp.result{i}) '(' mat2str(size(resp.result{i})) ');' tmp_result];
end
resp.result = tmp_result;
end
end

% build the response
resp.success = true;
resp.message = 'Successfully completed request';
end
76 changes: 0 additions & 76 deletions pymatbridge/matlab/util/pymat_eval.m

This file was deleted.

Loading