Mercurial > pytave
changeset 61:46e6fa692e0c pytave-native
Merge with trunk
author | David Grundberg <individ@acc.umu.se> |
---|---|
date | Mon, 08 Jun 2009 20:31:38 +0200 |
parents | 04e18a0cf25d (current diff) f379cb14c4d4 (diff) |
children | 8135e840ef0e a271989ff0eb |
files | octave_to_python.cc package/pytave.py pytave.cc test/test.py |
diffstat | 6 files changed, 161 insertions(+), 170 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Mon Jun 08 20:02:16 2009 +0200 +++ b/ChangeLog Mon Jun 08 20:31:38 2009 +0200 @@ -1,3 +1,31 @@ +2009-06-03 Jaroslav Hajek <highegg@gmail.com> + + * pytave.cc (delvar): New function. + * package/pytave.py (_VariablesDict.__delitem__): New method. + +2009-06-03 Jaroslav Hajek <highegg@gmail.com> + + * pytave.cc (isvar): Fix tests. + +2009-06-08 Jaroslav Hajek <highegg@gmail.com>, David Grundberg <individ@acc.umu.se> + + * octave_to_python.cc (copy_octarray_to_pyarrobj<PyObject *, + Cell>): New template specialization. + (createPyArr): Simplify row vectors to 1D arrays. + (octvalue_to_pyarrobj): Add string and cell cases. + (octcell_to_pyobject): Create a PyObject array if not row vector. + (octmap_to_pyobject): Create a simple dict if scalar struct. + (octvalue_to_pyobj): Allow converting arbitrary character matrices. + * python_to_octave.cc (pydict_to_octmap): Make scalar structs by + default, detect matching dimensions. + * package/pytave.py (feval): Update doc string. + (getvar, setvar, isvar): Remove. + (_VariablesDict): New class. + (locals, globals): Default instances. + (_local_scope): Rename to _LocalScope + * test/test.py: New tests. Update old tests. + (testsetget, testexception): New functions. + 2009-06-03 Jaroslav Hajek <highegg@gmail.com> * python_to_octave.cc (copy_pyarrobj_to_octarray<PyObject *,
--- a/octave_to_python.cc Mon Jun 08 20:02:16 2009 +0200 +++ b/octave_to_python.cc Mon Jun 08 20:31:38 2009 +0200 @@ -53,8 +53,7 @@ const unsigned int matindex, const unsigned int matstride, const int dimension, - const unsigned int offset, - bool = true) { + const unsigned int offset) { unsigned char *ptr = (unsigned char*) pyarr->data; if (dimension == pyarr->nd - 1) { // Last dimension, base case @@ -82,14 +81,13 @@ const unsigned int matindex, const unsigned int matstride, const int dimension, - const unsigned int offset, - bool native) { + const unsigned int offset) { unsigned char *ptr = (unsigned char*) pyarr->data; if (dimension == pyarr->nd - 1) { // Last dimension, base case for (int i = 0; i < pyarr->dimensions[dimension]; i++) { object pyobj; - octvalue_to_pyobj (pyobj, matrix.elem(matindex + i*matstride), native); + octvalue_to_pyobj (pyobj, matrix.elem(matindex + i*matstride)); Py_INCREF (pyobj.ptr()); *(PyObject **)&ptr[offset + i*pyarr->strides[dimension]] = pyobj.ptr(); @@ -102,30 +100,35 @@ matindex + i*matstride, matstride * pyarr->dimensions[dimension], dimension + 1, - offset + i*pyarr->strides[dimension], - native); + offset + i*pyarr->strides[dimension]); } } } static PyArrayObject *createPyArr(const dim_vector &dims, int pyarrtype) { - int dimensions[dims.length()]; + int len = dims.length(); + int dimensions[len]; for (int i = 0; i < dims.length(); i++) { dimensions[i] = dims(i); } + // Simplify row vectors to 1d arrays. + if (len == 2 && dimensions[0] == 1) { + dimensions[0] = dimensions[1]; + len = 1; + } return (PyArrayObject *)PyArray_FromDims( - dims.length(), dimensions, pyarrtype); + len, dimensions, pyarrtype); } template <class PythonPrimitive, class OctaveBase> static PyArrayObject *create_array(const OctaveBase &octarr, - int pyarraytype, bool native = true) { + int pyarraytype) { PyArrayObject *pyarr = createPyArr(octarr.dims(), pyarraytype); try { copy_octarray_to_pyarrobj - <PythonPrimitive, OctaveBase>(pyarr, octarr, 0, 1, 0, 0, native); + <PythonPrimitive, OctaveBase>(pyarr, octarr, 0, 1, 0, 0); } catch (value_convert_exception &pe) { Py_DECREF(pyarr); throw; @@ -244,13 +247,13 @@ return create_sint_array<int8NDArray, sizeof(int8_t)>( matrix.int8_array_value()); } - if (!native && matrix.is_string()) { + if (matrix.is_string()) { return create_array<char, charNDArray>( - matrix.char_array_value(), PyArray_CHAR, native); + matrix.char_array_value(), PyArray_CHAR); } - if (!native && matrix.is_cell()) { + if (matrix.is_cell()) { return create_array<PyObject *, Cell>( - matrix.cell_value(), PyArray_OBJECT, native); + matrix.cell_value(), PyArray_OBJECT); } throw value_convert_exception("Octave matrix type not known, " @@ -269,24 +272,20 @@ } static void octcell_to_pyobject(boost::python::object &py_object, - const Cell& cell, bool native) { - if (native) { + const Cell& cell) { + if (is_1xn_or_0x0 (cell.dims ())) { py_object = boost::python::list(); - if(!is_1xn_or_0x0(cell.dims())) { - throw value_convert_exception( - "Only one-dimensional (row mayor) cell arrays can be converted."); - } - for(octave_idx_type i = 0 ; i < cell.length(); i++) { boost::python::object py_val; - octvalue_to_pyobj(py_val, cell.elem(i), native); + octvalue_to_pyobj(py_val, cell.elem(i)); ((boost::python::list&)py_object).insert(i, py_val); } - } else - octvalue_to_pyarr(py_object, cell, native); + } else { + octvalue_to_pyarr (py_object, octave_value (cell)); + } } static void octmap_to_pyobject(boost::python::object &py_object, @@ -294,11 +293,18 @@ bool native = true) { py_object = boost::python::dict(); string_vector keys = map.keys(); + bool scalar = map.dims().all_ones(); for(octave_idx_type i = 0 ; i < keys.length(); i++) { boost::python::object py_val; - octcell_to_pyobject(py_val, map.contents(keys[i]), native); + const Cell c = map.contents(keys[i]); + + if (scalar) { + octvalue_to_pyobj(py_val, c(0)); + } else { + octcell_to_pyobject(py_val, c); + } py_object[keys[i]] = py_val; } @@ -322,19 +328,18 @@ else throw value_convert_exception( "Conversion for this scalar not implemented"); - } else if (native && octvalue.is_string()) { - if (! is_1xn_or_0x0 (octvalue.dims ())) - throw value_convert_exception( - "Multi-row character matrices can not be converted."); - py_object = str(octvalue.string_value()); - } else if (!native && octvalue.is_string()) { - octvalue_to_pyarr(py_object, octvalue, native); + } else if (octvalue.is_string()) { + if (is_1xn_or_0x0 (octvalue.dims ())) { + py_object = object(octvalue.string_value()); + } else { + octvalue_to_pyarr(py_object, octvalue); + } } else if (octvalue.is_matrix_type()) { octvalue_to_pyarr(py_object, octvalue); } else if (octvalue.is_map()) { octmap_to_pyobject(py_object, octvalue.map_value(), native); } else if (octvalue.is_cell()) { - octcell_to_pyobject(py_object, octvalue.cell_value(), native); + octcell_to_pyobject(py_object, octvalue.cell_value()); } else throw value_convert_exception( "Conversion from Octave value not implemented");
--- a/package/pytave.py Mon Jun 08 20:02:16 2009 +0200 +++ b/package/pytave.py Mon Jun 08 20:31:38 2009 +0200 @@ -56,9 +56,9 @@ Objects: int (32-bit) int32 float (64-bit) double - str string + str character array dict struct - list cell + list cell array Numeric Array: UBYTE, SBYTE, matrix of correct type @@ -66,11 +66,17 @@ UINT, SINT, -''- LONG, -''- DOUBLE -''- - CHAR character array - OBJECT cell array + CHAR character array + OBJECT cell array All other objects causes a pytave.ObjectConvertError to be raised. This exception inherits TypeError. + + When dicts are converted, all keys must be strings and + constitute valid Octave identifiers. By default, scalar + structures are created. However, when all values evaluate + to cell arrays with matching dimensions, an Octave struct + array is created. Octave to Python ================ @@ -78,25 +84,18 @@ Scalar values to objects: bool bool real scalar float (64-bit) - any string str* (if native = True) struct dict - cell list* (if native = True) - * Cell arrays must be one-dimensional (row vector) and - character matrices must only have one row. Any - other form will raise a ValueConvertError. - This is required unless native = False. - Matrix values to Numeric arrays: - double DOUBLE - single FLOAT - logical DOUBLE + double DOUBLE + single FLOAT + logical DOUBLE int64 LONG int32, uint32 INT, UINT int16, uint16 SHORT, USHORT int8, unint8 SBYTE, UBYTE - char CHAR (if native = False) - cell OBJECT (if native = False) + char CHAR (if native = False) + cell OBJECT (if native = False) All other values causes a pytave.ValueConvertError to be raised. This exception inherits TypeError. @@ -109,8 +108,7 @@ """ - return _pytave.feval(nargout, funcname, arguments, - kwargs.get("native", True)) + return _pytave.feval(nargout, funcname, arguments) def eval(nargout, code, **kwargs): @@ -145,8 +143,7 @@ """ - return _pytave.eval(nargout,code,kwargs.get("silent",True), - kwargs.get("native",True)) + return _pytave.eval(nargout,code,kwargs.get("silent",True)) def addpath(*arguments): """See Octave documentation""" @@ -160,13 +157,8 @@ """See Octave documentation""" return _pytave.feval(1, "path", paths)[0] -#xxx -_pytave.setvar("foo", "value", True) -print _pytave.isvar("foo", True) -print _pytave.getvar("foo", True, True) -#xxx class _VariablesDict(UserDict.DictMixin): - def __init__(self, global_variables, native): + def __init__(self, global_variables, native=False): self.global_variables = global_variables self.native = native @@ -174,7 +166,7 @@ if not isinstance(name, basestring): raise TypeError('Expected a string, not a ' + repr(type(name))) try: - return _pytave.getvar(name, self.global_variables, self.native) + return _pytave.getvar(name, self.global_variables) except VarNameError: raise KeyError('No Octave variable named ' + name) @@ -191,16 +183,17 @@ def __delitem__(self, name): if not isinstance(name, basestring): raise TypeError('Expected a string, not a ' + repr(type(name))) - try: - _pytave.clearvar(name, self.global_variables) - except VarNameError: + # Octave does not gripe when clearing non-existent + # variables. To be consistent with Python dict + # behavior, we shall do so. + if self.__contains__(name): + _pytave.delvar(name, self.global_variables) + else: raise KeyError('No Octave variable named ' + name) -locals = _VariablesDict(global_variables=False, native=False) -globals = _VariablesDict(global_variables=True, native=False) -native_locals = _VariablesDict(global_variables=False, native=True) -native_globals = _VariablesDict(global_variables=True, native=True) +locals = _VariablesDict(global_variables=False) +globals = _VariablesDict(global_variables=True) def push_scope(): """Creates a new anonymous local variable scope on the Octave call
--- a/pytave.cc Mon Jun 08 20:02:16 2009 +0200 +++ b/pytave.cc Mon Jun 08 20:31:38 2009 +0200 @@ -129,8 +129,7 @@ boost::python::tuple func_eval(const int nargout, const string &funcname, - const boost::python::tuple &arguments, - bool native) { + const boost::python::tuple &arguments) { octave_value_list octave_args, retval; @@ -161,7 +160,7 @@ if (nargout >= 0) { boost::python::tuple pytuple; - octlist_to_pytuple(pytuple, retval, native); + octlist_to_pytuple(pytuple, retval); return pytuple; } else { // Return () if nargout < 0. @@ -171,8 +170,7 @@ boost::python::tuple str_eval(int nargout, const string &code, - bool silent, - bool native) { + bool silent) { octave_value_list retval; int parse_status; @@ -207,7 +205,7 @@ if (nargout >= 0) { boost::python::tuple pytuple; - octlist_to_pytuple(pytuple, retval, native); + octlist_to_pytuple(pytuple, retval); return pytuple; } else { // Return () if nargout < 0. @@ -216,7 +214,7 @@ } boost::python::object getvar(const string& name, - bool global, bool native) { + bool global) { octave_value val; if (global) @@ -229,7 +227,7 @@ } boost::python::object pyobject; - octvalue_to_pyobj(pyobject, val, native); + octvalue_to_pyobj(pyobject, val); return pyobject; } @@ -265,21 +263,27 @@ bool clearvar(const string& name, bool global) { bool retval; - if (! isvar (name, global)) - { - throw variable_name_exception (name + " not defined in " + ((global) ? "global" : "local") + " table"); - } - -// if (global) -// symbol_table::clear_global (name); -// else -// symbol_table::clear_variable (name); - symbol_table::clear_global (name); - symbol_table::clear_variable (name); + if (global) + retval = symbol_table::global_varval (name).is_defined (); + else + retval = symbol_table::is_variable (name); return retval; } + void delvar(const string& name, bool global) { + + if (global) { + + // FIXME: workaround a bug in Octave 3.2.0. + if (! symbol_table::is_global (name)) + symbol_table::insert (name).mark_global (); + + symbol_table::clear_global (name); + } else + symbol_table::clear_variable (name); + } + int push_scope() { symbol_table::scope_id local_scope = symbol_table::alloc_scope(); symbol_table::set_scope(local_scope); @@ -307,7 +311,7 @@ def("getvar", pytave::getvar); def("setvar", pytave::setvar); def("isvar", pytave::isvar); - def("clearvar", pytave::clearvar); + def("delvar", pytave::delvar); def("push_scope", pytave::push_scope); def("pop_scope", pytave::pop_scope); def("get_exceptions", pytave::get_exceptions);
--- a/python_to_octave.cc Mon Jun 08 20:02:16 2009 +0200 +++ b/python_to_octave.cc Mon Jun 08 20:31:38 2009 +0200 @@ -310,11 +310,14 @@ dim_vector dims = dim_vector(1, 1); - bool has_dimensions = false; + bool dims_match = true; Array<octave_value> vals (length); Array<std::string> keys (length); + // Extract all keys and convert values. Remember whether dimensions + // match. + for(octave_idx_type i = 0; i < length; i++) { std::string& key = keys(i); @@ -343,23 +346,15 @@ pyobj_to_octvalue(val, tuple[1]); - if(val.is_cell()) { - const Cell c(val.cell_value()); - if (error_state) - throw object_convert_exception("Octave error"); - - // We do not bother measuring 1x1 values, since they are replicated - // to fill up the necessary dimensions. - if(!(c.dims().length() == 2 && c.dims()(0) == 1 && c.dims()(1) == 1)) { - - if(!has_dimensions) { - dims = c.dims(); - has_dimensions = true; - } else if(c.dims() != dims) { - throw object_convert_exception( - "Dimensions of the struct fields do not match"); - } + if(dims_match && val.is_cell()) { + dim_vector dv = val.dims(); + if(i == 0) { + dims = dv; + } else { + dims_match = dims == dv; } + } else { + dims_match = false; } } @@ -370,23 +365,10 @@ std::string& key = keys(i); octave_value val = vals(i); - if(!val.is_cell()) { - map.assign(key, Cell(dims, val)); + if(dims_match) { + map.assign(key, val.cell_value ()); } else { - const Cell c(val.cell_value()); - - if (error_state) - throw object_convert_exception("Octave error"); - - if(c.dims().length() == 2 && c.dims()(0) == 1 && c.dims()(1) == 1) { - map.assign(key, Cell(dims, c(0))); - } - else { - map.assign(key, c); - } - } - if (error_state) { - throw object_convert_exception("Octave error"); + map.assign(key, val); } } oct_value = map;
--- a/test/test.py Mon Jun 08 20:02:16 2009 +0200 +++ b/test/test.py Mon Jun 08 20:31:38 2009 +0200 @@ -12,26 +12,26 @@ arr1_0 = Numeric.zeros((1,0)); number = Numeric.array([1.32], Numeric.Float32) arr1fT = Numeric.array([[1.32], [2], [3], [4]], Numeric.Float32) -arr1fT2 = Numeric.array([[1.32, 2, 3, 4]], Numeric.Float32) -arr1f = Numeric.array([[1.32, 2, 3, 4]], Numeric.Float32) -arr1b = Numeric.array([[8, 2, 3, 256]], Numeric.Int8) -arr1i = Numeric.array([[17, 2, 3, 4]], Numeric.Int) -arr1i32 = Numeric.array([[32, 2, 3, 4]], Numeric.Int32) -arr1a = Numeric.array([[1, 2, 3, 4]]) +arr1fT2 = Numeric.array([1.32, 2, 3, 4], Numeric.Float32) +arr1f = Numeric.array([1.32, 2, 3, 4], Numeric.Float32) +arr1b = Numeric.array([8, 2, 3, 256], Numeric.Int8) +arr1i = Numeric.array([17, 2, 3, 4], Numeric.Int) +arr1i32 = Numeric.array([32, 2, 3, 4], Numeric.Int32) +arr1a = Numeric.array([1, 2, 3, 4]) arr2f = Numeric.array([[1.32, 2, 3, 4],[5,6,7,8]], Numeric.Float32) arr2d = Numeric.array([[1.17, 2, 3, 4],[5,6,7,8]], Numeric.Float) arr3f = Numeric.array([[[1.32, 2, 3, 4],[5,6,7,8]],[[9, 10, 11, 12],[13,14,15,16]]], Numeric.Float32) -arr1c = Numeric.array([[1+2j, 3+4j, 5+6j, 7+0.5j]], Numeric.Complex) -arr1fc = Numeric.array([[1+2j, 3+4j, 5+6j, 7+0.5j]], Numeric.Complex32) -arr1o = Numeric.array([["abc",1.0,2+3j]],Numeric.PyObject) -arr2o = Numeric.array([["abc",1.0,2+3j],[4.0,arr1i,"def"]],Numeric.PyObject) -arr1ch = Numeric.array(["abc"],Numeric.Character) +arr1c = Numeric.array([1+2j, 3+4j, 5+6j, 7+0.5j], Numeric.Complex) +arr1fc = Numeric.array([1+2j, 3+4j, 5+6j, 7+0.5j], Numeric.Complex32) +arr1ch = Numeric.array("abc",Numeric.Character) arr2ch = Numeric.array(["abc","def"],Numeric.Character) +arr1o = Numeric.array([1.0,"abc",2+3j],Numeric.PyObject) +arr2o = Numeric.array([[1.0,"abc",2+3j],[4.0,arr1i,"def"]],Numeric.PyObject) -alimit_int32 = Numeric.array([[-2147483648, 2147483647]], Numeric.Int32); -alimit_int16 = Numeric.array([[-32768, 32767, -32769, 32768]], Numeric.Int16); -alimit_int8 = Numeric.array([[-128, 127, -129, 128]], Numeric.Int8); -alimit_uint8 = Numeric.array([[0, 255, -1, 256]], Numeric.UnsignedInt8); +alimit_int32 = Numeric.array([-2147483648, 2147483647], Numeric.Int32); +alimit_int16 = Numeric.array([-32768, 32767, -32769, 32768], Numeric.Int16); +alimit_int8 = Numeric.array([-128, 127, -129, 128], Numeric.Int8); +alimit_uint8 = Numeric.array([0, 255, -1, 256], Numeric.UnsignedInt8); # This eval call is not to be seen as a encouragement to use Pytave @@ -72,7 +72,7 @@ fail("as %s != %s" % (value, nvalue)) if value.shape != nvalue.shape: fail("Size check failed for: %s. Expected shape %s, got %s with shape %s" \ - %(value, value.shape, nvalue.shape, nvalue)) + %(value, value.shape, nvalue, nvalue.shape)) if class1 != class2: fail( "Type check failed for: %s. Expected %s. Got %s." %(value, class1, class2)) @@ -163,21 +163,14 @@ except Exception, e: fail("testlocalscope: %s" % (x,), e) - -testequal('a') - - testmatrix(alimit_int32) testmatrix(alimit_int16) testmatrix(alimit_int8) # Strings # Multi-row character matrix cannot be returned -testvalueerror("eval", "['foo'; 'bar']") -testequal('a') testequal("mystring") -testequal('mystring') testequal("mystringåäöÅÄÖ") testequal(1) @@ -192,12 +185,12 @@ testmatrix(arr1fT2) testmatrix(arr1i) testmatrix(arr1b) -testmatrix(arr1c) testmatrix(arr1fc) # 2d arrays testmatrix(arr2f) testmatrix(arr2d) +testmatrix(arr2ch) # 3d arrays testmatrix(arr3f) @@ -207,7 +200,6 @@ print "FAIL: Zero test", testmatrix(arr0_0) -testmatrix(arr1_0) testmatrix(arr0_1) # Lists @@ -220,38 +212,24 @@ testvalueok("cell", 1, 3); testvalueok("cell", 1, 0) testvalueok("cell", 0, 0) - -# Return cells with incompatible dimensions -testvalueerror("cell", 3, 1) -testvalueerror("cell", 0, 1) +testvalueok("cell", 3, 1) +testvalueok("cell", 0, 1) # Dictionaries # Simple dictionary tests -testequal({"foo": [1], "bar": [2]}) +testequal({"foo": 1, "bar": 2}) testequal({"x": [1, 3], "y": [2, 4]}) testequal({"x": [1, "baz"], "y": [2, "foobar"]}) testequal({"x": [arr1f], "y": [arr1i]}) testequal({}) +testequal({"foo": arr1f, "bar": arr2f}) +testequal({"foo": 1, "bar": [2]}) +testexpect({"foo": [[1,2]], "bar": [[3,2]]}, + {"foo": [1,2], "bar": [3,2]}) # Try some odd dictionaries # The implicit conversion makes Pytave return cell-wrapped results. -testexpect({"foo": number, "bar": 2}, - {"foo": [number], "bar": [2]}) -testexpect({"foo": arr1f, "bar": arr2f}, - {"foo": [arr1f], "bar": [arr2f]}) -testexpect({"foo": 1, "bar": 2}, - {"foo": [1], "bar": [2]}) -testexpect({"foo": 1, "bar": [2]}, - {"foo": [1], "bar": [2]}) -testexpect({"foo": 1, "bar": [2, 3]}, - {"foo": [1, 1], "bar": [2, 3]}) -testexpect({"foo": [1], "bar": [2, 4]}, - {"foo": [1, 1], "bar": [2, 4]}) -testexpect({"bar": 1, "foo": [2, 3]}, - {"bar": [1, 1], "foo": [2, 3]}) -testexpect({"bar": [1], "foo": [2, 4]}, - {"bar": [1, 1], "foo": [2, 4]}) # Try some invalid keys testobjecterror({"this is not an Octave identifier": 1}) @@ -263,8 +241,8 @@ testobjecterror(()) result, = pytave.feval(1, "eval", "[1, 1, 1]") -if result.shape != (1, 3): - print "FAIL: expected 1x3 matrix" +if result.shape != (3,): + print "FAIL: expected length-3 vector" result, = pytave.feval(1, "eval", "[1; 2; 3]"); if result.shape != (3, 1): @@ -273,7 +251,8 @@ testparseerror(1, "endfunction") testevalexpect(1, "2 + 2", (4,)) testevalexpect(1, "{2}", ([2],)) -testevalexpect(2, "struct('foo', 2)", ({'foo': [2]},)) +testevalexpect(1, "struct('foo', 'bar')", ({'foo': Numeric.array(['bar'], Numeric.PyObject)},)) +testevalexpect(1, "struct('foo', {'bar', 'baz'})", ({'foo': Numeric.array(['bar', 'baz'], Numeric.PyObject)},)) testsetget(pytave.locals, "xxx", [1,2,3]) testsetget(pytave.globals, "xxx", [1,2,3])