Commit 9cdda192 authored by Ferdinand Fischer's avatar Ferdinand Fischer
Browse files

Change type of quantity.Domain.name from char to string.

Change the parameter order of quantity.*/diff
Now, the domain name wrt which the derivative should be taken is the second argument and the order of the derivative is the third.

speed up of the unittest utilizing setupOnce
parent 3378086d
function [str] = ensureString(chr)
if iscell( chr )
if ~all( cellfun( @ischar, chr ) )
chr = [chr{:}];
end
end
if isstring( chr )
str = chr;
else
str = convertCharsToStrings( chr );
end
end
function gridName( name )
name = misc.ensureString( name );
assert( isstring( name ), 'A grid name should be a string-array')
end
classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mixin.Copyable & matlab.mixin.CustomDisplay
classdef (InferiorClasses = {?quantity.Symbolic}) Discrete ...
< handle & matlab.mixin.Copyable & matlab.mixin.CustomDisplay
properties (SetAccess = protected)
% Discrete evaluation of the continuous quantity
......@@ -79,7 +80,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
%% input parser
myParser = misc.Parser();
myParser.addParameter('name', string(), @isstr);
myParser.addParameter('name', "", @mustBe.gridName);
myParser.addParameter('figureID', 1, @isnumeric);
myParser.parse(varargin{:});
......@@ -143,9 +144,9 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
%---------------------------
function gridName = get.gridName(obj)
if isempty(obj.domain)
gridName = {};
gridName = [];
else
gridName = {obj.domain.name};
gridName = [obj.domain.name];
end
end
......@@ -316,21 +317,18 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
% for this, find the index of the common domain in list of
% temporary combined domain
intersectDomain = intersect( {originalDomain( ~logOfDomain ).name}, ...
{g(1).domain.name} );
intersectDomain = intersect( originalDomain( ~logOfDomain ), ...
g(1).domain );
if ~isempty(intersectDomain)
idx = 1:length(tmpDomain);
logCommon = strcmp({tmpDomain.name}, intersectDomain);
idx = tmpDomain.gridIndex( intersectDomain );
% take the diagonal values of the common domain, i.e., z = zeta
% use the diag_nd function because it seems to be faster
% then the diagNd function, although the values must be
% sorted.
% #TODO: Rewrite the diagNd function, using for loops, in order to be as fast as diag_nd
newValues = permute( newValues, [idx(logCommon), idx(~logCommon)]);
newValues = misc.diag_nd(newValues);
newValues = misc.diagNd(newValues, idx);
end
% *) build a new valueDiscrete on the correct grid.
......@@ -398,14 +396,10 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
% Since the order of the domains is not neccessarily equal to the
% order in obj(1).domain, this is more involved:
myDomain = misc.ensureIsCell(myDomain);
gridNames = misc.ensureIsCell(gridNames);
gridNames = misc.ensureString(gridNames);
assert(all(cellfun(@(v)isvector(v), myDomain)), ...
'The cell entries for a new grid have to be vectors')
assert(iscell(gridNames), ...
'The gridNames parameter must be cell array')
assert(all(cellfun(@ischar, gridNames)), ...
'The gridNames must be strings')
newGrid = myDomain;
myDomain = quantity.Domain.empty();
......@@ -1015,7 +1009,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
function [idx, logicalIdx] = gridIndex(obj, varargin)
warning('DEPRICATED: use quantity.Domain.gridIndex method instead')
[idx, logicalIdx] = obj(1).domain.gridIndex(varargin{:});
[~, idx, logicalIdx] = obj(1).domain.find(varargin{:});
end
function value = at(obj, point)
......@@ -1305,19 +1299,20 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
% corresponding domain from DOMAIN.
%
% example:
% q.changeGrid( linspace(0,1)', 't')
% will change the grid with the name 't' to the new grid linspace(0,1)'
% q.changeGrid( linspace(0,1)', 't')
% will change the grid with the name 't' to the new grid
% linspace(0,1)'
if isempty(obj)
newObj = obj.copy();
return;
end
if isa(gridNew, 'quantity.Domain')
gridNameNew = {gridNew.name};
gridNameNew = [gridNew.name];
gridNew = {gridNew.grid};
else
gridNameNew = misc.ensureString(gridNameNew);
gridNew = misc.ensureIsCell(gridNew);
gridNameNew = misc.ensureIsCell(gridNameNew);
end
if obj(1).isConstant
......@@ -1325,7 +1320,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
newDomain(1:length( gridNew )) = quantity.Domain();
for it = 1 : length(gridNew)
newDomain(it) = ...
quantity.Domain(gridNameNew{it}, gridNew{it});
quantity.Domain(gridNameNew(it), gridNew{it});
end
else
gridIndexNew = obj(1).domain.gridIndex(gridNameNew);
......@@ -1333,9 +1328,9 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
for it = 1 : length(gridIndexNew)
newDomain(gridIndexNew(it)) = ...
quantity.Domain(gridNameNew{it}, gridNew{it});
quantity.Domain(gridNameNew(it), gridNew{it});
end
assert(isequal({newDomain.name}, obj(1).gridName), ...
assert(isequal([newDomain.name], obj(1).gridName), ...
'rearranging grids failed');
end
......@@ -1661,17 +1656,13 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
% Optionally, a weight can be defined, such that instead
% xNorm = sqrt(int_0^1 x.' * weight * x dz).
% The integral domain is specified by integralGrid.
arguments
obj;
integralGridName = {obj(1).domain.name};
integralGridName {mustBe.gridName} = [obj(1).domain.name];
optArg.weight = eye(size(obj, 1));
end
integralGridName = misc.ensureIsCell(integralGridName);
assert(all(ischar([integralGridName{:}])), ...
'integralGrid must specify a gridName as a char');
integralGridName = misc.ensureString(integralGridName);
if obj.nargin == 1 && all(strcmp(obj(1).gridName, integralGridName))
xNorm = sqrtm(on(int(obj.' * optArg.weight * obj), integralGridName));
......@@ -1844,16 +1835,16 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
end
end
function result = diff(obj, k, diffGridName)
function result = diff(obj, diffGridName, k)
% DIFF computation of the derivative
% result = DIFF(obj, k, diffGridName) applies the
% 'k'th-derivative for the variable specified with the input
% 'diffGridName' to the obj. If no 'diffGridName' is specified,
% then diff applies the derivative w.r.t. all gridNames.
if nargin == 1 || isempty(k)
k = 1; % by default, only one derivatve per diffGridName is applied
else
assert(isnumeric(k) && (round(k) == k))
arguments
obj;
diffGridName (1,1) string = obj(1).gridName;
k uint64 = 1;
end
if obj.isConstant && isempty(obj(1).gridName)
......@@ -1864,11 +1855,6 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
return
end
if nargin < 3 % if no diffGridName is specified, then the derivative
% w.r.t. all gridNames is applied
diffGridName = obj(1).gridName;
end
if isa(diffGridName, 'quantity.Domain')
% a quantity.Domain is used instead of a grid name
% -> get the grid name from the domain object
......@@ -1880,22 +1866,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
diffGridName = {diffGridName.name};
end
% diff for each element of diffGridName (this is rather
% inefficient, but an easy implementation of the specification)
if iscell(diffGridName) || isempty(diffGridName)
if numel(diffGridName) == 0 || isempty(diffGridName)
result = copy(obj);
else
result = obj.diff(k, diffGridName{1}); % init result
for it = 2 : numel(diffGridName)
result = result.diff(k, diffGridName{it});
end
end
elseif k == 0
result = obj;
else
result = obj.diff_inner(k, diffGridName);
end
result = obj.diff_inner(k, diffGridName);
end
function I = int(obj, varargin)
......@@ -2323,8 +2294,8 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
end
function result = diff_inner(obj, k, diffGridName)
gridSelector = strcmp(obj(1).gridName, diffGridName);
gridSelectionIndex = find(gridSelector);
gridSelectionIndex = obj(1).domain.gridIndex(diffGridName);
spacing = gradient(obj(1).grid{gridSelectionIndex}, 1);
assert(numeric.near(spacing, spacing(1)), ...
......@@ -2350,9 +2321,9 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
'name', ['(d_{', diffGridName, '}', obj(1).name, ')']);
if k > 1
% % if a higher order derivative is requested, call the function
% % recursivly until the first-order derivative is reached
result = result.diff(k-1, diffGridName);
% if a higher order derivative is requested, call the function
% recursivly until the first-order derivative is reached
result = result.diff(diffGridName, k-1);
end
end
......@@ -2360,13 +2331,18 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
% Computes the required permutation vectors to use
% misc.multArray for multiplication of matrices
uA = unique(a(1).gridName, 'stable');
assert(numel(uA) == numel(a(1).gridName), 'Gridnames have to be unique!');
uB = unique(b(1).gridName, 'stable');
assert(numel(uB) == numel(b(1).gridName), 'Gridnames have to be unique!');
% #todo this assertion should not be required!
% uA = unique(a(1).gridName, 'stable');
% assert(numel(uA) == numel(a(1).gridName), 'Gridnames have to be unique!');
% uB = unique(b(1).gridName, 'stable');
% assert(numel(uB) == numel(b(1).gridName), 'Gridnames have to be unique!');
% 1) find common entries
common = intersect(a(1).gridName, b(1).gridName);
if isempty( b(1).gridName ) || isempty( a(1).gridName )
common = [];
else
common = intersect(a(1).gridName, b(1).gridName);
end
commonA = false(1, a.nargin());
commonB = false(1, b.nargin());
......
......@@ -11,7 +11,7 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
% a speaking name for this domain; Should be unique, so that the
% domain can be identified by the name.
name char;
name string = "";
n (1,1) double {mustBeInteger}; % number of discretization points == gridLength
end
......@@ -96,7 +96,7 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
s = [obj.n];
end
function d = find(obj, domain, varargin)
function d = find(obj, searchName)
% FIND a domain in object-array of quantity.Domain
%
% d = find(OBJ, NAME) returns the domain with the name NAME
......@@ -111,30 +111,56 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
%
% d = find(OBJ, { NAME or DOMAIN}) the NAME or the DOMAIN to
% be found can also be defined by cell-array.
names = {obj.name};
if isa(domain, 'quantity.Domain')
searchName = {domain.name};
elseif ischar(domain)
searchName = {domain};
elseif iscell(domain)
searchName = domain;
else
error('Not implemented yet')
arguments
obj quantity.Domain;
end
arguments (Repeating)
searchName string;
end
d = obj( obj.gridIndex( searchName ) );
end
function [idx, log] = gridIndex(obj, searchName, position)
%% GRIDINDEX returns the index of the grid
% [idx, log] = gridIndex(obj, names) searches in the name
% properties of obj for the "names" and returns its index as
% "idx" and its logical index as "log"
arguments
obj
searchName = [obj.name];
position string = "all";
end
if numel(searchName) > 1
d = obj.find(searchName{:});
return
if isa(searchName, 'quantity.Domain')
searchName = [searchName.name];
end
d = obj(strcmp(names, searchName));
objNames = [obj.name];
if isempty(objNames)
idx = 0;
log = [];
else
searchName = misc.ensureString( searchName );
log = false(size(objNames));
counter = 1:numel(objNames);
idx = [];
for k = 1 : numel(searchName)
log_ = objNames == searchName(k);
log = log | log_;
idx = [idx counter( log_ )];
end
if isempty(idx)
idx = 0;
end
end
for it = 1 : numel(varargin)
d = [d, find(obj, varargin{:})];
if position == "first"
idx = idx(1);
end
end
end % gridIndex
function l = ne(obj, obj2)
% ~= Not equal.
l = ~(obj == obj2);
......@@ -155,27 +181,17 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
return;
end
[joinedGrid, index] = unique({domain1.name, domain2.name}, 'stable');
[joinedGrid, index] = unique([domain1.name, domain2.name], 'stable');
joinedDomain(1 : numel(joinedGrid)) = quantity.Domain();
% #todo@domain: the gird comparison seems to be very
% complicated
% check for each grid if it is in the domain of obj1 or obj2 or
% both
for i = 1 : numel(joinedGrid)
currentGridName = joinedGrid{i};
[index1, logicalIndex1] = domain1.gridIndex(currentGridName);
[index2, logicalIndex2] = domain2.gridIndex(currentGridName);
%
% if ~any(logicalIndex1)
% joinedDomain(i) = obj2(index2);
% elseif ~any(logicalIndex2)
% joinedDomain(i) = obj1(index1);
% else
%
currentGridName = joinedGrid(i);
[index1, logicalIndex1] = domain1.gridIndex(currentGridName, "first");
[index2, logicalIndex2] = domain2.gridIndex(currentGridName, "first");
% Check if a domain is in both domains:
% -> then take the finer one of both
if any(logicalIndex1) && any(logicalIndex2)
......@@ -192,7 +208,7 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
end
% If it is not in both, -> just take the normal grid
elseif any(logicalIndex1)
joinedDomain(i) = domain1(index1);
joinedDomain(i) = domain1(index1(1));
elseif any(logicalIndex2)
joinedDomain(i) = domain2(index2);
end
......@@ -204,41 +220,7 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
[joinedDomain, index] = obj1.join(obj2);
end % gridJoin()
function [idx, logicalIdx] = gridIndex(obj, names)
%% GRIDINDEX returns the index of the grid
% [idx, log] = gridIndex(obj, names) searches in the name
% properties of obj for the "names" and returns its index as
% "idx" and its logical index as "log"
arguments
obj
names = {obj.name};
end
if isa(names, 'quantity.Domain')
names = {names.name};
end
names = misc.ensureIsCell(names);
idx = zeros(1, length(names));
nArgIdx = 1:obj.ndims();
logicalIdx = false(1, obj.ndims());
for k = 1:length(names)
log = strcmp({obj.name}, names{k});
logicalIdx = logicalIdx | log;
if any(log)
% #todo@domain: if the index for a grid name is searched in a
% list with multiple grids but same names, only the first
% occurrence is returned. this should be changed...
tmpFirst = nArgIdx(log);
idx(k) = tmpFirst(1);
else
idx(k) = 0;
end
end
end % gridIndex
function i = isempty(obj)
i = any(size(obj) == 0);
......@@ -269,14 +251,14 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
function [idx, newDomain] = getPermutationIdx(obj, order)
if isa(order, 'quantity.Domain')
names = {order.name};
elseif ischar([order{:}])
names = order;
names = [order.name];
elseif iscell(order) || ischar(order) || isstring(order)
names = misc.ensureString( order );
else
error('the input parameter order must be a array of quantity.Domain objects or a cell-array with string')
error('the input parameter order must be a array of quantity.Domain objects or a string-array')
end
idx = cellfun(@(v) obj.gridIndex(v), names);
idx = cellfun(@(v) obj.gridIndex(v), names); % #todo@domainNameString
if isa(order, 'quantity.Domain')
newDomain = order;
......@@ -302,7 +284,7 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
% only sort the grids if there is something to sort
if obj.ndims > 1
gridNames = {obj.name};
gridNames = [obj.name];
% this is the default case for ascending alphabetical
% order
......@@ -404,7 +386,7 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
%% domain parser
domainParser = misc.Parser();
domainParser.addParameter('domain', {}, @(g) isa(g, 'quantity.Domain'));
domainParser.addParameter('gridName', '', @(g) ischar(g) || iscell(g));
domainParser.addParameter('gridName', '');
domainParser.addParameter('grid', [], @(g) isnumeric(g) || iscell(g));
domainParser.parse(varargin{:});
......@@ -418,7 +400,7 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
% -> initialize quantity.Domain objects with the
% specified values
myGridName = misc.ensureIsCell(domainParser.Results.gridName);
myGridName = misc.ensureString(domainParser.Results.gridName);
myGrid = misc.ensureIsCell(domainParser.Results.grid);
assert(isequal(numel(myGrid), numel(myGridName)), ...
......@@ -427,7 +409,7 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
% initialize the domain objects
myDomain = quantity.Domain.empty();
for k = 1:numel(myGrid)
myDomain(k) = quantity.Domain(myGridName{k}, myGrid{k});
myDomain(k) = quantity.Domain(myGridName(k), myGrid{k});
end
else
% else case: the domains are specified as domain
......@@ -435,7 +417,7 @@ classdef (InferiorClasses = {?quantity.EquidistantDomain}) Domain < ...
myDomain = domainParser.Results.domain;
end
assert(numel(myDomain) == numel(unique({myDomain.name})), ...
assert(misc.isunique([myDomain.name]), ...
'The names of the domain must be unique');
unmatched = domainParser.UnmatchedNameValuePair;
......
......@@ -31,21 +31,21 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Piecewise < quantity.Discrete
% ensure all have the same domain
d = quantities{1}(1).domain;
assert( all( cellfun(@(q) all( strcmp( {d.name}, {q(1).domain.name} ) ), quantities) ) , ...
assert( all( cellfun(@(q) all( strcmp( [d.name], [q(1).domain.name] ) ), quantities) ) , ...
'The quantities for the piecewise combination must have the same domain names' );
assert(length(quantities) >= 2, 'Only one quantity is given for the piecewise combination. At least 2 are required.');
assert( quantities{1}(1).domain.gridIndex(domainToJoin) == 1, ...
'The domain to join must be the first domain!');
joinedGrid = quantities{1}(1).domain.find(domainToJoin).grid;
joinedGrid = quantities{1}(1).domain.find(domainToJoin.name).grid;
joinedValues = quantities{1}.on();
% ensure the domains fit to each other and then concatenate
% them
for k = 2:length(quantities)
dkm1 = quantities{k-1}(1).domain.find(domainToJoin);
dk = quantities{k}(1).domain.find(domainToJoin);
dkm1 = quantities{k-1}(1).domain.find(domainToJoin.name);
dk = quantities{k}(1).domain.find(domainToJoin.name);
assert(dkm1.upper == dk.lower, 'Domains do not connect to each other');
......
......@@ -70,10 +70,10 @@ classdef Symbolic < quantity.Function
% variable
if isa(fun{k}, 'sym')
symb{k} = fun{k};
fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, {myDomain.name});
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}, {myDomain.name});
fun{k} = quantity.Symbolic.setValueContinuous(symb{k}, [myDomain.name]);
elseif isa(fun{k}, 'function_handle')
symb{k} = sym(fun{k});
else
......@@ -299,10 +299,10 @@ classdef Symbolic < quantity.Function
else
gridName = {gridName.name};
end
else
gridName = misc.ensureIsCell(gridName);
end
gridName = misc.ensureString(gridName);
if nargin == 3 && isa(values, 'quantity.Domain')
% replacement of the grid AND the gridName
% 1) replace the grid
......@@ -314,14 +314,6 @@ classdef Symbolic < quantity.Function
values = misc.ensureIsCell(values);
end
% % ensure that domains which should be replaced are known by the
% % object
% for k = 1:length(values)
% if ischar(values{k})
% assert( any( strcmp( obj(1).gridName, values{k} ) ), 'The domain name to be replaced must be a domain name of the object' );
% end
% end
isNumericValue = cellfun(@isnumeric, values);
if any((cellfun(@(v) numel(v(:)), values)>1) & isNumericValue)
error('only implemented for one value per grid');
......@@ -344,13 +336,13 @@ classdef Symbolic < quantity.Function
for it = 1 : numel(values)
if ~isNumericValue(it) && ~isempty(obj(1).gridName(~selectRemainingGrid))
% check if there is a symbolic value and if this value exists in the object
if ischar(values{it})
newGridName{strcmp(obj(1).gridName(~selectRemainingGrid), gridName{it})} ...
if isstring( values{it} ) || ischar( values{it} )
newGridName( strcmp(obj(1).gridName(~selectRemainingGrid), gridName(it)) ) ...
= values{it};
elseif isa(values{it}, 'sym')
gridNameTemp = symvar(values{it});
assert(numel(gridNameTemp) == 1, 'replacing one gridName with 2 gridName is not supported');
newGridName{strcmp(obj(1).gridName(~selectRemainingGrid), gridName{it})} ...
newGridName{strcmp(obj(1).gridName(~selectRemainingGrid), gridName(it))} ...
= char(gridNameTemp);
else
......@@ -359,7 +351,7 @@ classdef Symbolic < quantity.Function
end
end
symbolicSolution = subs(obj.sym(), gridName, values);
symbolicSolution = subs(obj.sym(), cellstr(gridName), values);
if isempty(symvar(symbolicSolution(:)))
% take the new values that are not numeric as the new
......@@ -374,7 +366,7 @@ classdef Symbolic < quantity.Function
end
for it = 1 : numel(newGridName)
if ~isempty(charValues) && any(contains(charValues, newGridName{it}))
newGrid{it} = obj.gridOf(gridName{strcmp(values, newGridName{it})});
newGrid{it} = obj.gridOf(gridName( strcmp(values, newGridName{it}) ));