Commit e805bffc authored by Ferdinand Fischer's avatar Ferdinand Fischer
Browse files

except some points, that must be discussed for the quantity.Symbolic, the...

except some points, that must be discussed for the quantity.Symbolic, the implementation of quantity.Domain seems to be nearly finished.
parent 605efca2
......@@ -83,42 +83,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
myParser.parse(varargin{:});
%% domain parser
domainParser = misc.Parser();
domainParser.addParameter('domain', {}, @(g) isa(g, 'quantity.Domain'));
domainParser.addParameter('gridName', '', @(g) ischar(g) || iscell(g));
domainParser.addParameter('grid', [], @(g) isnumeric(g) || iscell(g));
domainParser.parse(varargin{:});
if domainParser.isDefault('domain') && ...
( domainParser.isDefault('grid') || ...
domainParser.isDefault('gridName') )
% case 1: nothing about the grid is defined
% -> use default grid
error('No domain is specified! A domain is obligatory for the initialization of a quantity.')
elseif domainParser.isDefault('domain')
% case 3: the gridNames and the gridValues are defined:
% -> initialize quantity.Domain objects with the
% specified values
myGridName = misc.ensureIsCell(domainParser.Results.gridName);
myGrid = misc.ensureIsCell(domainParser.Results.grid);
assert(isequal(size(myGrid), size(myGridName)), ...
'number of grids and gridNames must be equal');
% initialize the domain objects
myDomain = quantity.Domain.empty();
for k = 1:numel(myGrid)
myDomain(k) = quantity.Domain('grid', myGrid{k}, ...
'name', myGridName{k});
end
else
% else case: the domains are specified as domain
% objects.
myDomain = domainParser.Results.domain;
end
% #TODO check uniqueness of gridNames
myDomain = quantity.Domain.parser(varargin{:});
%% get the sizes of obj and grid
gridLength = myDomain.gridLength;
......@@ -1856,14 +1821,12 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
end
if all(isGrid)
grdName = '';
newGrid = {};
newDomain = quantity.Domain();
else
grdName = obj(kObj).gridName{~isGrid};
newGrid = obj(kObj).grid{~isGrid};
newDomain = quantity.Domain('grid', obj(kObj).grid{~isGrid}, ...
'name', obj(kObj).gridName{~isGrid});
end
newDomain = quantity.Domain('grid', newGrid, 'name', grdName);
[I.domain] = deal(newDomain);
end
......@@ -2123,7 +2086,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
end
value = reshape(v, [obj(1).gridSize(), size(obj)]);
if myDomain ~= obj(1).domain
if nargin == 2 && myDomain ~= obj(1).domain
% if a new domain is specified for the evaluation of
% the quantity, ...
if obj.isConstant
......
......@@ -298,6 +298,49 @@ classdef Domain < matlab.mixin.CustomDisplay
g(k) = quantity.Domain('grid', O, 'name', name{k});
end
end
function [myDomain, unmatched]= parser(varargin)
%% domain parser
domainParser = misc.Parser();
domainParser.addParameter('domain', {}, @(g) isa(g, 'quantity.Domain'));
domainParser.addParameter('gridName', '', @(g) ischar(g) || iscell(g));
domainParser.addParameter('grid', [], @(g) isnumeric(g) || iscell(g));
domainParser.parse(varargin{:});
if domainParser.isDefault('domain') && ...
( domainParser.isDefault('grid') || ...
domainParser.isDefault('gridName') )
% case 1: nothing about the grid is defined
error('No domain is specified! A domain is obligatory for the initialization of a quantity.')
elseif domainParser.isDefault('domain')
% case 3: the gridNames and the gridValues are defined:
% -> initialize quantity.Domain objects with the
% specified values
myGridName = misc.ensureIsCell(domainParser.Results.gridName);
myGrid = misc.ensureIsCell(domainParser.Results.grid);
assert(isequal(numel(myGrid), numel(myGridName)), ...
'number of grids and gridNames must be equal');
% initialize the domain objects
myDomain = quantity.Domain.empty();
for k = 1:numel(myGrid)
myDomain(k) = quantity.Domain('grid', myGrid{k}, ...
'name', myGridName{k});
end
else
% else case: the domains are specified as domain
% objects.
myDomain = domainParser.Results.domain;
end
assert(numel(myDomain) == numel(unique({myDomain.name})), ...
'The names of the domain must be unique');
unmatched = domainParser.UnmatchedNameValuePair;
end
end
end
......@@ -82,16 +82,17 @@ classdef Function < quantity.Discrete
recalculate = false;
end
if ~recalculate && (myDomain == obj(1).domain)
value = obj2value@quantity.Discrete(obj, myDomain);
value = obj2value@quantity.Discrete(obj);
else
% otherwise the function has to be evaluated on the new
% domain
value = nan([numel(obj), myDomain.gridLength]);
value = cell(size(obj));
for k = 1:numel(obj)
ndGrd = myDomain.ndgrid;
tmp = obj(k).evaluateFunction( ndGrd{:} );
value(k,:) = tmp(:);
value{k} = tmp(:);
end
value = reshape( cell2mat(value), [ gridLength(myDomain), size(obj)]);
end
end
......
......@@ -15,6 +15,12 @@ classdef Symbolic < quantity.Function
function obj = Symbolic(valueContinuous, varargin)
%TODO Contruktor überarbeiten:
% # bei matlabFunction( symbolischer ausdruck ) muss auf die
% Reihenfolge der variablen geachtet werden!
% # Manchmal werden mehrere grid variablen an den
% untergeordneten Konstruktor durchgereicht.
parentVarargin = {};
if nargin > 0
......@@ -26,21 +32,21 @@ classdef Symbolic < quantity.Function
variableParser = misc.Parser();
variableParser.addParameter('variable', quantity.Symbolic.getVariable(valueContinuous));
variableParser.addParameter('symbolicEvaluation', false);
variableParser.addParameter('gridName', '');
variableParser.parse(varargin{:});
variable = variableParser.Results.variable;
numVar = numel(variable);
defaultGrid = cell(1, numVar);
for it = 1:numVar
if it==1
defaultGrid{it} = linspace(0, 1, 101).';
else
defaultGrid{it} = reshape(linspace(0, 1, 101), [ones(1, it-1), numel(linspace(0, 1, 101))]);
end
if ~variableParser.isDefault('variable')
warning('Do not set the variable property. It will be ignored.')
end
gridParser = misc.Parser();
gridParser.addParameter('grid', defaultGrid);
gridParser.parse(varargin{:});
if variableParser.isDefault('gridName')
warning('Grid names are generated from the symbolic variable. Please set the grid names in a quantity.Domain object explicitly')
variableNames = cellstr(string(variableParser.Results.variable));
varargin = [varargin(:)', {'gridName'}, {variableNames}];
end
[myDomain, varargin] = quantity.Domain.parser(varargin{:});
s = size(valueContinuous);
S = num2cell(s);
......@@ -63,10 +69,10 @@ classdef Symbolic < quantity.Function
% variable
if isa(fun{k}, 'sym')
symb{k} = fun{k};
fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, variable);
fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, {myDomain.name});
elseif isa(fun{k}, 'double')
symb{k} = sym(fun{k});
fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, variable);
fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, {myDomain.name});
elseif isa(fun{k}, 'function_handle')
symb{k} = sym(fun{k});
else
......@@ -74,16 +80,14 @@ classdef Symbolic < quantity.Function
end
end
gridName = cellstr(string(variable));
% check if the reuqired gridName fits to the current
% variables:
if isfield(gridParser.Unmatched, 'gridName') ...
&& ~all(strcmp(gridParser.Unmatched.gridName, gridName))
error('If the gridName(s) are set explicitly, they have to be the same as the variable names!')
end
%
% % check if the reuqired gridName fits to the current
% % variables:
% if ~all(strcmp(sort({myDomain.name}), sort(variableName)))
% error('If the gridName(s) are set explicitly, they have to be the same as the variable names!')
% end
parentVarargin = [{fun}, varargin(:)', {'grid'}, {gridParser.Results.grid}, ...
{'gridName'}, {gridName}];
parentVarargin = [{fun}, varargin(:)', {'domain'}, {myDomain}];
end
% call parent class
obj@quantity.Function(parentVarargin{:});
......@@ -96,7 +100,8 @@ classdef Symbolic < quantity.Function
% special properties
for k = 1:numel(valueContinuous)
obj(k).variable = variable; %#ok<AGROW>
obj(k).variable = ...
quantity.Symbolic.getVariable(valueContinuous); %#ok<AGROW>
obj(k).valueSymbolic = symb{k}; %#ok<AGROW>
obj(k).symbolicEvaluation = ...
variableParser.Results.symbolicEvaluation; %#ok<AGROW>
......@@ -569,35 +574,13 @@ classdef Symbolic < quantity.Function
function mObj = uminus(obj)
% unitary minus: C = -A
mObj = quantity.Symbolic(-obj.sym, 'grid', obj(1).grid, ...
'variable', obj(1).variable, 'name', ['-', obj(1).name]);
'domain', obj(1).domain, 'name', ['-', obj(1).name]);
end % uminus()
function mObj = uplus(obj)
% unitary plus: C = +A
mObj = copy(obj);
end % uplus()
% function parameters = combineGridGridNameVariable(A, B, parameters)
% assert(isa(A, 'quantity.Symbolic') && isa(B, 'quantity.Symbolic'), ...
% 'A and B must be quantity.Symbolic');
%
% if nargin < 3
% parameters = struct(A(1));
% end
% % combine grids of two quantities. This is often used in mathematical
% % operations to obtain the parameters of the resulting quantity.
% idx = computePermutationVectors(A, B);
% parameters.grid = ...
% [A(1).grid(idx.A.common), ...
% A(1).grid(~idx.A.common), ...
% B(1).grid(~idx.B.common)];
% parameters.gridName = [A(1).gridName(idx.A.common), ...
% A(1).gridName(~idx.A.common), ...
% B(1).gridName(~idx.B.common)];
% parameters.variable = [A(1).variable(idx.A.common), ...
% A(1).variable(~idx.A.common), ...
% B(1).variable(~idx.B.common)];
% end % combineGridGridNameVariable()
%
function C = mtimes(A, B)
% if one input is ordinary matrix, this is very simple.
if isempty(B) || isempty(A)
......@@ -609,13 +592,13 @@ classdef Symbolic < quantity.Function
end
if isnumeric(B)
C = quantity.Symbolic(A.sym() * B, ...
'grid', A(1).grid, 'variable', A(1).variable, ...
'domain', A(1).domain, ...
'name', [A(1).name, ' c']);
return
end
if isnumeric(A)
C = quantity.Symbolic(A * B.sym(), ...
'grid', B(1).grid, 'variable', B(1).variable, ...
'domain', B(1).domain, ...
'name', ['c ', B(1).name]);
return
end
......@@ -624,7 +607,7 @@ classdef Symbolic < quantity.Function
return;
end
parameters.domain = gridJoin(A.domain, B.domain);
parameters.domain = gridJoin(A(1).domain, B(1).domain);
parameters.name = [A(1).name, ' ', B(1).name];
parameters = misc.struct2namevaluepair(parameters);
......@@ -635,16 +618,32 @@ classdef Symbolic < quantity.Function
% minus() see quantity.Discrete.
assert(isequal(size(a), size(b)), 'terms must be of same size');
if ~isa(a, 'quantity.Discrete')
a = quantity.Symbolic(a, 'name', 'c');
if isnumeric(a)
a = quantity.Symbolic(a, 'name', 'c', 'domain', quantity.Domain.empty());
else
error('Not yet implemented')
end
parameters = struct(b(1));
else
if ~isa(b, 'quantity.Discrete')
b = quantity.Symbolic(b, 'name', 'c');
if isnumeric(b)
b = quantity.Symbolic(b, 'name', 'c', 'domain', quantity.Domain.empty());
else
error('Not yet implemented')
end
end
parameters = struct(a(1));
end
parameters.domain = gridJoin(a.domain, b.domain);
% remove the not required parameters:
for k = {'domain', 'grid', 'gridName', 'variable'}
if isfield(parameters, k)
parameters = rmfield(parameters, k);
end
end
% set new parameter values:
parameters.domain = gridJoin(a(1).domain, b(1).domain);
parameters.name = [a(1).name, '+' , b(1).name];
parameters = misc.struct2namevaluepair(parameters);
......@@ -654,56 +653,50 @@ classdef Symbolic < quantity.Function
function absQuantity = abs(obj)
% abs returns the absolut value of the quantity as a quantity
absQuantity = quantity.Symbolic(abs(obj.sym()), ...
'grid', obj(1).grid, 'variable', obj(1).variable, ...
'name', ['|', obj(1).name, '|']);
'name', ['|', obj(1).name, '|'], ...
'domain', obj(1).domain);
end % abs()
function y = real(obj)
% real() returns the real part of the obj.
y = quantity.Symbolic(real(obj.sym()), ...
'name', ['real(', obj(1).name, ')'], ...
'grid', obj(1).grid, ...
'variable', obj(1).variable);
'domain', obj(1).domain);
end % real()
function y = imag(obj)
% imag() returns the real part of the obj.
y = quantity.Symbolic(imag(obj.sym()), ...
'name', ['real(', obj(1).name, ')'], ...
'grid', obj(1).grid, ...
'variable', obj(1).variable);
'domain', obj(1).domain);
end % imag()
function y = inv(obj)
% inv inverts the matrix obj.
y = quantity.Symbolic(inv(obj.sym()), ...
'name', ['(', obj(1).name, ')^{-1}'], ...
'grid', obj(1).grid, ...
'variable', obj(1).variable);
'domain', obj(1).domain);
end % inv()
function y = exp(obj)
% exp() is the exponential function using obj as the exponent.
y = quantity.Symbolic(exp(obj.sym()), ...
'name', ['exp(', obj(1).name, ')'], ...
'grid', obj(1).grid, ...
'variable', obj(1).variable);
'domain', obj(1).domain);
end % exp()
function y = expm(obj)
% exp() is the matrix-exponential function using obj as the exponent.
y = quantity.Symbolic(expm(obj.sym()), ...
'name', ['expm(', obj(1).name, ')'], ...
'grid', obj(1).grid, ...
'variable', obj(1).variable);
'domain', obj(1).domain);
end % expm()
function y = ctranspose(obj)
% ctranspose() or ' is the complex conjugate tranpose
y = quantity.Symbolic(conj(obj.sym().'), ...
'name', ['{', obj(1).name, '}^{H}'], ...
'grid', obj(1).grid, ...
'variable', obj(1).variable);
'domain', obj(1).domain);
end % expm()
function C = int(obj, z, a, b)
......@@ -937,11 +930,7 @@ classdef Symbolic < quantity.Function
end
function f = setValueContinuous(f, symVar)
% converts symVar into a matlabFunction.
if isa(f, 'sym')
if nargin == 1
symVar = quantity.Symbolic.getVariable(f);
end
if isa(f, 'sym')
if isempty(symvar(f))
f = @(varargin) double(f) + quantity.Function.zero(varargin{:});
else
......
......@@ -590,7 +590,7 @@ syms z zeta
Z = linspace(0, pi, 51)';
ZETA = linspace(0, pi / 2, 71)';
P = quantity.Symbolic( sin(zeta) * z, 'grid', {Z, ZETA}, 'gridName', {'z', 'zeta'})
P = quantity.Symbolic( sin(zeta) * z, 'grid', {Z, ZETA}, 'gridName', {'z', 'zeta'});
B = quantity.Symbolic( sin(zeta), 'grid', ZETA, 'gridName', 'zeta');
PB = P*B;
......
......@@ -7,8 +7,11 @@ end
function testVec2Diag(testCase)
% quantity.Symbolic
syms z
myMatrixSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1, 0; 0, 0.9-z/2]);
myVectorSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1; 0.9-z/2]);
Z = quantity.Domain('grid', linspace(0, 1, 3), 'name', 'z');
myMatrixSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1, 0; 0, 0.9-z/2], ...
'domain', Z);
myVectorSymbolic = quantity.Symbolic([sin(0.5*z*pi)+1; 0.9-z/2], ...
'domain', Z);
testCase.verifyTrue( myVectorSymbolic.vec2diag.near(myMatrixSymbolic) );
end
......@@ -100,6 +103,7 @@ testCase.verifyEqual( f.on(f(1).grid, f(1).gridName), V.on(f(1).grid, f(1).gridN
fCumInt2Bcs = cumInt(integrandSymbolic, 's', 'zeta', 't');
fCumInt2Cum = cumInt(integrandSymbolic, 's', tGrid(1), 't') ...
- cumInt(integrandSymbolic, 's', tGrid(1), 'zeta');
testCase.verifyEqual(fCumInt2Bcs.on(fCumInt2Bcs(1).grid, fCumInt2Bcs(1).gridName), ...
fCumInt2Cum.on(fCumInt2Bcs(1).grid, fCumInt2Bcs(1).gridName), 'AbsTol', 100*eps);
end
......@@ -365,7 +369,7 @@ end
function testZero(testCase)
x = quantity.Symbolic(0);
x = quantity.Symbolic(0, 'domain', quantity.Domain.empty());
verifyEqual(testCase, x.on(), 0);
verifyTrue(testCase, misc.alln(on(x) * magic(5) == zeros(5)));
......@@ -424,9 +428,9 @@ function testMLRdivide2ConstantSymbolic(testCase)
syms z
assume(z>0 & z<1);
Lambda = quantity.Symbolic(eye(2), 'variable', z, ...
Lambda = quantity.Symbolic(eye(2), ...
'grid', linspace(0, 1), 'gridName', 'z', 'name', 'Lambda');
A = quantity.Symbolic(eye(2), 'variable', z, ...
A = quantity.Symbolic(eye(2), ...
'grid', linspace(0, 1), 'gridName', 'z', 'name', 'A');
C1 = A / Lambda;
C2 = A \ Lambda;
......@@ -451,7 +455,9 @@ end
function testCastToQuantity(testCase)
syms z t
x = quantity.Symbolic(sin(z * t * pi), 'grid', {linspace(0, 1).', linspace(0, 2, 200)});
x = quantity.Symbolic(sin(z * t * pi), ...
'grid', {linspace(0, 1).', linspace(0, 2, 200)}, ...
'gridName', {'z', 't'});
X1 = quantity.Discrete(x);
......@@ -460,25 +466,14 @@ verifyEqual(testCase, X1.on, x.on);
end
% function testmTimesFunctionHandleSymbolic(testCase)
%
% TODO repair this kind of multiplication!
%
% %
% f = quantity.Function(@(z) z, 'name', 'f');
% s = quantity.Symbolic(sym('z'), 'name', 'f');
% verifyTrue(testCase, isa(f*s, 'quantity.Function'));
%
% end
function testDiff(testCase)
%%
z = sym('z', 'real');
f1 = sinh(z * pi);
f2 = cosh(z * pi);
f = quantity.Symbolic([f1 ; f2 ], 'name', 'f');
f = quantity.Symbolic([f1 ; f2 ], 'name', 'f', 'domain', ...
quantity.Domain('grid', linspace(0,1,11), 'name', 'z'));
d2f = f.diff(2);
d1f = f.diff(1);
......@@ -502,23 +497,37 @@ verifyTrue(testCase, numeric.near(double([R3{:}]), f.diff(2).on(), 1e-12));
end
function testInit(testCase)
syms z
A = [0 1; 1 0];
z = linspace(0,1).';
t = linspace(0,1,101);
syms x y
v = [sin(x * y * pi); cos(x * y * pi)];
%% compute comparative solution
P1 = expm(A * z);
try
b = quantity.Symbolic(v, 'grid', {z, t}, 'gridName', {'z', 't'});
testCase.verifTrue(false);
end
b = quantity.Symbolic(v, 'grid', {z, t}, 'gridName', {'x', 'y'});
c = quantity.Symbolic(v, 'grid', {z, t}, 'gridName', {'x', 'y'});
%%
verifyTrue(testCase, misc.alln(b.on() == c.on()));
end
function testPlus(testCase)
syms z zeta
Z = quantity.Domain('grid', linspace(0, 1, 3), 'name', 'z');
Zeta = quantity.Domain('grid', linspace(0, 1, 4), 'name', 'zeta');
%% 1 + 2 variables
fSymVec3 = quantity.Symbolic([sinh(pi * z)], 'name', 'sinh');
fSymVec4 = quantity.Symbolic([cosh(zeta * pi * z)], 'name', 'cosh');
fSymVec3 = quantity.Symbolic([sinh(pi * z)], 'name', 'sinh', 'domain', Z);
fSymVec4 = quantity.Symbolic([cosh(zeta * pi * z)], 'name', 'cosh', ...
'domain', [Z Zeta]);
result = fSymVec3 - fSymVec4;
resultDiscrete = quantity.Discrete(fSymVec3) - quantity.Discrete(fSymVec4);
......@@ -527,7 +536,9 @@ resultDiscrete = quantity.Discrete(fSymVec3) - quantity.Discrete(fSymVec4);
testCase.verifyEqual(result.on(), resultDiscrete.on(), 'AbsTol', 10*eps);
%% quantity + constant
myQuan = quantity.Symbolic([sinh(pi * z), cosh(zeta * pi * z); 1, z^2], 'name', 'myQuan');
myQuan = quantity.Symbolic([sinh(pi * z), cosh(zeta * pi * z); 1, z^2], ...
'name', 'myQuan', 'domain', [Z Zeta]);
myQuanPlus1 = myQuan + ones(size(myQuan));
my1PlusQuan = ones(size(myQuan)) + myQuan;
......@@ -537,7 +548,8 @@ testCase.verifyEqual(myQuan.on()+1, my1PlusQuan.on(), 'AbsTol', 10*eps);
%% 1 variable
fSymVec = quantity.Symbolic([sinh(z * pi) ; cosh(z * pi)], 'name', 'sinhcosh');
fSymVec = quantity.Symbolic([sinh(z * pi) ; cosh(z * pi)], ...
'domain', Z, 'name', 'sinhcosh');
F = fSymVec + fSymVec;
val = F.on();
......@@ -556,7 +568,8 @@ syms zeta
f1fun = @(z, zeta) sinh(z * pi);
f2fun = @(z, zeta) cosh(zeta * pi) + 0*z;
fSymVec = quantity.Symbolic([sinh(z * pi); cosh(zeta * pi)], 'name', 'sinhcosh');
fSymVec = quantity.Symbolic([sinh(z * pi); cosh(zeta * pi)], ...
'domain', [Z Zeta], 'name', 'sinhcosh');
fSymVec2 = [fSymVec(2); fSymVec(1)];
F = fSymVec + fSymVec;
......@@ -579,7 +592,8 @@ syms z
f1 = sinh(z * pi);
f2 = cosh(z * pi);
f = quantity.Symbolic([f1 ; f2 ], 'name', 'sinhcosh');
f = quantity.Symbolic([f1 ; f2 ], 'name', 'sinhcosh', 'domain', ...
quantity.Domain('grid', linspace(0,1,3), 'name', 'z'));
F = f * f.';
val = F.on();
......@@ -605,7 +619,7 @@ end
function testVariable(testCase)
syms z
q = quantity.Symbolic(z);
q = quantity.Symbolic(z, 'domain', quantity.Domain('grid', 1, 'name', 'z'));
verifyEqual(testCase, q.variable, z);
......@@ -626,7 +640,7 @@ values = f.on();
%%
testCase.verifyEqual(on( -f ), -values);
testCase.verifyEqual(on( + ( - f ) ), values );