Skip to content
Snippets Groups Projects
Commit c29d04c9 authored by Jameson Graef Rollins's avatar Jameson Graef Rollins
Browse files

rework MATLAB interface

Add wrapper Matlab class that provides cleaner more useful interface to the
matlab.engine.  The engine itself is held as a global variable, so that it
doesn't need to be passed around for multiple invocations.
parent 18f35d45
No related branches found
No related tags found
No related merge requests found
...@@ -9,6 +9,8 @@ logging.basicConfig(level=logging.INFO, format='%(message)s') ...@@ -9,6 +9,8 @@ logging.basicConfig(level=logging.INFO, format='%(message)s')
from .ifo import available_ifos, load_ifo from .ifo import available_ifos, load_ifo
from .precomp import precompIFO from .precomp import precompIFO
from .gwinc import gwinc as pygwinc
from . import gwinc_matlab
from . import plot_noise from . import plot_noise
################################################## ##################################################
...@@ -77,11 +79,9 @@ def main(): ...@@ -77,11 +79,9 @@ def main():
freq = np.logspace(np.log10(args.flo), np.log10(args.fhi), args.npoints) freq = np.logspace(np.log10(args.flo), np.log10(args.fhi), args.npoints)
if args.matlab: if args.matlab:
from .gwinc_matlab import gwinc_matlab as gwinc gwinc = gwinc_matlab.gwinc_matlab
# FIXME: why weird import issue requires doing this here instead
# of above?
else: else:
from . import gwinc gwinc = pygwinc
if args.fom: if args.fom:
import inspiral_range import inspiral_range
......
...@@ -3,43 +3,99 @@ import tempfile ...@@ -3,43 +3,99 @@ import tempfile
import scipy.io import scipy.io
import scipy.constants import scipy.constants
import numpy as np import numpy as np
import logging import logging
from .struct import Struct from .struct import Struct
from .plot import plot_noise
##################################################
# NOTE: gwinc needs to be imported before matlab for some very strange
# reason: MATLAB_ENGINE = None
#
# Traceback (most recent call last): class Matlab:
# File "./ifo2txt.py", line 9, in <module> def __init__(self, gwincpath=None):
# import gwinc """Start a MATLAB engine for GWINC processing
# File "/home/jrollins/ligo/src/pygwinc/gwinc/__init__.py", line 9, in <module>
# from . import plot engine is provided, the GWINC path is added
# File "/home/jrollins/ligo/src/pygwinc/gwinc/plot.py", line 2, in <module> to it's path.
# import matplotlib.pyplot as plt
# File "/usr/lib/python2.7/dist-packages/matplotlib/pyplot.py", line 29, in <module> """
# import matplotlib.colorbar global MATLAB_ENGINE
# File "/usr/lib/python2.7/dist-packages/matplotlib/colorbar.py", line 32, in <module>
# import matplotlib.artist as martist if MATLAB_ENGINE:
# File "/usr/lib/python2.7/dist-packages/matplotlib/artist.py", line 15, in <module> return
# from .transforms import (Bbox, IdentityTransform, TransformedBbox,
# File "/usr/lib/python2.7/dist-packages/matplotlib/transforms.py", line 39, in <module> import matlab.engine
# from matplotlib._path import (affine_transform, count_bboxes_overlapping_bbox,
# ImportError: /opt/matlab/R2016b/extern/engines/python/dist/matlab/engine/glnxa64/../../../../../../../sys/os/glnxa64/libstdc++.so.6: version `CXXABI_1.3.9' not found (required by /usr/lib/python2.7/dist-packages/matplotlib/_path.x86_64-linux-gnu.so) if not gwincpath:
# gwincpath = os.path.expanduser(os.getenv('GWINCPATH', 'gwinc'))
# no idea what that's about if not os.path.exists(os.path.join(gwincpath, 'gwinc.m')):
import matlab.engine raise IOError("Invalid MATLAB GWINC path: '{}'".format(gwincpath))
logging.info("starting MATLAB engine...")
def start_matlab(): MATLAB_ENGINE = matlab.engine.start_matlab()
"""Start a MATLAB engine"""
logging.info("starting matlab engine...") logging.info("gwinc path: {}".format(gwincpath))
eng = matlab.engine.start_matlab() MATLAB_ENGINE.addpath(gwincpath)
return eng
@property
def eng(self):
return MATLAB_ENGINE
@property
def workspace(self):
return MATLAB_ENGINE.workspace
def addpath(self, *args):
return MATLAB_ENGINE.addpath(*args)
def eval(self, *args, **kwargs):
return MATLAB_ENGINE.eval(*args, **kwargs)
def load_array(self, var, array):
"""Load numpy array into workspace as vector.
`var` is name of workspace variable, and `array` is numpy
ndarray.
"""
# this stupidity because you can't just give the engine a np.ndarray
MATLAB_ENGINE.workspace[var] = array.tolist()
MATLAB_ENGINE.eval('{0} = cell2mat({0});'.format(var), nargout=0)
def load_struct(self, var, struct):
"""Load pygwinc.Struct array into workspace as vector.
`var` is name of workspace variable, and `struct` is
pygwinc.Struct.
"""
# similar stupidity prevents this (this time recarrays in the dict):
#matlab.workspace['ifo'] = ifo.to_dict(array=True)
with tempfile.NamedTemporaryFile(suffix='.mat') as f:
scipy.io.savemat(f, struct.to_dict(array=True))
MATLAB_ENGINE.eval("{} = load('{}');".format(var, f.name), nargout=0)
def extract(self, *wvars):
"""Extract workspace variables from engine.
Returns dict with wvars as keys.
"""
assert len(wvars) > 0
with tempfile.NamedTemporaryFile(suffix='.mat') as f:
MATLAB_ENGINE.save(f.name, *wvars, nargout=0)
data = scipy.io.loadmat(f, squeeze_me=True, struct_as_record=False)
if len(wvars) == 1:
return data[wvars[0]]
else:
return data
##################################################
def ifo_add_constants(ifo): def ifo_add_constants(ifo):
"""Add "constants" sub-Struct to ifo Struct """Add "constants" sub-Struct to ifo Struct
...@@ -105,46 +161,37 @@ def _rename_noises(d): ...@@ -105,46 +161,37 @@ def _rename_noises(d):
return nd return nd
def gwinc_matlab(f, ifo, fig=False, title=None, gwincpath=None, eng=None, **kwargs): def gwinc_matlab(f, ifo, plot=False):
"""Execute gwinc in MATLAB with the specified ifo model. """Execute gwinc in MATLAB with the specified ifo model.
This uses the python matlab.engine (see start_matlab()) to This uses the python matlab.engine (see Matlab class) to calculate
calculate noises with MATLAB gwinc at the specified path noises with MATLAB gwinc (gwinc directory specified by GWINCPATH
`gwincpath` (defaults to 'gwinc' in the current directory if not environment variable).
specified, or uses the GWINCPATH environment variable).
returns [source, noises, ifo] as returned from matlab.
""" Returns `score` (dict), `noises` (dict), and `ifo` (Struct) as
if not gwincpath: returned from MATLAB.
gwincpath = os.getenv('GWINCPATH', 'gwinc')
if not os.path.exists(os.path.join(gwincpath, 'gwinc.m')):
raise IOError("Invalid matlab gwinc path: '{}'".format(gwincpath))
if not eng: If `plot` is True will cause MATLAB to produce it's own plot for
eng = start_matlab() the noise budget.
logging.info("adding gwinc path: {}".format(gwincpath)) """
eng.addpath(gwincpath) matlab = Matlab()
# add Constants attribute to ifo structure # add Constants attribute to ifo structure
ifo_add_constants(ifo) ifo_add_constants(ifo)
# this stupidity because you can't just give the engine a np.ndarray matlab.load_array('f', f)
eng.workspace['f'] = f.tolist() matlab.load_struct('ifo', ifo)
eng.eval('f = cell2mat(f);', nargout=0)
# similar stupidity prevents this (this time recarrays in the dict): if plot:
#eng.workspace['ifo'] = ifo.to_dict(array=True) plot_flag = '3'
with tempfile.NamedTemporaryFile(suffix='.mat') as f: else:
scipy.io.savemat(f, ifo.to_dict(array=True)) plot_flag = '0'
eng.eval("ifo = load('{}');".format(f.name), nargout=0)
eng.eval('[score, noises, ifo] = gwinc(f, [], ifo, [], 0);', nargout=0) cmd = "[score, noises, ifo] = gwinc(f, [], ifo, SourceModel, {});".format(plot_flag)
matlab.eval(cmd, nargout=0)
with tempfile.NamedTemporaryFile(suffix='.mat') as f: data = matlab.extract('score', 'noises', 'ifo')
eng.save(f.name, 'score', 'noises', 'ifo', nargout=0)
data = scipy.io.loadmat(f, squeeze_me=True, struct_as_record=False)
score = data['score'] score = data['score']
mnoises = Struct.from_matstruct(data['noises']).to_dict() mnoises = Struct.from_matstruct(data['noises']).to_dict()
...@@ -156,5 +203,6 @@ def gwinc_matlab(f, ifo, fig=False, title=None, gwincpath=None, eng=None, **kwar ...@@ -156,5 +203,6 @@ def gwinc_matlab(f, ifo, fig=False, title=None, gwincpath=None, eng=None, **kwar
del mnoises['MirrorThermal'] del mnoises['MirrorThermal']
##### #####
noises = _rename_noises(mnoises) noises = _rename_noises(mnoises)
ifo = Struct.from_matstruct(data['ifo'])
return score, noises, ifo return score, noises, ifo
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment