【问题标题】:A Matlab class for Commutative Hypercomplex number: How to overload the basic Matlab function?交换超复数的 Matlab 类:如何重载基本的 Matlab 函数?
【发布时间】:2022-01-11 19:57:44
【问题描述】:

我正在编写交换超复数类。

首先:如果已经存在,请告知。我还没有找到。 PS:四元数存在但不可交换。

我试图重现 Matlab 对真实和复杂的所有功能,但使用超复杂。我正在研究的解决方案是定义一个类。如果可以使用更聪明的方法,请告诉。

存在许多具有复数的代码,其想法是巧妙地重载以使所有现有程序都与 Hypercomplex 一起工作。许多用户可能会使用这项工作。

请在文末查看 Ablamowicz1996 中的主要数学原理 _ Clifford Algebras with Numeric and Symbolic Computations

我的 Hypercomplex 类适用于基本操作:我重载了 mtimes(a,b) = a*b 操作。 例如,我可以这样做:

A = MyClass(1);
B = MyClass(2);
A*B; % ->that works

作为一个主要问题,例如在 2D 中,我希望能够使用

[A B C].*[D ; E]

在使用 Matlab 的逐元素行为时,对于任何维度,以及我对 * 的定义。例如,在处理大小兼容但不相同的矩阵时:

A = [1 2 3]; B = [1;2]

Matlab 给出

A.*B    
ans =

 1     2     3
 2     4     6

兼容时也适用于 N 维。 如何重载 times(a,b) = a.*b , power, rdivide.. 以获得 Matlab 的元素行为?

这是我的课

classdef Hypercomplex < handle
    properties (SetAccess = public, GetAccess = public)
        A
    end
    properties (SetAccess = private, GetAccess = private)
        eps,nu,e1,e2
    end
    
    methods
        function s = Hypercomplex(Z)             %% Conctruc
            % Z might be real, complex or hypercomplex
            if isa(Z,'Hypercomplex')            % is HyperComplex
                s = Z;
            elseif ~isreal(Z) && length(Z) == 1 % is Complex
                s = Hypercomplex([real(Z) imag(Z) 0 0]);
            elseif isreal(Z) && length(Z) < 5
                s.A = zeros(1,4);
                for ii = 1:length(Z)            % is vecteur
                    s.A(ii) = Z(ii);
                end
            end
        end
        function s = epsnu(s)
            s.eps = (s.A(1) - s.A(4)) + 1i*(s.A(2)+s.A(3));
            s.nu  = (s.A(1) + s.A(4)) + 1i*(s.A(2)-s.A(3));
        end
        function s = e1e2(s)
            s.e1 = Hypercomplex([0.5 0 0 -0.5]);
            s.e2 = Hypercomplex([0.5 0 0  0.5]);
        end
        %% Overload
        function out = cos(a)
            a.epsnu.e1e2;
            out = cos(a.eps)*a.e1 + cos(a.nu)*a.e2;
            % I might also do for  sin exp sqrt ...
        end
        
        function out = mtimes(a,b) % '*'
            a = Hypercomplex(a).epsnu;
            b = Hypercomplex(b).epsnu;
            eps12 = 0.5*(a.eps * b.eps);% produit complex
            nu12  = 0.5*(a.nu  * b.nu );% produit complex
            
            h1 = Hypercomplex([real(eps12) imag(eps12) imag(eps12) -real(eps12)]);
            h2 = Hypercomplex([real(nu12)  imag(nu12) -imag(nu12)   real(nu12) ]);
            out = h1+h2;
        end
        function out = mrdivide(a,b)
            a = Hypercomplex(a);
            b = Hypercomplex(b);
            out = a * b.inverse;
        end
        function s = inverse(s)
            s = Hypercomplex(s).epsnu.e1e2;
            s = 1/(s.eps)*s.e1 + 1/(s.nu)*s.e2;
        end
        function out = plus(a,b)
            a = Hypercomplex(a);
            b = Hypercomplex(b);
            out = Hypercomplex(a.A + b.A);
        end
        function out = minus(a,b)
            a = Hypercomplex(a);
            b = Hypercomplex(b);
            out = Hypercomplex(a.A - b.A);
        end
    end
end

示例 i^2 = -1 :

>> Ni =  Hypercomplex([0 1 0 0]) ;
>> Ni*Ni
  Hypercomplex with properties:

    A: [-1 0 0 0]

【问题讨论】:

  • 您可能需要重载 times 以使用 bsxfun(隐式扩展在它成为本机功能之前完成的方式)与重载的 mtimes
  • 我试过 bsxfun(@mtimes,A,B) 但 A 和 B 需要是数字数组,而不是来自我定义的类。还是我错了?
  • 我认为如果它们属于您定义的类,那么重载会起作用,不是吗?
  • 显然,它不起作用,即使在调用内部应用

标签: matlab class oop operator-overloading complex-numbers


【解决方案1】:

我认为你需要遍历你的数组,并且有一种特殊情况,如果一个维度的一个输入的大小为 1,而另一个没有,则使用该数组的相同元素。实现此目的的一种简单方法是使用 (i-1)*step + 1 进行索引,其中如果数组大小为 1,step 为 0,否则为 1。

需要注意的一点是,在您的重载方法中,索引不使用您的自定义索引定义(subsrefsubsasgn),而是使用内置的。

例如,假设您只允许类的二维数组:

function C = times(A, B)
szA = size(A);
szB = size(B);
if numel(szA)~=2 || numel(szB)~=2
   error('Only 2D arrays are supported')
end
szC = max(szA, szB);
stepA = [matchSizes(szA(1), szC(1)), matchSizes(szA(2), szC(2))];
stepB = [matchSizes(szB(1), szC(1)), matchSizes(szB(2), szC(2))];
C = MyClass.empty(szC);
for jj = 1:szC(2)
   for ii = 1:szC(1)
      C(ii,jj) = A((ii-1)*stepA(1)+1,(jj-1)*stepA(2)+1) * B((ii-1)*stepB(1)+1,(jj-1)*stepB(2)+1);
   end
end

% private helper function, keep in the same file
function step = matchSizes(szIn, szOut)
step = 1;
if szIn ~= szOut
   if szIn == 1
      step = 0;
   else
      error('Sizes not compatible')
   end
end

注意:代码未经测试。

注意 2:这是基于 0 的索引更方便的情况之一!

【讨论】:

  • 效果很好,聪明的克里斯。谢谢。
【解决方案2】:

如果MyClass 有一个属性val 来存储数值,我认为(希望)这应该是可能的(未经测试):

function obj = times(A, B)
  r = reshape([A.val], size(A)) .* reshape([B.val], size(B));
  obj = arrayfun(@MyClass, r);
end

编辑(因为问题已编辑):

您可以按照两种方法来实现这些数字的数组:

  1. 对象数组
    在这种方法中,每个对象只包含一个超复数。这是您尝试过的方式。正如@CrisLuengo 指出的那样,您可以使用循环来创建所需的结果,或者您可以结合使用矢量化和使用arrayfuncellfun 等函数。您需要知道如何使用元胞数组和逗号分隔列表,并熟悉隐式扩展等 Verctorization 操作。在这里,我使用矢量化和cellfun 实现了plusepsnu。它们可以使用标量和对象数组:

     function out = plus(a,b)
         a = Hypercomplex(a);
         b = Hypercomplex(b);
         AA = reshape([a.A], [4, size(a)]);
         BB = reshape([b.A], [4, size(b)]])
         CC = AA + BB;
         A1 = reshape()
         out = cellfun(@Hypercomplex, num2cell(reshape(CC, 4, []).', 2));
     end 
    
     function s = epsnu(s)
       AA = reshape([s.A], 4, []);
       eps = num2cell(AA(1, :) - AA(4, :) + 1i.*(AA(2, :) + AA(3, :)));
       nu = num2cell(AA(1, :) + AA(4, :) + 1i.*(AA(2, :) - AA(3, :)));
       [s.eps] = eps{:};
       [s.nu] = nu{:};
    end
    
  2. 包含数字数组的对象
    除了将A 定义为[4 x 1] 数组之外,您还可以将每个坐标(x,y,z,et) 定义为类的单独属性,每个属性都可以包含数字数组。您需要重载 cat 运算符来连接对象。结果没有创建对象数组,但连接了底层的verctors。如果要模拟元素访问,则需要重载 subsrefsubsasgn。我认为这种方法比第一种方法实现起来更简单。

【讨论】:

  • A.val 是具有 [1 i j k] 的 4x1 向量。事实上,我正在尝试创建一个可交换的 HyperComplex 数字类。我认为我的问题还不够完整,我将发布一个包含更多详细信息的新问题。
  • 您可以编辑当前问题。
  • 所以据我了解,该类得到一个实标量并形成一个超复数?
  • 好的,我会重新编辑:有没有办法更具体地分享 MyClass.m 文件?
  • 这里你需要提供一个最小的工作示例。只需将简化版本复制/粘贴到您的编辑中即可。
猜你喜欢
  • 1970-01-01
  • 2013-12-25
  • 2011-04-07
  • 1970-01-01
  • 1970-01-01
  • 2011-12-26
  • 1970-01-01
  • 2017-07-01
  • 2013-10-21
相关资源
最近更新 更多