Commit 50afa926 authored by Ferdinand Fischer's avatar Ferdinand Fischer
Browse files

First version of the quantity.domain object.

parent 532c63d3
......@@ -6,13 +6,15 @@ classdef Parser < inputParser
methods
function obj = Parser()
function obj = Parser(varargin)
obj = obj@inputParser();
% set default properties
obj.KeepUnmatched = true;
obj.CaseSensitive = true;
obj.PartialMatching = false;
end % Parser()
function i = isDefault(obj, name)
......
function value = ensureIsCell(value)
%ENSUREISCELL ensures that the value is a cell.
% c = ensureIsCell(value) checks if value is a cell. If it is not a cell,
% it is converted as a cell.
if ~iscell(value)
value = {value};
end
end
......@@ -2,9 +2,6 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
properties (SetAccess = protected)
% Discrete evaluation of the continuous quantity
valueDiscrete double;
% Name of the domains that generate the grid.
gridName {mustBe.unique};
end
properties (Hidden, Access = protected, Dependent)
......@@ -12,6 +9,21 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
end
properties
% ID of the figure handle in which the handle is plotted
figureID double = 1;
% Name of this object
name char;
% domain
domain;
end
properties ( Dependent )
% Name of the domains that generate the grid.
gridName {mustBe.unique};
% Grid for the evaluation of the continuous quantity. For the
% example with the function f(x,t), the grid would be
% {[<spatial domain>], [<temporal domain>]}
......@@ -19,12 +31,6 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
% spatial domain and <temporal domain> the discrete description of
% the temporal domain.
grid; % in set.grid it is ensured that, grid is a (1,:)-cell-array
% ID of the figure handle in which the handle is plotted
figureID double = 1;
% Name of this object
name char;
end
methods
......@@ -32,10 +38,18 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
% --- Constructor ---
%--------------------
function obj = Discrete(valueOriginal, varargin)
% The constructor requires valueOriginal to be
% DISCRETE a quantity, represented by discrete values.
% obj = Discrete(valueOriginal, varargin) initializes a
% quantity. The parameters to be set are:
% 'valueOrigin' must be
% 1) a cell-array of double arrays with
% size(valueOriginal) == size(obj) and
% size(valueOriginal{it}) == gridSize
% Example: valueOrigin = { f(Z, T), g(Z, T) } is a cell array
% wich contains the functions f(z,t) and g(z,t) evaluated on
% the discrete domain (Z x T). Then, the name-value-pair
% parameter 'domain' must be set with quantity.Domain
% objects, according to the domains Z and T.
% OR
% 2) adouble-array with
% size(valueOriginal) == [gridSize, size(quantity)]
......@@ -64,106 +78,122 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
%% input parser
myParser = misc.Parser();
myParser.addParameter('gridName', [], @(g) ischar(g) || iscell(g));
myParser.addParameter('grid', [], @(g) isnumeric(g) || iscell(g));
myParser.addParameter('name', string(), @isstr);
myParser.addParameter('figureID', 1, @isnumeric);
myParser.parse(varargin{:});
assert(all(~contains(myParser.UsingDefaults, 'gridName')), ...
'gridName is a mandatory input for quantity');
if iscell(myParser.Results.gridName)
myGridName = myParser.Results.gridName;
%% 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
assert(iscell(valueOriginal), 'If no grid is specified, valueOriginal must be a cell-array')
valueOriginalGridSize = size(valueOriginal{1});
myDomain = quantity.Domain.defaultGrid(valueOriginalGridSize);
elseif domainParser.isDefault('domain') && ...
domainParser.isDefault('grid')
% case 2: the gridNames are specified
% -> use default grid with specified names
assert(iscell(valueOriginal), 'If no grid is specified, valueOriginal must be a cell-array')
valueOriginalGridSize = size(valueOriginal{1});
myDomain = quantity.Domain.defaultGrid(valueOriginalGridSize, ...
misc.ensureIsCell(domainParser.Results.gridName));
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
myGridName = {myParser.Results.gridName};
% else case: the domains are specified as domain
% objects.
myDomain = domainParser.Results.domain;
end
%% allow initialization of empty objects:
% #TODO check uniqueness of gridNames
%% get the sizes of obj and grid
gridLength = myDomain.gridLength;
% convert double valued valueOriginal to cell-valued value
% original
if ~iscell(valueOriginal)
valueOriginal = quantity.Discrete.value2cell(valueOriginal, gridLength);
end
% Check if the grid fits to the values. In addition, catch
% the case if all values are empty. This is required for
% the initialization of quantity.Function and
% quantity.Symbolic objects
assert( numGridElements(myDomain) == numel(valueOriginal{1}) || ...
misc.alln( cellfun(@isempty, valueOriginal ) ), ...
'grids do not fit to valueOriginal');
% allow initialization of empty objects
valueOriginalSize = size(valueOriginal);
if any(valueOriginalSize == 0)
% If the size is specified in the arguements, it should
% be chosen instead of the default size from the
% valueOriginal.
myParser = misc.Parser();
myParser.addParameter('size', valueOriginalSize((1+numel(myGridName)):end));
myParser.addParameter('size', valueOriginalSize((1+ndims(myDomain)):end));
myParser.parse(varargin{:});
obj = quantity.Discrete.empty(myParser.Results.size);
return;
end
%% get the sizes of obj and grid
if iscell(valueOriginal)
if isempty(valueOriginal{1})
% if valueOriginal is a cell-array with empty
% cells, then grid must be specified as an input
% parameter. This case is important for
% constructing Symbolic or Function quantities
% without discrete values.
assert(all(~contains(myParser.UsingDefaults, 'grid')), ...
['grid is a mandatory input for quantity, ', ...
'if no discrete values are specified']);
if ~iscell(myParser.Results.grid)
gridSize = numel(myParser.Results.grid);
% set valueDiscrete
for k = 1:numel(valueOriginal)
if numel(myDomain) == 1
% TODO: Which case is this? Why does it need extra
% treatment?
obj(k).valueDiscrete = valueOriginal{k}(:); %#ok<AGROW>
else
gridSize = cellfun(@(v) numel(v), myParser.Results.grid);
end
else
gridSize = size(valueOriginal{1});
end
objSize = size(valueOriginal);
elseif isnumeric(valueOriginal)
gridSize = valueOriginalSize(1 : numel(myGridName));
objSize = [valueOriginalSize(numel(myGridName)+1 : end), 1, 1];
end
%% get grid and check size
if any(contains(myParser.UsingDefaults, 'grid'))
myGrid = quantity.Discrete.defaultGrid(gridSize);
else
myGrid = myParser.Results.grid;
end
if ~iscell(myGrid)
myGrid = {myGrid};
end
if isempty(myGridName) || isempty(myGrid)
if ~(isempty(myGridName) && isempty(myGrid))
error(['If one of grid and gridName is empty, ', ...
'then both must be empty.']);
end
else
assert(isequal(size(myGrid), size(myGridName)), ...
'number of grids and gridNames must be equal');
myGridSize = cellfun(@(v) numel(v), myGrid);
assert(isequal(gridSize(gridSize>1), myGridSize(myGridSize>1)), ...
'grids do not fit to valueOriginal');
end
%% set valueDiscrete
if ~iscell(valueOriginal)
valueOriginal = quantity.Discrete.value2cell(valueOriginal, gridSize, objSize);
end
for k = 1:prod(objSize)
if numel(myGrid) == 1
obj(k).valueDiscrete = valueOriginal{k}(:);
else
obj(k).valueDiscrete = valueOriginal{k};
obj(k).valueDiscrete = valueOriginal{k}; %#ok<AGROW>
end
end
%% set further properties
[obj.grid] = deal(myGrid);
[obj.gridName] = deal(myGridName);
[obj.domain] = deal(myDomain);
[obj.name] = deal(myParser.Results.name);
[obj.figureID] = deal(myParser.Results.figureID);
%% reshape object from vector to matrix
obj = reshape(obj, objSize);
obj = reshape(obj, size(valueOriginal));
end
end% Discrete() constructor
%---------------------------
% --- getter and setters ---
%---------------------------
function gridName = get.gridName(obj)
gridName = {obj.domain.name};
end
function grid = get.grid(obj)
grid = {obj.domain.grid};
end
function itIs = isConstant(obj)
% the quantity is interpreted as constant if it has no grid or
% it has a grid that is only one point.
......@@ -684,12 +714,12 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
solution = objInverseTemp.on(rhs);
% solution = zeros(numel(obj), 1);
% for it = 1 : numel(obj)
% objInverseTemp = obj(it).invert(gridName);
% solution(it) = objInverseTemp.on(rhs(it));
% end
% solution = reshape(solution, size(obj));
% solution = zeros(numel(obj), 1);
% for it = 1 : numel(obj)
% objInverseTemp = obj(it).invert(gridName);
% solution(it) = objInverseTemp.on(rhs(it));
% end
% solution = reshape(solution, size(obj));
end
function inverse = invert(obj, gridName)
......@@ -1797,8 +1827,8 @@ 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
% % if a higher order derivative is requested, call the function
% % recursivly until the first-order derivative is reached
result = result.diff(k-1, diffGridName);
end
end
......@@ -1842,9 +1872,9 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
if nargin == 4
I = cumInt( obj, varargin{:});
return;
%
% assert(all((varargin{2} == a(idxGrid)) & (varargin{3} == b(idxGrid))), ...
% 'only integration from beginning to end of domain is implemented');
%
% assert(all((varargin{2} == a(idxGrid)) & (varargin{3} == b(idxGrid))), ...
% 'only integration from beginning to end of domain is implemented');
end
%% do the integration over one dimension
......@@ -1858,6 +1888,8 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
myGrid = currentGrid(domainIdx{idxGrid});
for kObj = 1:numel(obj)
J = numeric.trapz_fast_nDim(myGrid, obj(kObj).atIndex(domainIdx{:}), idxGrid);
......@@ -1975,12 +2007,12 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
oldDim = ndims(valDiscrete);
valDiscrete = permute(valDiscrete, [(1:sum(~gridLogical)) + oldDim, 1:oldDim] );
valDiscrete = repmat(valDiscrete, [gridJoinedLength(~gridLogical), ones(1, ndims(valDiscrete))]);
%
%
valDiscrete = reshape(valDiscrete, ...
[gridJoinedLength(~gridLogical), gridJoinedLength(gridLogical), size(obj)]);
% % permute valDiscrete such that grids are in the order specified
% % by gridNameJoined.
% % permute valDiscrete such that grids are in the order specified
% % by gridNameJoined.
gridIndex = 1:numel(gridLogical);
gridOrder = [gridIndex(~gridLogical), gridIndex(gridLogical)];
valDiscrete = permute(valDiscrete, [gridOrder, numel(gridLogical)+(1:ndims(obj))]);
......@@ -2187,26 +2219,19 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi
function q = value2cell(value, gridSize, valueSize)
% VALUE2CELL
gs = num2cell(gridSize(:));
vs = arrayfun(@(n) ones(n, 1), valueSize, 'UniformOutput', false);
s = [gs(:); vs(:)]; %{gs{:}, vs{:}};%
q = reshape(mat2cell(value, s{:}), valueSize);
end
function g = defaultGrid(gridSize)
fullSize = size(value);
g = cell(1, length(gridSize));
for k = 1:length(gridSize)
myGridSize = num2cell( fullSize(1:length(gridSize)) );
o = ones(1, length(gridSize) + 1); % + 1 is required to deal with one dimensional grids
o(k) = gridSize(k);
O = ones(o);
O(:) = linspace(0, 1, gridSize(k));
g{k} = O;
end
if nargin == 2
valueSize = [fullSize(length(myGridSize)+1:length(fullSize)), 1];
end
myValueSize = arrayfun(@(n) ones(n, 1), valueSize, 'UniformOutput', false);
s = [myGridSize(:); myValueSize(:)]; %{gs{:}, vs{:}};%
q = reshape(mat2cell(value, s{:}), valueSize);
end
function [p, q, parameters, gridSize] = inputNormalizer(a, b)
......
classdef Domain
%DOMAIN class to describes a range of values on which a function can be defined.
% todo:
% * EquidistantDomain
% * multi dimensional
properties
% The discrete points of the grid for the evaluation of a
% continuous quantity. For an example, the function f(x) should be
% considered on the domain x \in X = [0, 1]. Then, a grid can be
% generated by X_grid = linspace(0, 1).
grid double {mustBeReal};
% a speaking name for this domain; Should be unique, so that the
% domain can be identified by the name.
name char;
end
properties (Dependent)
n; % number of discretization points
lower; % lower bound of the domain
upper; % upper bound of the domain
end
methods
function obj = Domain(varargin)
%DOMAIN initialize the domain
%
if nargin >= 1
parser = misc.Parser();
parser.addParameter('grid', [], @isvector);
parser.addParameter('name', '', @ischar);
parser.parse(varargin{:});
% todo: assertions
% * ascending ?
obj.grid = parser.Results.grid(:);
obj.name = parser.Results.name;
else
obj = quantity.Domain.empty();
end
end
function n = get.n(obj)
n = length(obj.grid);
end
function lower = get.lower(obj)
lower = min( obj.grid );
end
function upper = get.upper(obj)
upper = max( obj.grid );
end
function nd = ndims(obj)
%NDIMS number of dimensions of the domain specified by the
%object-array.
nd = size(obj(:), 1);
end
function n = numGridElements(obj)
% NUMGRIDLEMENTS returns the number of the elements of the grid
n = prod([obj.n]);
end
function s = gridLength(obj)
%GRIDLENGTH number of discretization points for each grid in the
%object-array.
s = [obj.n];
end
end
methods (Static)
function g = defaultGrid(gridSize, name)
if nargin == 1
% If no names are specified, chose x_1, x_2, ... as default
% names.
name = cell(1, length(gridSize));
for k = 1:length(gridSize)
name{k} = ['x_' num2str(k)];
end
end
% generate a default gird with given sizes
g = quantity.Domain.empty();
for k = 1:length(gridSize)
o = ones(1, length(gridSize) + 1); % + 1 is required to deal with one dimensional grids
o(k) = gridSize(k);
O = ones(o);
O(:) = linspace(0, 1, gridSize(k));
g(k) = quantity.Domain('grid', O, 'name', name{k});
end
end
end
end
classdef EquidistantDomain < quantity.Domain
%EQUIDISTANTDOMAIN class to handle the discretization of the range of
%definition of a function. The discretization points are equally
%distributed over the domain.
properties
Property1
end
methods
function obj = untitled(inputArg1,inputArg2)
%UNTITLED Construct an instance of this class
% Detailed explanation goes here
obj.Property1 = inputArg1 + inputArg2;
end
function outputArg = method1(obj,inputArg)
%METHOD1 Summary of this method goes here
% Detailed explanation goes here
outputArg = obj.Property1 + inputArg;
end
end
end
function [tests] = testDomain()
tests = functiontests(localfunctions);
end
function setupOnce(testCase)
end
function testDomainInit(testCase)
Z = linspace(0,pi, 3);
d = quantity.Domain('name', 'z', 'grid', Z);
D = [d d];
testCase.verifyEqual( ndims(D), 2);
testCase.verifyEqual( ndims(d), 1);
end
function testDomainGridLength(testCase)
Z = linspace(0,pi, 3);
d = quantity.Domain('name', 'z', 'grid', Z);
D = [d d];
testCase.verifyEqual( cellfun(@(v) numel(v), {Z, Z}), D.gridLength)
end
function testDomainNumGridElements(testCase)
Z = linspace(0,pi, 3);
d = quantity.Domain('name', 'z', 'grid', Z);
D = [d d];
testCase.verifyEqual( D.numGridElements, prod([length(Z), length(Z)]));
end
function testDomainEmpty(testCase)
d = quantity.Domain();
testCase.verifyTrue( isempty(d) )
end
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment