|
| 1 | +%% Copyright (C) 2022 Alex Vong |
| 2 | +%% |
| 3 | +%% This file is part of OctSymPy. |
| 4 | +%% |
| 5 | +%% OctSymPy is free software; you can redistribute it and/or modify |
| 6 | +%% it under the terms of the GNU General Public License as published |
| 7 | +%% by the Free Software Foundation; either version 3 of the License, |
| 8 | +%% or (at your option) any later version. |
| 9 | +%% |
| 10 | +%% This software is distributed in the hope that it will be useful, |
| 11 | +%% but WITHOUT ANY WARRANTY; without even the implied warranty |
| 12 | +%% of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| 13 | +%% the GNU General Public License for more details. |
| 14 | +%% |
| 15 | +%% You should have received a copy of the GNU General Public |
| 16 | +%% License along with this software; see the file COPYING. |
| 17 | +%% If not, see <https://www.gnu.org/licenses/>. |
| 18 | + |
| 19 | +%% -*- texinfo -*- |
| 20 | +%% @documentencoding UTF-8 |
| 21 | +%% @defmethod @@sym iztrans (@var{F}, @var{z}, @var{n}) |
| 22 | +%% @defmethodx @@sym iztrans (@var{F}) |
| 23 | +%% @defmethodx @@sym iztrans (@var{F}, @var{n}) |
| 24 | +%% FIXME: add help text and doctest |
| 25 | +%% |
| 26 | +%% @seealso{@@sym/ztrans} |
| 27 | +%% @end defmethod |
| 28 | + |
| 29 | +%% Author: Alex Vong |
| 30 | +%% Keywords: symbolic |
| 31 | + |
| 32 | +function x = iztrans (varargin) |
| 33 | + if (nargin > 3 || nargin == 0) |
| 34 | + print_usage (); |
| 35 | + end |
| 36 | + |
| 37 | + %% ensure all inputs are sym |
| 38 | + if ~all (cellfun (@(x) isa (x, 'sym'), varargin)) |
| 39 | + inputs = cellfun (@sym, varargin, 'UniformOutput', false); |
| 40 | + x = iztrans (inputs{:}); |
| 41 | + return |
| 42 | + end |
| 43 | + |
| 44 | + %% recursively call iztrans for non-scalar inputs |
| 45 | + if ~all (cellfun (@isscalar, varargin)) |
| 46 | + inputs = cellfun (@(x) num2cell (x), varargin, 'UniformOutput', false); |
| 47 | + x = sym (cellfun (@iztrans, inputs{:}, 'UniformOutput', false)); |
| 48 | + return |
| 49 | + end |
| 50 | + |
| 51 | + F = sym (varargin{1}); |
| 52 | + F_vars = findsymbols (F); |
| 53 | + find_var_from_F = @(v) F_vars (cellfun (@(x) strcmp (char (x), v), F_vars)); |
| 54 | + |
| 55 | + %% select var z |
| 56 | + if (nargin == 3) |
| 57 | + z = sym (varargin{2}); |
| 58 | + else |
| 59 | + zs = find_var_from_F ('z'); |
| 60 | + assert (numel (zs) <= 1, 'ztrans: there is more than one "z" symbol: check symvar (F) and sympy (F)'); |
| 61 | + if (~isempty (zs)) |
| 62 | + z = zs{:}; % use var z from F |
| 63 | + elseif (~isempty (F_vars)) |
| 64 | + z = symvar (F, 1); % select var from F using symfun |
| 65 | + else |
| 66 | + z = sym ('z'); % use freshly generated var z, FIXME: any assumptions? |
| 67 | + end |
| 68 | + end |
| 69 | + |
| 70 | + %% select var n |
| 71 | + if (nargin == 3) |
| 72 | + n = sym (varargin{3}); |
| 73 | + elseif (nargin == 2) |
| 74 | + n = sym (varargin{2}); |
| 75 | + else |
| 76 | + ns = find_var_from_F ('n'); |
| 77 | + if (isempty (ns)) % use var n if F is a not function of n |
| 78 | + n = sym ('n'); |
| 79 | + else % use var k if F is a function of n |
| 80 | + n = sym ('k'); |
| 81 | + end |
| 82 | + end |
| 83 | + |
| 84 | + cmd = {'from sympy.series.formal import compute_fps' |
| 85 | + 'def unevaluated_iztrans(F, z, n):' |
| 86 | + ' R = Symbol("R", positive=True, real=True)' |
| 87 | + ' theta = Symbol("theta", real=True)' |
| 88 | + ' F_polar = F.subs(z, R * exp(I * theta))' |
| 89 | + ' xn = Limit(R**n * Integral(F_polar * exp(I * n * theta),' |
| 90 | + ' (theta, -pi, pi)),' |
| 91 | + ' R, +oo) / (2 * pi)' |
| 92 | + ' return xn' |
| 93 | + 'def poly_to_lin_comb_of_kron_delta(p):' |
| 94 | + ' return sum([ak * KroneckerDelta(n - k, 0)' |
| 95 | + ' for ((k,), ak) in p.all_terms()])' |
| 96 | + '(F, z, n) = _ins' |
| 97 | + 'F_of_1_over_z = F.subs(z, 1 / z)' |
| 98 | + 'if expand(F_of_1_over_z).is_polynomial(z):' |
| 99 | + ' return poly_to_lin_comb_of_kron_delta(F_of_1_over_z.as_poly(z))' |
| 100 | + 'try:' |
| 101 | + ' (ak, _, ind) = compute_fps(F_of_1_over_z, z)' |
| 102 | + 'except TypeError:' |
| 103 | + ' return unevaluated_iztrans(F, z, n)' |
| 104 | + '(k,) = ak.variables' |
| 105 | + 'assert ak.start.is_integer and ak.start >= 0 and ak.stop == +oo' |
| 106 | + 'if expand(ind).is_polynomial(z):' |
| 107 | + ' an = Piecewise((ak.formula.subs(k, n), n >= ak.start), (0, True))' |
| 108 | + ' bn = poly_to_lin_comb_of_kron_delta(ind.as_poly(z))' |
| 109 | + ' xn = piecewise_fold(an) + bn' |
| 110 | + ' return xn' |
| 111 | + 'else:' |
| 112 | + ' return unevaluated_iztrans(F, z, n)'}; |
| 113 | + x = pycall_sympy__ (cmd, F, z, n); |
| 114 | +end |
| 115 | + |
| 116 | +%% FIXME: add bist |
0 commit comments