diff --git a/inst/@sym/find.m b/inst/@sym/find.m index fde8a9fa4..af7b58850 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 cac1fbd65..bbb583913 100644 --- a/inst/@sym/horzcat.m +++ b/inst/@sym/horzcat.m @@ -1,4 +1,5 @@ %% Copyright (C) 2014-2017, 2019 Colin B. Macdonald +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -21,7 +22,7 @@ %% @defop Method @@sym {horzcat} {(@var{x}, @var{y}, @dots{})} %% @defopx Operator @@sym {[@var{x}, @var{y}, @dots{}]} {} %% @defopx Operator @@sym {[@var{x} @var{y} @dots{}]} {} -%% Horizontally concatentate symbolic arrays. +%% Horizontally concatenate symbolic arrays. %% %% Example: %% @example @@ -45,25 +46,8 @@ function h = horzcat(varargin) - % special case for 0x0 but other empties should be checked for - % compatibilty - 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 diff --git a/inst/@sym/isAlways.m b/inst/@sym/isAlways.m index bda13094f..6c253027e 100644 --- a/inst/@sym/isAlways.m +++ b/inst/@sym/isAlways.m @@ -131,8 +131,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 = [simplify_tfn(a) for a in r]' diff --git a/inst/@sym/isconstant.m b/inst/@sym/isconstant.m index fc580a4a6..ccd900f45 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 0e0ea4bdb..2682f548d 100644 --- a/inst/@sym/logical.m +++ b/inst/@sym/logical.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. %% @@ -100,8 +100,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]' @@ -178,7 +178,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 fe68ca95e..996bd43d5 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 = [[0]*n for i in range(m)]' + 'for i in range(m):' + ' for j in range(n):' + ' g[i][j] = _op(*[k[i, j] if isinstance(k, (MatrixBase, NDimArray)) else k for k in _ins])' + 'return make_2d_sym(g)' ]; z = pycall_sympy__ (cmd, varargin{:}); diff --git a/inst/@sym/private/mat_rclist_access.m b/inst/@sym/private/mat_rclist_access.m index 46593030b..8662ac578 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 = list_from_2d_sym(A) if is_2d_sym(A) else [[A]]' + 'MM = [[AA[i][j]] for i, j in zip(rr, cc)]' + 'M = make_2d_sym(MM)' + 'return M,'}; 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 ce779776e..91b60762b 100644 --- a/inst/@sym/private/mat_rclist_asgn.m +++ b/inst/@sym/private/mat_rclist_asgn.m @@ -1,6 +1,7 @@ %% Copyright (C) 2014, 2016-2017, 2019, 2022 Colin B. Macdonald %% Copyright (C) 2020 Mike Miller %% Copyright (C) 2020 Fernando Alvarruiz +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -59,40 +60,36 @@ % AA[0, 0] = A % Also usefil: .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 is_2d_sym(A):' + ' AA = list_from_2d_sym(A)' + ' (nrows_A, ncols_A) = shape_of_2d_sym(A)' + 'else:' + ' AA = [[A]]' + ' (nrows_A, ncols_A) = (1, 1)' + 'bb = list_from_2d_sym(b) if is_2d_sym(b) 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 137aef88e..8e3a19d35 100644 --- a/inst/@sym/private/mat_replace.m +++ b/inst/@sym/private/mat_replace.m @@ -2,6 +2,7 @@ %% Copyright (C) 2016 Lagu %% Copyright (C) 2016 Abhinav Tripathi %% Copyright (C) 2020 Fernando Alvarruiz +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -154,7 +155,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,' @@ -171,7 +173,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 c9d6ab201..b2f3eb927 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 af16d0d8f..7a8d521fe 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 abbdfdb28..b80879193 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 543344090..aa6c9e093 100644 --- a/inst/@sym/subs.m +++ b/inst/@sym/subs.m @@ -256,14 +256,14 @@ 'sizes = {(a.shape if a.is_Matrix else (1, 1)) for a in yy}' 'sizes.discard((1, 1))' 'assert len(sizes) == 1, "all substitions 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 = [[0]*n for i in range(m)]' + '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[i][j] = f.subs(sublist, simultaneous=True).doit()' + 'return make_2d_sym(g)' }; g = pycall_sympy__ (cmd, f, in, out); diff --git a/inst/@sym/transpose.m b/inst/@sym/transpose.m index 214766b35..316c14035 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,8 @@ print_usage (); end - cmd = { 'x = _ins[0]' - 'if x.is_Matrix:' - ' return x.T' - 'else:' - ' return x' }; + cmd = {'x, = _ins' + 'return transpose(x) if is_2d_sym(x) else x'}; z = pycall_sympy__ (cmd, x); @@ -92,3 +90,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 09a8ea787..a9c5d3ce4 100644 --- a/inst/@sym/vertcat.m +++ b/inst/@sym/vertcat.m @@ -1,4 +1,5 @@ %% Copyright (C) 2014-2017, 2019 Colin B. Macdonald +%% Copyright (C) 2022 Alex Vong %% %% This file is part of OctSymPy. %% @@ -20,7 +21,7 @@ %% @documentencoding UTF-8 %% @defop Method @@sym {vertcat} {(@var{x}, @var{y}, @dots{})} %% @defopx Operator @@sym {[@var{x}; @var{y}; @dots{}]} {} -%% Vertically concatentate symbolic arrays. +%% Vertically concatenate symbolic arrays. %% %% Example: %% @example @@ -44,23 +45,23 @@ % special case for 0x0 but other empties should be checked for % compatibilty - 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 number_of_columns(x):' + ' return shape_of_2d_sym(x)[1] if is_2d_sym(x) else 1' + 'def all_equal(*ls):' + ' return True if ls == [] else all(ls[0] == x for x in ls[1:])' + 'def as_list_of_list(x):' + ' return list_from_2d_sym(x) if is_2d_sym(x) else [[x]]' + 'args = [x for x in _ins if x != zeros(0, 0)] # remove 0x0 matrices' + 'ncols = [number_of_columns(x) for x in args]' + 'if not all_equal(*ncols):' + ' msg = "vertcat: all inputs must have the same number of columns"' + ' raise ShapeError(msg)' + 'CCC = [as_list_of_list(x) for x in args]' + 'CC = flatten(CCC, levels=1)' + 'return make_2d_sym(CC)'}; + + args = cellfun (@sym, varargin, 'UniformOutput', false); + h = pycall_sympy__ (cmd, args{:}); end diff --git a/inst/private/python_header.py b/inst/private/python_header.py index 925e7a50b..4c3ae2fd6 100644 --- a/inst/private/python_header.py +++ b/inst/private/python_header.py @@ -152,7 +152,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 @@ -161,6 +161,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: @@ -235,6 +238,54 @@ def octoutput(x, et): except: echo_exception_stdout("in python_header defining fcns block 4") raise + + +try: + # begin: 2D sym funcs + # 2D sym funcs defined in inst/private/python_ipc_native.m + # and inst/private/python_header.py should be kept in sync + def make_2d_sym(it_of_it, dbg_matrix_only=False): + # 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...") + # FIXME: should we use Array or TableForm? + return Array(ls_of_ls) + def is_2d_sym(x): + types = (MatrixBase, NDimArray, TableForm) + return isinstance(x, types) + def is_matrix(x): + return isinstance(x, MatrixBase) + def is_non_matrix_2d_sym(x): + return isinstance(x, (NDimArray, TableForm)) + def list_from_2d_sym(X): + if isinstance(X, TableForm): + return [[x for x in tup] for tup in X._lines] + else: + return X.tolist() + def shape_of_2d_sym(X): + if isinstance(X, TableForm): + return (X._h, X._w) + else: + return X.shape + # end: 2D sym funcs +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 d1bd19328..652f17e81 100644 --- a/inst/private/python_ipc_native.m +++ b/inst/private/python_ipc_native.m @@ -111,6 +111,48 @@ ' # should be kept in sync with the same function' ' # defined in inst/private/python_header.py' ' sys.stderr.write("pydebug: " + str(l) + "\n")' + '# begin: 2D sym funcs' + '# 2D sym funcs defined in inst/private/python_ipc_native.m' + '# and inst/private/python_header.py should be kept in sync' + 'def make_2d_sym(it_of_it, dbg_matrix_only=False):' + ' # 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...")' + ' # FIXME: should we use Array or TableForm?' + ' return Array(ls_of_ls)' + 'def is_2d_sym(x):' + ' types = (MatrixBase, NDimArray, TableForm)' + ' return isinstance(x, types)' + 'def is_matrix(x):' + ' return isinstance(x, MatrixBase)' + 'def is_non_matrix_2d_sym(x):' + ' return isinstance(x, (NDimArray, TableForm))' + 'def list_from_2d_sym(X):' + ' if isinstance(X, TableForm):' + ' return [[x for x in tup] for tup in X._lines]' + ' else:' + ' return X.tolist()' + 'def shape_of_2d_sym(X):' + ' if isinstance(X, TableForm):' + ' return (X._h, X._w)' + ' else:' + ' return X.shape' + '# end: 2D sym funcs' }, newl)) have_headers = true; end