diff --git a/inst/@sym/find.m b/inst/@sym/find.m index fde8a9fa..af7b5885 100644 --- a/inst/@sym/find.m +++ b/inst/@sym/find.m @@ -132,8 +132,8 @@ ' return r' '#' 'x, = _ins' - 'if x is not None and x.is_Matrix:' - ' x = [a for a in x.T]' % note transpose + 'if isinstance(x, (MatrixBase, NDimArray)):' + ' x = [a for a in flatten(transpose(x).tolist(), levels=1)]' % note transpose 'else:' ' x = [x,]' 'return [scalar2tf(a) for a in x],' }; diff --git a/inst/@sym/horzcat.m b/inst/@sym/horzcat.m index a31e2bb4..295ad751 100644 --- a/inst/@sym/horzcat.m +++ b/inst/@sym/horzcat.m @@ -1,5 +1,6 @@ %% SPDX-License-Identifier: GPL-3.0-or-later -%% Copyright (C) 2014-2017, 2019, 2024 Colin B. Macdonald +%% Copyright (C) 2014-2017, 2019, 2022, 2024 Colin B. Macdonald +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -45,25 +46,8 @@ function h = horzcat(varargin) - % special case for 0x0 but other empties should be checked for - % compatibility - cmd = { - '_proc = []' - 'for i in _ins:' - ' if i is None or not i.is_Matrix:' - ' _proc.append(sp.Matrix([[i]]))' - ' else:' - ' if i.shape == (0, 0):' - ' pass' - ' else:' - ' _proc.append(i)' - 'return sp.MatrixBase.hstack(*_proc),' - }; - - for i = 1:nargin - varargin{i} = sym(varargin{i}); - end - h = pycall_sympy__ (cmd, varargin{:}); + args = cellfun (@transpose, varargin, 'UniformOutput', false); + h = vertcat (args{:}).'; end @@ -133,8 +117,37 @@ %! q = sym(ones(3, 0)); %! w = horzcat(v, q); +%!error +%! z30 = sym (zeros (3, 0)); +%! z40 = sym (zeros (4, 0)); +%! % Note Issue #1238: unclear error message: +%! % [z30 z40]; +%! % but this gives the ShapeError +%! horzcat(z30, z40); + +%!test +%! % special case for the 0x0 empty: no error +%! z00 = sym (zeros (0, 0)); +%! z30 = sym (zeros (3, 0)); +%! [z00 z30]; + %!test %! % issue #700 %! A = sym ([1 2]); %! B = simplify (A); %! assert (isequal ([B A], [A B])) + +%!test +%! % issue #1236, correct empty sizes +%! syms x +%! z00 = sym (zeros (0, 0)); +%! z30 = sym (zeros (3, 0)); +%! z03 = sym (zeros (0, 3)); +%! z04 = sym (zeros (0, 4)); +%! assert (size ([z00 z00]), [0 0]) +%! assert (size ([z00 z03]), [0 3]) +%! assert (size ([z03 z03]), [0 6]) +%! assert (size ([z03 z04]), [0 7]) +%! assert (size ([z03 z00 z04]), [0 7]) +%! assert (size ([z30 z30]), [3 0]) +%! assert (size ([z30 z30 z30]), [3 0]) diff --git a/inst/@sym/isAlways.m b/inst/@sym/isAlways.m index 08989b5e..94cb79a0 100644 --- a/inst/@sym/isAlways.m +++ b/inst/@sym/isAlways.m @@ -128,8 +128,8 @@ cmd = vertcat(cmd, { '(x, map_unknown_to) = _ins' - 'if x is not None and x.is_Matrix:' - ' r = [a for a in x.T]' % note transpose + 'if isinstance(x, (MatrixBase, NDimArray)):' + ' r = [a for a in flatten(transpose(x).tolist(), levels=1)]' % note tranpose 'else:' ' r = [x,]' 'r = [simplify_true_false_none(a) for a in r]' diff --git a/inst/@sym/isconstant.m b/inst/@sym/isconstant.m index fc580a4a..ccd900f4 100644 --- a/inst/@sym/isconstant.m +++ b/inst/@sym/isconstant.m @@ -47,7 +47,7 @@ function z = isconstant(x) cmd = { '(x,) = _ins' - 'if x is not None and x.is_Matrix:' + 'if isinstance(x, (MatrixBase, NDimArray)):' ' return x.applyfunc(lambda a: a.is_constant()),' 'return x.is_constant(),' }; z = pycall_sympy__ (cmd, sym(x)); @@ -68,3 +68,14 @@ %! A = [x 2; 3 x]; %! B = [false true; true false]; %! assert (isequal (isconstant (A), B)) + +%!error +%! % semantically not an error, but is using SymPy's Array +%! t = sym(true); +%! A = [t t]; +%! isconstant (A); + +%!error +%! syms x +%! A = [x == 10; x <= 10]; +%! isconstant (A); diff --git a/inst/@sym/logical.m b/inst/@sym/logical.m index d664defd..1fa56eab 100644 --- a/inst/@sym/logical.m +++ b/inst/@sym/logical.m @@ -1,5 +1,5 @@ %% SPDX-License-Identifier: GPL-3.0-or-later -%% Copyright (C) 2014-2016, 2019, 2024 Colin B. Macdonald +%% Copyright (C) 2014-2016, 2019, 2022, 2024 Colin B. Macdonald %% %% This file is part of OctSymPy. %% @@ -102,8 +102,8 @@ cmd = vertcat(cmd, { '(x, unknown) = _ins' - 'if x is not None and x.is_Matrix:' - ' r = [a for a in x.T]' % note transpose + 'if isinstance(x, (MatrixBase, NDimArray)):' + ' r = [a for a in flatten(transpose(x).tolist(), levels=1)]' % note tranpose 'else:' ' r = [x,]' 'r = [scalar2tfn(a) for a in r]' @@ -180,7 +180,7 @@ %! w = logical(e); %! assert (islogical (w)) %! assert (isequal (w, [true false true])) -%! e = e'; +%! e = e.'; %! w = logical(e); %! assert (islogical (w)) %! assert (isequal (w, [true; false; true])) diff --git a/inst/@sym/private/elementwise_op.m b/inst/@sym/private/elementwise_op.m index fe68ca95..74c99844 100644 --- a/inst/@sym/private/elementwise_op.m +++ b/inst/@sym/private/elementwise_op.m @@ -1,4 +1,4 @@ -%% Copyright (C) 2014, 2016, 2018-2019, 2022 Colin B. Macdonald +%% Copyright (C) 2014, 2016, 2018-2019, 2021-2022 Colin B. Macdonald %% Copyright (C) 2016 Lagu %% %% This file is part of OctSymPy. @@ -84,7 +84,7 @@ % Make sure all matrices in the input are the same size, and set q to one of them 'q = None' 'for A in _ins:' - ' if isinstance(A, MatrixBase):' + ' if isinstance(A, (MatrixBase, NDimArray)):' ' if q is None:' ' q = A' ' else:' @@ -94,10 +94,13 @@ ' return _op(*_ins)' % at least one input was a matrix: '# dbout(f"at least one matrix param, shape={q.shape}")' - 'elements = []' - 'for i in range(0, len(q)):' - ' elements.append(_op(*[k[i] if isinstance(k, MatrixBase) else k for k in _ins]))' - 'return Matrix(*q.shape, elements)' ]; + 'assert len(q.shape) == 2, "non-2D arrays/tensors not yet supported"' + 'm, n = q.shape' + 'g = []' + 'for i in range(m):' + ' for j in range(n):' + ' g.append(_op(*[k[i, j] if isinstance(k, (MatrixBase, NDimArray)) else k for k in _ins]))' + 'return _make_2d_sym(g, shape=q.shape)' ]; z = pycall_sympy__ (cmd, varargin{:}); diff --git a/inst/@sym/private/mat_rclist_access.m b/inst/@sym/private/mat_rclist_access.m index 46593030..92a7c06c 100644 --- a/inst/@sym/private/mat_rclist_access.m +++ b/inst/@sym/private/mat_rclist_access.m @@ -1,4 +1,5 @@ %% Copyright (C) 2014, 2016, 2019, 2022 Colin B. Macdonald +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -32,15 +33,11 @@ error('this routine is for a list of rows and cols'); end - cmd = { '(A, rr, cc) = _ins' - 'if A is None or not A.is_Matrix:' - ' A = sp.Matrix([A])' - 'n = len(rr)' - 'M = [[0] for i in range(n)]' - 'for i in range(0, n):' - ' M[i][0] = A[rr[i],cc[i]]' - 'M = sp.Matrix(M)' - 'return M,' }; + cmd = {'(A, rr, cc) = _ins' + 'AA = A.tolist() if isinstance(A, (MatrixBase, NDimArray)) else [[A]]' + 'M = [AA[i][j] for i, j in zip(rr, cc)]' + 'return _make_2d_sym(M, shape=(len(rr), 1)),' + }; rr = num2cell(int32(r-1)); cc = num2cell(int32(c-1)); diff --git a/inst/@sym/private/mat_rclist_asgn.m b/inst/@sym/private/mat_rclist_asgn.m index 636cdb40..1641229e 100644 --- a/inst/@sym/private/mat_rclist_asgn.m +++ b/inst/@sym/private/mat_rclist_asgn.m @@ -2,6 +2,7 @@ %% Copyright (C) 2014, 2016-2017, 2019, 2022, 2024 Colin B. Macdonald %% Copyright (C) 2020 Mike Miller %% Copyright (C) 2020 Fernando Alvarruiz +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -58,42 +59,38 @@ % Easy trick to copy A into larger matrix AA: % AA = sp.Matrix.zeros(n, m) % AA[0, 0] = A - % Also usefil: .copyin_matrix + % Also useful: .copyin_matrix - cmd = { '(A, r, c, B) = _ins' - '# B linear access fix, transpose for sympy row-based' - 'if B is None or not B.is_Matrix:' - ' B = sp.Matrix([[B]])' - 'BT = B.T' - '# make a resized copy of A, and copy existing stuff in' - 'if isinstance(A, list):' - ' assert len(A) == 0, "unexpectedly non-empty list: report bug!"' - ' n = max(max(r) + 1, 1)' - ' m = max(max(c) + 1, 1)' - ' AA = [[0]*m for i in range(n)]' - 'elif A is None or not isinstance(A, MatrixBase):' - ' # we have non-matrix, put in top-left' - ' n = max(max(r) + 1, 1)' - ' m = max(max(c) + 1, 1)' - ' AA = [[0]*m for i in range(n)]' - ' AA[0][0] = A' - 'else:' - ' # build bigger matrix' - ' n = max(max(r) + 1, A.rows)' - ' m = max(max(c) + 1, A.cols)' - ' AA = [[0]*m for i in range(n)]' - ' # copy current matrix in' - ' for i in range(A.rows):' - ' for j in range(A.cols):' - ' AA[i][j] = A[i, j]' - '# now insert the new bits from B' - 'for i, (r, c) in enumerate(zip(r, c)):' - ' AA[r][c] = BT[i]' - 'return sp.Matrix(AA),' }; + cmd = {'(A, rr, cc, b) = _ins' + 'assert A == [] or not isinstance(A, list), "unexpectedly non-empty list: report bug!"' + 'if A == []:' + ' AA = []' + ' (nrows_A, ncols_A) = (0, 0)' + 'elif isinstance(A, (MatrixBase, NDimArray)):' + ' AA = A.tolist()' + ' (nrows_A, ncols_A) = A.shape' + 'else:' + ' AA = [[A]]' + ' (nrows_A, ncols_A) = (1, 1)' + 'bb = b.tolist() if isinstance(b, (MatrixBase, NDimArray)) else [[b]]' + 'entries = dict(zip(zip(rr, cc), flatten(bb, levels=1)))' + 'def entry(i, j):' + ' if (i, j) in entries:' + ' return entries[i, j]' + ' elif i < nrows_A and j < ncols_A:' + ' return AA[i][j]' + ' else:' + ' return S.Zero' + 'n = max(max(rr) + 1, nrows_A)' + 'm = max(max(cc) + 1, ncols_A)' + 'MM = [[entry(i, j) for j in range(m)] for i in range(n)]' + 'M = make_2d_sym(MM)' + 'return M,'}; rr = num2cell(int32(r-1)); cc = num2cell(int32(c-1)); - z = pycall_sympy__ (cmd, A, rr, cc, B); + b = vec (B); # B is accessed with linear indexing, as a column vector + z = pycall_sympy__ (cmd, A, rr, cc, b); % a simpler earlier version, but only for scalar r,c %cmd = { '(A, r, c, b) = _ins' diff --git a/inst/@sym/private/mat_replace.m b/inst/@sym/private/mat_replace.m index 9919a0fd..931c9e9d 100644 --- a/inst/@sym/private/mat_replace.m +++ b/inst/@sym/private/mat_replace.m @@ -3,6 +3,7 @@ %% Copyright (C) 2016 Abhinav Tripathi %% Copyright (C) 2017 NVS Abhilash %% Copyright (C) 2020 Fernando Alvarruiz +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -161,7 +162,8 @@ if isscalar (A) z = sym(zeros (1, 0)); else - cmd = { 'A, subs = _ins' + cmd = { 'AA, subs = _ins' + 'A = AA.as_mutable()' 'if isinstance(subs, Integer):' ' A.col_del(subs - 1)' ' return A,' @@ -178,7 +180,8 @@ % no test coverage: not sure how to hit this z = sym(zeros (0, 1)); else - cmd = { 'A, subs = _ins' + cmd = { 'AA, subs = _ins' + 'A = AA.as_mutable()' 'if isinstance(subs, Integer):' ' A.row_del(subs - 1)' ' return A,' diff --git a/inst/@sym/private/uniop_bool_helper.m b/inst/@sym/private/uniop_bool_helper.m index c9d6ab20..b2f3eb92 100644 --- a/inst/@sym/private/uniop_bool_helper.m +++ b/inst/@sym/private/uniop_bool_helper.m @@ -1,4 +1,4 @@ -%% Copyright (C) 2016, 2019 Colin B. Macdonald +%% Copyright (C) 2016, 2019, 2022 Colin B. Macdonald %% %% This file is part of OctSymPy. %% @@ -51,9 +51,9 @@ cmd = [ cmd 'x = _ins[0]' 'pp = _ins[1:]' - 'if x is not None and x.is_Matrix:' + 'if isinstance(x, (MatrixBase, NDimArray)):' ' # bool will map None to False' - ' return [bool(sf(a, *pp)) for a in x.T],' + ' return [bool(sf(a, *pp)) for a in flatten(transpose(x).tolist(), levels=1)],' 'return bool(sf(x, *pp))' ]; r = pycall_sympy__ (cmd, x, varargin{:}); @@ -65,10 +65,11 @@ case 'sym' warning('FIXME: not working for scalars') + % currently unused, and certainly not tested cmd = [ cmd 'x = _ins[0]' 'pp = _ins[1:]' - 'if x if not None and x.is_Matrix:' + 'if isinstance(x, (MatrixBase, NDimArray)):' ' return x.applyfunc(sf, *pp)' 'return sf(x, *pp)' ]; diff --git a/inst/@sym/repmat.m b/inst/@sym/repmat.m index af16d0d8..7a8d521f 100644 --- a/inst/@sym/repmat.m +++ b/inst/@sym/repmat.m @@ -1,4 +1,5 @@ %% Copyright (C) 2014, 2016, 2019 Colin B. Macdonald +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -51,18 +52,21 @@ print_usage (); end - cmd = { '(A, n, m) = _ins' - 'if n == 0 or m == 0:' - ' return sp.Matrix(n, m, [])' - 'if A is None or not A.is_Matrix:' - ' A = sp.Matrix([A])' - 'L = [A]*m' - 'B = sp.Matrix.hstack(*L)' - 'L = [B]*n' - 'B = sp.Matrix.vstack(*L)' - 'return B' }; + if n == 0 || m == 0 + [nrows_A, ncols_A] = size (A); + B = sym (zeros (n * nrows_A, m * ncols_A)); + return + end + + %% duplicate A horizontally m times + MM = cellfun (@(varargin) A, cell (1, m), 'UniformOutput', false); + M = horzcat (MM{:}); + + %% now duplicate that result vertically n times + NN = cellfun (@(varargin) M, cell (1, n), 'UniformOutput', false); + N = vertcat (NN{:}); - B = pycall_sympy__ (cmd, sym(A), int32(n), int32(m)); + B = N; end @@ -95,3 +99,11 @@ %! assert (isequal (size(A), [0 3])) %! A = repmat(sym(pi), [2 0]); %! assert (isequal (size(A), [2 0])) + +%!test +%! % even more empties, see also https://github.com/cbm755/octsympy/issues/1218 +%! A = randi (16, 5, 7); +%! assert (isa (repmat (sym (A), [0 3]), 'sym')); +%! assert (isa (repmat (sym (A), [2 0]), 'sym')); +%! assert (isequal (sym (repmat (A, [0 3])), (repmat (sym (A), [0 3])))); +%! assert (isequal (sym (repmat (A, [2 0])), (repmat (sym (A), [2 0])))); diff --git a/inst/@sym/reshape.m b/inst/@sym/reshape.m index abbdfdb2..b8087919 100644 --- a/inst/@sym/reshape.m +++ b/inst/@sym/reshape.m @@ -1,4 +1,4 @@ -%% Copyright (C) 2014, 2016, 2019 Colin B. Macdonald +%% Copyright (C) 2014, 2016, 2019, 2022 Colin B. Macdonald %% %% This file is part of OctSymPy. %% @@ -75,9 +75,10 @@ cmd = { '(A, n, m) = _ins' - 'if A is not None and A.is_Matrix:' - ' #sympy is row-based' - ' return A.T.reshape(m,n).T' + 'if isinstance(A, (MatrixBase, NDimArray)):' + ' #sympy is row-based, but Array does not have .T' + ' #return A.T.reshape(m,n).T' + ' return transpose(transpose(A).reshape(m, n))' 'else:' ' if n != 1 or m != 1:' ' raise ValueError("cannot reshape scalar to non-1x1 size")' diff --git a/inst/@sym/subs.m b/inst/@sym/subs.m index 66d78616..056ba60a 100644 --- a/inst/@sym/subs.m +++ b/inst/@sym/subs.m @@ -255,14 +255,14 @@ 'sizes = {(a.shape if a.is_Matrix else (1, 1)) for a in yy}' 'sizes.discard((1, 1))' 'assert len(sizes) == 1, "all substitutions must be same size or scalar"' - 'size = sizes.pop()' - 'numel = prod(size)' - 'g = [0]*numel' - 'for i in range(len(g)):' - ' yyy = [y[i] if y.is_Matrix else y for y in yy]' - ' sublist = list(zip(xx, yyy))' - ' g[i] = f.subs(sublist, simultaneous=True).doit()' - 'return Matrix(*size, g)' + 'm, n = sizes.pop()' + 'g = []' + 'for i in range(m):' + ' for j in range(n):' + ' yyy = [y[i, j] if y.is_Matrix else y for y in yy]' + ' sublist = list(zip(xx, yyy))' + ' g.append(f.subs(sublist, simultaneous=True).doit())' + 'return _make_2d_sym(g, shape=(m, n))' }; g = pycall_sympy__ (cmd, f, in, out); @@ -459,3 +459,13 @@ %! g = subs (f); %! syms x %! assert (isequal (g, 6*x*y)) + +%!test +%! % issue #1236, correct empty sizes +%! syms x +%! g = subs (x, x, zeros (0, 0)); +%! assert (size (g), [0 0]) +%! g = subs (x, x, zeros (0, 3)); +%! assert (size (g), [0 3]) +%! g = subs (x, x, zeros (3, 0)); +%! assert (size (g), [3 0]) diff --git a/inst/@sym/transpose.m b/inst/@sym/transpose.m index 214766b3..16155480 100644 --- a/inst/@sym/transpose.m +++ b/inst/@sym/transpose.m @@ -1,4 +1,5 @@ %% Copyright (C) 2014-2016, 2019 Colin B. Macdonald +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -65,11 +66,10 @@ print_usage (); end - cmd = { 'x = _ins[0]' - 'if x.is_Matrix:' - ' return x.T' - 'else:' - ' return x' }; + cmd = {'def is_matrix_or_array(x):' + ' return isinstance(x, (MatrixBase, NDimArray))' + 'x, = _ins' + 'return transpose(x) if is_matrix_or_array(x) else x'}; z = pycall_sympy__ (cmd, x); @@ -92,3 +92,8 @@ %!test %! A = [1 2] + 1i; %! assert(isequal( sym(A).' , sym(A.') )) + +%!test +%! % see https://github.com/cbm755/octsympy/issues/1215 +%! none = pycall_sympy__ ('return None'); +%! assert (isequal (none.', none)); diff --git a/inst/@sym/vertcat.m b/inst/@sym/vertcat.m index 3a6907fa..2f71a9ff 100644 --- a/inst/@sym/vertcat.m +++ b/inst/@sym/vertcat.m @@ -1,5 +1,6 @@ %% SPDX-License-Identifier: GPL-3.0-or-later -%% Copyright (C) 2014-2017, 2019, 2024 Colin B. Macdonald +%% Copyright (C) 2014-2017, 2019, 2022, 2024 Colin B. Macdonald +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -44,23 +45,27 @@ % special case for 0x0 but other empties should be checked for % compatibility - cmd = { - '_proc = []' - 'for i in _ins:' - ' if i is None or not i.is_Matrix:' - ' _proc.append(sp.Matrix([[i]]))' - ' else:' - ' if i.shape == (0, 0):' - ' pass' - ' else:' - ' _proc.append(i)' - 'return sp.MatrixBase.vstack(*_proc),' - }; - - for i = 1:nargin - varargin{i} = sym(varargin{i}); - end - h = pycall_sympy__ (cmd, varargin{:}); + cmd = {'def is_matrix_or_array(x):' + ' return isinstance(x, (MatrixBase, NDimArray))' + 'def number_of_columns(x):' + ' return x.shape[1] if is_matrix_or_array(x) else 1' + 'def number_of_rows(x):' + ' return x.shape[0] if is_matrix_or_array(x) else 1' + 'def all_equal(*ls):' + ' return True if ls == [] else all(ls[0] == x for x in ls[1:])' + 'args = [x for x in _ins if x != zeros(0, 0)] # remove 0x0 matrices' + 'ncols = [number_of_columns(x) for x in args]' + 'nrows = [number_of_rows(x) for x in args]' + 'if not all_equal(*ncols):' + ' msg = "vertcat: all inputs must have the same number of columns"' + ' raise ShapeError(msg)' + 'ncol = 0 if not ncols else ncols[0]' + 'CCC = [flatten(x, levels=1) if is_matrix_or_array(x) else [x] for x in args]' + 'CC = flatten(CCC, levels=1)' + 'return _make_2d_sym(CC, shape=(sum(nrows), ncol))' }; + + args = cellfun (@sym, varargin, 'UniformOutput', false); + h = pycall_sympy__ (cmd, args{:}); end @@ -120,12 +125,6 @@ %! a = [v; []; []]; %! assert (isequal (a, v)) -%!xtest -%! % FIXME: is this Octave bug? worth worrying about -%! syms x -%! a = [x; [] []]; -%! assert (isequal (a, x)) - %!test %! % more empty vectors %! v = [sym(1) sym(2)]; @@ -137,6 +136,25 @@ %! q = sym(ones(0, 3)); %! w = vertcat(v, q); +%!error +%! z03 = sym (zeros (0, 3)); +%! z04 = sym (zeros (0, 4)); +%! % Note Issue #1238: unclear error message: +%! % [z03; z04]; +%! % but this gives the ShapeError +%! vertcat(z03, z04); + +%!test +%! % special case for the 0x0 empty: no error +%! z00 = sym (zeros (0, 0)); +%! z03 = sym (zeros (0, 3)); +%! [z00; z03]; + +%!test +%! syms x +%! a = [x; sym([]) []]; +%! assert (isequal (a, x)) + %!test %! % Octave 3.6 bug: should pass on 3.8.1 and matlab %! a = [sym(1) 2]; @@ -154,3 +172,18 @@ %! A = sym ([1 2]); %! B = simplify (A); %! assert (isequal ([B; A], [A; B])) + +%!test +%! % issue #1236, correct empty sizes +%! syms x +%! z00 = sym (zeros (0, 0)); +%! z30 = sym (zeros (3, 0)); +%! z40 = sym (zeros (4, 0)); +%! z03 = sym (zeros (0, 3)); +%! assert (size ([z00; z00]), [0 0]) +%! assert (size ([z00; z30]), [3 0]) +%! assert (size ([z30; z30]), [6 0]) +%! assert (size ([z30; z40]), [7 0]) +%! assert (size ([z30; z00; z30]), [6 0]) +%! assert (size ([z03; z03]), [0 3]) +%! assert (size ([z03; z03; z03]), [0 3]) diff --git a/inst/private/python_header.py b/inst/private/python_header.py index 3506bf56..2c265190 100644 --- a/inst/private/python_header.py +++ b/inst/private/python_header.py @@ -155,7 +155,7 @@ def octoutput(x, et): f.text = str(OCTCODE_BOOL) f = ET.SubElement(a, "f") f.text = str(x) - elif x is None or isinstance(x, (sp.Basic, sp.MatrixBase)): + elif x is None or isinstance(x, (sp.Basic, sp.MatrixBase, sp.NDimArray)): # FIXME: is it weird to pretend None is a SymPy object? if isinstance(x, (sp.Matrix, sp.ImmutableMatrix)): _d = x.shape @@ -164,6 +164,9 @@ def octoutput(x, et): _d = [float(r) if (isinstance(r, sp.Basic) and r.is_Integer) else float('nan') if isinstance(r, sp.Basic) else r for r in x.shape] + elif isinstance(x, sp.NDimArray): + _d = x.shape + dbout(f"I am here with an array with shape {_d}") elif x is None: _d = (1,1) else: @@ -238,6 +241,49 @@ def octoutput(x, et): except: echo_exception_stdout("in python_header defining fcns block 4") raise + + +try: + def make_2d_sym(it_of_it, dbg_matrix_only=False): + # should be kept in sync with the same function + # defined in inst/private/python_ipc_native.m + # FIXME: dbg_matrix_only is used for debugging, remove + # it once sympy drops non-Expr support in Matrix + """ + Given an iterable of iterables of syms IT_OF_IT. + If all elements of IT_OF_IT are Expr, construct the + corresponding Matrix. Otherwise, construct the + corresponding non-Matrix 2D sym. + """ + ls_of_ls = [[elt for elt in it] for it in it_of_it] + elts = flatten(ls_of_ls, levels=1) + if Version(spver) <= Version("1.11.1"): + # never use Array on older SymPy + dbg_matrix_only = True + if (dbg_matrix_only + or all(isinstance(elt, Expr) for elt in elts)): + return Matrix(ls_of_ls) + else: + dbout(f"make_2d_sym: constructing 2D sym...") + return Array(ls_of_ls) + def _make_2d_sym(flat, shape, dbg_matrix_only=False): + """ + If all elements of FLAT are Expr, construct the + corresponding Matrix. Otherwise, construct the + corresponding non-Matrix 2D sym. + """ + flat = list(flat) + if Version(spver) <= Version("1.11.1"): + # never use Array on older SymPy + dbg_matrix_only = True + if (dbg_matrix_only + or all(isinstance(elt, Expr) for elt in flat)): + return Matrix(*shape, flat) + dbout(f"make_2d_sym: constructing 2D sym...") + return Array(flat, shape) +except: + echo_exception_stdout("in python_header defining fcns block 5") + raise # end of python header, now couple blank lines diff --git a/inst/private/python_ipc_native.m b/inst/private/python_ipc_native.m index 5beb23cc..b62c47e0 100644 --- a/inst/private/python_ipc_native.m +++ b/inst/private/python_ipc_native.m @@ -112,6 +112,43 @@ ' # should be kept in sync with the same function' ' # defined in inst/private/python_header.py' ' sys.stderr.write("pydebug: " + str(l) + "\n")' + 'def make_2d_sym(it_of_it, dbg_matrix_only=False):' + ' # should be kept in sync with the same function' + ' # defined in inst/private/python_header.py' + ' # FIXME: dbg_matrix_only is used for debugging, remove' + ' # it once sympy drops non-Expr support in Matrix' + ' """' + ' Given an iterable of iterables of syms IT_OF_IT.' + ' If all elements of IT_OF_IT are Expr, construct the' + ' corresponding Matrix. Otherwise, construct the' + ' corresponding non-Matrix 2D sym.' + ' """' + ' ls_of_ls = [[elt for elt in it] for it in it_of_it]' + ' elts = flatten(ls_of_ls, levels=1)' + ' if Version(spver) <= Version("1.11.1"):' + ' # never use Array on older SymPy' + ' dbg_matrix_only = True' + ' if (dbg_matrix_only' + ' or all(isinstance(elt, Expr) for elt in elts)):' + ' return Matrix(ls_of_ls)' + ' else:' + ' dbout(f"make_2d_sym: constructing 2D sym...")' + ' return Array(ls_of_ls)' + 'def _make_2d_sym(flat, shape, dbg_matrix_only=False):' + ' """' + ' If all elements of FLAT are Expr, construct the' + ' corresponding Matrix. Otherwise, construct the' + ' corresponding non-Matrix 2D sym.' + ' """' + ' flat = list(flat)' + ' if Version(spver) <= Version("1.11.1"):' + ' # never use Array on older SymPy' + ' dbg_matrix_only = True' + ' if (dbg_matrix_only' + ' or all(isinstance(elt, Expr) for elt in flat)):' + ' return Matrix(*shape, flat)' + ' dbout(f"make_2d_sym: constructing 2D sym...")' + ' return Array(flat, shape)' }, newl)) have_headers = true; end diff --git a/inst/vpasolve.m b/inst/vpasolve.m index 6fb80e78..2b0a7308 100644 --- a/inst/vpasolve.m +++ b/inst/vpasolve.m @@ -105,6 +105,9 @@ '(e, x, x0, n) = _ins' 'import mpmath' 'mpmath.mp.dps = n' + 'if isinstance(x, MatrixBase):' + ' # TODO: find/file upstream issue' + ' x = x.as_mutable()' 'r = nsolve(e, x, x0)' 'return r' }; r = pycall_sympy__ (cmd, sym(e), x, x0, n);