diff --git a/+quantity/Discrete.m b/+quantity/Discrete.m index 123bbc1b80fa5ed2770b1d9c521de24e669c2b4e..650e062a842267a307a26218ebbdd11e7ad18495 100644 --- a/+quantity/Discrete.m +++ b/+quantity/Discrete.m @@ -90,23 +90,11 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi domainParser.parse(varargin{:}); if domainParser.isDefault('domain') && ... - domainParser.isDefault('grid') && ... - domainParser.isDefault('gridName') + ( 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)); - + 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 @@ -224,7 +212,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi % has already been computed. empty = isempty(obj.valueDiscrete); if any(empty(:)) - obj.valueDiscrete = obj.on(obj.grid); + obj.valueDiscrete = obj.obj2value(obj.domain, true); end valueDiscrete = obj.valueDiscrete; end @@ -287,46 +275,61 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi end methods (Access = public) - function value = on(obj, myGrid, myGridName) - % TODO es sieht so aus als w�rde die Interpolation bei - % konstanten werten ziemlichen Quatsch machen! - % Da muss man nochmal ordentlich drauf schauen! + function value = on(obj, myDomain, gridNames) + % ON evaluation of the quantity on a certain domain. + % value = on(obj) or value = obj.on(); evaluates the quantity + % on its standard grid. + % value = obj.on( myDomain ) evalutes the quantity on the + % grid specified by myDomain. The order of the domains in + % domain, will be the same as from myDomain. + % value = obj.on( grid ) evaluates the quantity specified by + % grid. Grid must be a cell-array with the grids as elements. + % value = obj.on( grid, gridName ) evaluates the quantity + % specified by grid. Grid must be a cell-aary with the grids + % as elements. By the gridName parameter the order of the + % grid can be specified. + if isempty(obj) value = zeros(size(obj)); else - if nargin == 1 - myGrid = obj(1).grid; - myGridName = obj(1).gridName; - elseif nargin >= 2 && ~iscell(myGrid) - myGrid = {myGrid}; - end - gridPermuteIdx = 1:obj(1).nargin; - if nargin == 3 - if ~iscell(myGridName) - myGridName = {myGridName}; + if nargin == 2 + % case 1: a domain is specified by myDomain or by + % myDomain as a cell-array with grid entries + if iscell(myDomain) || isnumeric(myDomain) + myDomain = misc.ensureIsCell(myDomain); + assert(all(cellfun(@(v)isvector(v), myDomain)), 'The cell entries for a new grid have to be vectors') + newGrid = myDomain; + myDomain = quantity.Domain.empty(); + for k = 1:length(newGrid) + myDomain(k) = quantity.Domain('grid', newGrid{k}, 'name', obj(1).domain(k).name); + end end - assert(numel(myGrid) == numel(myGridName), ... - ['If on() is called by using gridNames as third input', ... - ', then the cell-array of grid and gridName must have ', ... - 'equal number of elements.']); - assert(numel(myGridName) == obj(1).nargin, ... - 'All (or none) gridName must be specified'); - gridPermuteIdx = cellfun(@(v) obj(1).gridIndex(v), myGridName); - myGrid = myGrid(gridPermuteIdx); + elseif nargin == 3 + assert(iscell(myDomain), 'If the domain is specified by cell-array pairs, the value myDomain must be a cell-array with grid entries') + 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(); + for k = 1:length(newGrid) + myDomain(k) = quantity.Domain('grid', newGrid{k}, 'name', gridNames{k}); + end + else + myDomain = obj(1).domain; end + + % verify the domain + assert(numel(myDomain) == numel(obj(1).domain), ['Wrong grid for the evaluation of the object']); + [myDomain, gridPermuteIdx] = obj(1).domain.permute(myDomain); + + % get the valueDiscrete data for this object. Apply the + % permuted myDomain. Then the obj2value will be evaluated + % in the order of the original domain. The permuatation to + % the new order will be done in the next step. + value = obj.obj2value(myDomain(gridPermuteIdx)); - value = obj.obj2value(); - - if nargin >= 2 && (prod(obj(1).gridSize) > 1) - indexGrid = arrayfun(@(s)linspace(1,s,s), size(obj), 'UniformOutput', false); - tempInterpolant = numeric.interpolant(... - [obj(1).grid, indexGrid{:}], value); - value = tempInterpolant.evaluate(myGrid{:}, indexGrid{:}); - elseif obj.isConstant - value = repmat(value, [cellfun(@(v) numel(v), myGrid), ones(1, length(size(obj)))]); - gridPermuteIdx = 1:numel(myGrid); - end - value = permute(reshape(value, [cellfun(@(v) numel(v), myGrid), size(obj)]), ... + value = permute(reshape(value, [cellfun(@(v) numel(v), {myDomain(gridPermuteIdx).grid}), size(obj)]), ... [gridPermuteIdx, numel(gridPermuteIdx)+(1:ndims(obj))]); end end @@ -422,37 +425,6 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi end end - function [gridJoined, gridNameJoined] = gridJoin(obj1, obj2) - %% gridJoin combines the grid and gridName of two objects (obj1, - % obj2), such that every gridName only occurs once and that the - % finer grid of both is used. - - gridNameJoined = unique([obj1(1).gridName, obj2(1).gridName]); - gridJoined = cell(1, numel(gridNameJoined)); - for it = 1 : numel(gridNameJoined) - currentGridName = gridNameJoined{it}; - [index1, lolo1] = obj1.gridIndex(currentGridName); - [index2, lolo2] = obj2.gridIndex(currentGridName); - if ~any(lolo1) - gridJoined{it} = obj2(1).grid{index2}; - elseif ~any(lolo2) - gridJoined{it} = obj1(1).grid{index1}; - else - tempGrid1 = obj1(1).grid{index1}; - tempGrid2 = obj2(1).grid{index2}; - - if ~obj1.isConstant && ~obj2.isConstant - assert(tempGrid1(1) == tempGrid2(1), 'Grids must have same domain for gridJoin') - assert(tempGrid1(end) == tempGrid2(end), 'Grids must have same domain for gridJoin') - end - if numel(tempGrid1) > numel(tempGrid2) - gridJoined{it} = tempGrid1; - else - gridJoined{it} = tempGrid2; - end - end - end - end % gridJoin() function obj = sort(obj, varargin) %SORT sorts the grid of the object in a desired order % obj = sortGrid(obj) sorts the grid in alphabetical order. @@ -814,6 +786,26 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi end function solution = subs(obj, gridName2Replace, values) + % SUBS substitute variables of a quantity + % solution = SUBS(obj, newDomain), replaces the original domain + % of the object with the new domain specified by newDomain. + % NewDomain must have the same grid name as the original + % domain. + % + % solution = SUBS(obj, GRIDNAMES2REPLACE, VALUES) + % replaces the domains which are specified by + % GRIDNAMES2REPLACE by VALUES. GRIDNAMES2REPLACE must be a + % cell-array with the names of the domains which should be + % replaced by VALUES. VALUES must be a cell-array of the new + % values or new grid names. + % + % Example: + % q = q.subs('z', 't') + % will replace the domain with the name 'z' by a domain + % with the name 't' but with the same discretization. + % q = q.subs('z', linspace(0,1)') + % will replace the grid of domain with the name 'z' by + % the new grid specified by linspace. if nargin == 1 || isempty(gridName2Replace) % if gridName2Replace is empty, then nothing must be done. solution = obj; @@ -822,14 +814,19 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi solution = obj; else % input checks - assert(nargin == 3, ['Wrong number of input arguments. ', ... - 'gridName2Replace and values must be cell-arrays!']); - if ~iscell(gridName2Replace) - gridName2Replace = {gridName2Replace}; - end - if ~iscell(values) - values = {values}; + if nargin == 2 + assert(isa(gridName2Replace, 'quantity.Domain'), 'If only two parameters are specified, the second parameter must be a quantiy.Domain'); + + values = {gridName2Replace.grid}; + gridName2Replace = {gridName2Replace.name}; + + + elseif nargin == 3 + + gridName2Replace = misc.ensureIsCell(gridName2Replace); + values = misc.ensureIsCell(values); end + assert(numel(values) == numel(gridName2Replace), ... 'gridName2Replace and values must be of same size'); @@ -865,8 +862,8 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi % called with subs(f, zeta, z), then g(z) = f(z, z) % results, hence the dimensions z and zeta are % merged. - gridIndices = [obj(1).gridIndex(gridName2Replace{1}), ... - obj(1).gridIndex(values{1})]; + gridIndices = [obj(1).domain.gridIndex(gridName2Replace{1}), ... + obj(1).domain.gridIndex(values{1})]; newGridForOn = obj(1).grid; if numel(obj(1).grid{gridIndices(1)}) > numel(obj(1).grid{gridIndices(2)}) newGridForOn{gridIndices(2)} = newGridForOn{gridIndices(1)}; @@ -886,7 +883,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi % changed. newGrid = obj(1).grid; newGridName = obj(1).gridName; - newGridName{obj(1).gridIndex(gridName2Replace{1})} ... + newGridName{obj(1).domain.gridIndex(gridName2Replace{1})} ... = values{1}; newValue = obj.on(); end @@ -900,7 +897,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi % newGrid is the similar to the original grid, but the % grid of gridName2Replace is removed. newGrid = obj(1).grid; - newGrid = newGrid((1:1:numel(newGrid)) ~= obj.gridIndex(gridName2Replace{1})); + newGrid = newGrid((1:1:numel(newGrid)) ~= obj(1).domain.gridIndex(gridName2Replace{1})); newGridSize = cellfun(@(v) numel(v), newGrid); % newGridForOn is the similar to the original grid, but % the grid of gridName2Replace is set to values{1} for @@ -930,9 +927,12 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi solution = solution.subs(gridName2Replace(2:end), values(2:end)); end end - end + function [idx, logicalIdx] = gridIndex(obj, varargin) + [idx, logicalIdx] = obj(1).domain.gridIndex(varargin{:}); + end + function value = at(obj, point) % at() evaluates the object at one point and returns it as array % with the same size as size(obj). @@ -1017,20 +1017,6 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi end end - function matGrid = ndgrid(obj, grid) - % ndgrid calles ndgrid for the default grid, if no other grid - % is specified. Empty grid as input returns empty cell as - % result. - if nargin == 1 - grid = obj.grid; - end - if isempty(grid) - matGrid = {}; - else - [matGrid{1:obj.nargin}] = ndgrid(grid{:}); - end - end % ndgrid() - function H = plot(obj, varargin) H = []; p = misc.Parser(); @@ -1222,28 +1208,37 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi end % flipGrid() function newObj = changeGrid(obj, gridNew, gridNameNew) + % CHANGEGRID change the grid of the quantity. + % newObj = CHANGEGRID(obj, gridNew, gridNameNew) % change the grid of the obj quantity. The order of grid and % gridName in the obj properties remains unchanged, only the % data points are exchanged. + % + % example: + % 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 - gridIndexNew = obj(1).gridIndex(gridNameNew); - myGrid = cell(1, numel(obj(1).grid)); - myGridName = cell(1, numel(obj(1).grid)); - for it = 1 : numel(myGrid) - myGrid{gridIndexNew(it)} = gridNew{it}; - myGridName{gridIndexNew(it)} = gridNameNew{it}; + + gridNew = misc.ensureIsCell(gridNew); + gridNameNew = misc.ensureIsCell(gridNameNew); + + [gridIndexNew, logIdx] = obj(1).domain.gridIndex(gridNameNew); + newDomain = obj(1).domain; + + for i = 1 : length(gridIndexNew) + newDomain(gridIndexNew(i)) = quantity.Domain(... + 'grid', gridNew{i}, 'name', gridNameNew{i}); end - assert(isequal(myGridName(:), obj(1).gridName(:)), 'rearranging grids failed'); + assert(isequal({newDomain.name}, obj(1).gridName), 'rearranging grids failed'); newObj = obj.copy(); - for it = 1 : numel(obj) - newObj(it).valueDiscrete = obj(it).on(myGrid); + [newObj.domain] = deal(newDomain); + for i = 1 : numel(obj) + newObj(i).valueDiscrete = obj(i).on(newDomain); end - %[newObj.derivatives] = deal({}); - [newObj.grid] = deal(myGrid); end function [lowerBounds, upperBounds] = getBoundaryValues(obj, varargin) @@ -1438,6 +1433,9 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi P = (b' * a')'; P.setName([a(1).name, ' ', b(1).name]); return + elseif a.isConstant() && b.isConstant() + P = a.on() * b.on(); + return end if isscalar(b) % do the scalar multiplication in a loop @@ -1469,38 +1467,48 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi % multiple dimensions of the input are correctly arranged. [idx, permuteGrid] = computePermutationVectors(a, b); - parameters = struct(a(1)); parameters.name = [a(1).name, ' ', b(1).name]; - parameters.grid = [... - a(1).grid(idx.A.common), ... - b(1).grid(~idx.B.common)]; - parameters.gridName = [... - a(1).gridName(idx.A.grid), ... - b(1).gridName(~idx.B.common)]; + parameters.figureID = a(1).figureID; + + + domainA = a(1).domain; + domainB = b(1).domain; % select finest grid from a and b - parameters.grid = cell(1, numel(parameters.gridName)); - [gridJoined, gridNameJoined] = gridJoin(a, b); + joinedDomain = gridJoin(domainA, domainB); + newDomainName = {joinedDomain.name}; + % gridJoin combines the grid of a and b while using the finer % of both. - aGrid = cell(1, a.nargin); % for later call of a.on() with fine grid - bGrid = cell(1, b.nargin); % for later call of b.on() with fine grid - for it = 1 : numel(parameters.gridName) - parameters.grid{it} = gridJoined{... - strcmp(parameters.gridName{it}, gridNameJoined)}; - if a.gridIndex(parameters.gridName{it}) - aGrid{a.gridIndex(parameters.gridName{it})} = ... - parameters.grid{it}; + + finalGridOrder = [domainA(idx.A.common), domainA(~idx.A.common), domainB(~idx.B.common)]; + + %% generate the new grids + newDomainA = quantity.Domain.empty(); % for later call of a.on() with fine grid + newDomainB = quantity.Domain.empty(); % for later call of b.on() with fine grid + for i = 1 : numel(newDomainName) + + parameters.domain(i) = finalGridOrder(i); + + % if there is a component of the original domain in the + % joined domain, it can happen the length of a domain has + % changed. Thus, it must be updated for a later evaluation. + idxA = domainA.gridIndex(finalGridOrder(i).name); + idxB = domainB.gridIndex(finalGridOrder(i).name); + + if idxA + newDomainA = [newDomainA, finalGridOrder(i)]; end - if b.gridIndex(parameters.gridName{it}) - bGrid{b.gridIndex(parameters.gridName{it})} = ... - parameters.grid{it}; + + if idxB + newDomainB = [newDomainB, finalGridOrder(i)]; end end parameters = misc.struct2namevaluepair(parameters); - valueA = permute(a.on(aGrid), idx.A.permute); - valueB = permute(b.on(bGrid), idx.B.permute); + %% + valueA = a.on(newDomainA); + valueB = b.on(newDomainB); C = misc.multArray(valueA, valueB, idx.A.value(end), idx.B.value(1), idx.common); C = permute(C, permuteGrid); @@ -1712,7 +1720,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi function myGrid = gridOf(obj, myGridName) if ~iscell(myGridName) - gridIdx = obj(1).gridIndex(myGridName); + gridIdx = obj(1).domain.gridIndex(myGridName); if gridIdx > 0 myGrid = obj(1).grid{gridIdx}; else @@ -1721,7 +1729,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi else myGrid = cell(size(myGridName)); for it = 1 : numel(myGrid) - gridIdx = obj(1).gridIndex(myGridName{it}); + gridIdx = obj(1).domain.gridIndex(myGridName{it}); if gridIdx > 0 myGrid{it} = obj(1).grid{gridIdx}; else @@ -1731,37 +1739,6 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi end end - function [idx, logicalIdx] = gridIndex(obj, names) - %% GRIDINDEX returns the index of the grid - % [idx, log] = gridIndex(obj, names) searches in the gridName - % properties of obj for the "names" and returns its index as - % "idx" and its logical index as "log" - - if nargin == 1 - names = obj(1).gridName; - end - - if ~iscell(names) - names = {names}; - end - - idx = zeros(1, length(names)); - nArgIdx = 1:obj.nargin(); - - logicalIdx = false(1, obj.nargin()); - - for k = 1:length(names) - log = strcmp(obj(1).gridName, names{k}); - logicalIdx = logicalIdx | log; - if any(log) - idx(k) = nArgIdx(log); - else - idx(k) = 0; - end - end - - end - function result = diff(obj, k, diffGridName) % DIFF computation of the derivative % result = DIFF(obj, k, diffGridName) applies the @@ -1888,8 +1865,6 @@ 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); @@ -1900,21 +1875,22 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi J = shiftdim(J); end - if all(isGrid) - grdName = ''; - newGrid = {}; - else - grdName = obj(kObj).gridName{~isGrid}; - newGrid = obj(kObj).grid{~isGrid}; - end - % create result: I(kObj).valueDiscrete = J; - I(kObj).gridName = grdName; - I(kObj).grid = newGrid; I(kObj).name = ['int(' obj(kObj).name ')']; end + if all(isGrid) + grdName = ''; + newGrid = {}; + else + grdName = obj(kObj).gridName{~isGrid}; + newGrid = obj(kObj).grid{~isGrid}; + end + + newDomain = quantity.Domain('grid', newGrid, 'name', grdName); + [I.domain] = deal(newDomain); + end function result = cumInt(obj, domain, lowerBound, upperBound) @@ -1930,7 +1906,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi % input parser since some default values depend on intGridName. myParser = misc.Parser; - myParser.addRequired('domain', @(d) obj.gridIndex(d) ~= 0); + myParser.addRequired('domain', @(d) obj(1).domain.gridIndex(d) ~= 0); myParser.addRequired('lowerBound', ... @(l) isnumeric(l) || ischar(l) ); myParser.addRequired('upperBound', ... @@ -1939,7 +1915,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi % get grid myGrid = obj(1).grid; - intGridIdx = obj.gridIndex(domain); + intGridIdx = obj(1).domain.gridIndex(domain); % integrate F = numeric.cumtrapz_fast_nDim(myGrid{intGridIdx}, ... @@ -1968,54 +1944,57 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi % for support of numeric inputs: if ~isa(A, 'quantity.Discrete') if isnumeric(A) - A = quantity.Discrete(A, 'name', 'c', 'gridName', {}, 'grid', {}); + A = quantity.Discrete(A, 'name', 'c', 'domain', quantity.Domain.empty()); else error('Not yet implemented') end elseif ~isa(B, 'quantity.Discrete') if isnumeric(B) - B = quantity.Discrete(B, 'name', 'c', 'gridName', {}); + B = quantity.Discrete(B, 'name', 'c', 'domain', quantity.Domain.empty()); else B = quantity.Discrete(B, 'name', 'c', 'gridName', A(1).gridName, 'grid', A.grid); end end % combine both domains with finest grid - [gridJoined, gridNameJoined] = gridJoin(A, B); - gridJoinedLength = cellfun(@(v) numel(v), gridJoined); + joinedDomain = gridJoin(A(1).domain, B(1).domain); + - [aDiscrete] = A.expandValueDiscrete(gridJoined, gridNameJoined, gridJoinedLength); - [bDiscrete] = B.expandValueDiscrete(gridJoined, gridNameJoined, gridJoinedLength); + [aDiscrete] = A.expandValueDiscrete(joinedDomain); + [bDiscrete] = B.expandValueDiscrete(joinedDomain); % create result object C = quantity.Discrete(aDiscrete + bDiscrete, ... - 'grid', gridJoined, 'gridName', gridNameJoined, ... - 'size', size(A), 'name', [A(1).name, '+', B(1).name]); + 'domain', joinedDomain, ... + 'name', [A(1).name, '+', B(1).name]); end - function [valDiscrete] = expandValueDiscrete(obj, gridJoined, gridNameJoined, gridJoinedLength) + function [valDiscrete] = expandValueDiscrete(obj, newDomain) % EXPANDVALUEDISCRETE % [valDiscrete, gridNameSorted] = ... % expandValueDiscrete(obj, gridIndex, valDiscrete) % Expanses the value of obj, so that - % todo + + + gridNameJoined = {newDomain.name}; + gridJoinedLength = newDomain.gridLength; % get the index of obj.grid in the joined grid - gridLogical = logical( obj.gridIndex(gridNameJoined) ); + [~, logicalIdx] = newDomain.gridIndex({obj(1).domain.name}); - valDiscrete = obj.on(gridJoined(gridLogical > 0), gridNameJoined(gridLogical > 0)); + valDiscrete = obj.on( newDomain(logicalIdx) ); oldDim = ndims(valDiscrete); - valDiscrete = permute(valDiscrete, [(1:sum(~gridLogical)) + oldDim, 1:oldDim] ); - valDiscrete = repmat(valDiscrete, [gridJoinedLength(~gridLogical), ones(1, ndims(valDiscrete))]); + valDiscrete = permute(valDiscrete, [(1:sum(~logicalIdx)) + oldDim, 1:oldDim] ); + valDiscrete = repmat(valDiscrete, [gridJoinedLength(~logicalIdx), ones(1, ndims(valDiscrete))]); % valDiscrete = reshape(valDiscrete, ... - [gridJoinedLength(~gridLogical), gridJoinedLength(gridLogical), size(obj)]); + [gridJoinedLength(~logicalIdx), gridJoinedLength(logicalIdx), size(obj)]); - % % 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))]); + % permute valDiscrete such that grids are in the order specified + % by gridNameJoined. + gridIndex = 1:numel(logicalIdx); + gridOrder = [gridIndex(~logicalIdx), gridIndex(logicalIdx)]; + valDiscrete = permute(valDiscrete, [gridOrder, numel(logicalIdx)+(1:ndims(obj))]); end @@ -2155,15 +2134,32 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi medianValue = reshape(median(obj.on(), 1:obj(1).nargin), size(obj)); end - function q = obj2value(obj) - % for a case with a 3-dimensional grid, the common cell2mat - % with reshape did not work. So it has to be done manually: + function value = obj2value(obj, myDomain) + % OBJ2VALUE make the stored data in valueDiscrete available for + % in the output format + % value = obj2value(obj) returns the valueDiscrete in the + % form size(value) = [gridLength, objSize] + % obj2value(obj, myDomain) returns the valueDiscrete in the + % form size(value) = [myDomain.gridLength objSize] v = zeros([numel(obj(1).valueDiscrete), numel(obj)]); for k = 1:numel(obj) v(:,k) = obj(k).valueDiscrete(:); end - q = reshape(v, [obj(1).gridSize(), size(obj)]); + value = reshape(v, [obj(1).gridSize(), size(obj)]); + + if myDomain ~= obj(1).domain + % if a new domain is specified for the evaluation of + % the quantity, do an interpolation based on the old + % data. + indexGrid = arrayfun(@(s) 1:s, size(obj), 'UniformOutput', false); + tempInterpolant = numeric.interpolant(... + [obj(1).grid, indexGrid{:}], value); + tempGrid = {myDomain.grid}; + value = tempInterpolant.evaluate(tempGrid{:}, indexGrid{:}); + elseif obj.isConstant + value = repmat(value, [cellfun(@(v) numel(v), {myDomain.grid}), ones(1, length(size(obj)))]); + end end @@ -2225,7 +2221,7 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete < handle & matlab.mi myGridSize = num2cell( fullSize(1:length(gridSize)) ); if nargin == 2 - valueSize = [fullSize(length(myGridSize)+1:length(fullSize)), 1]; + valueSize = [fullSize(length(myGridSize)+1:length(fullSize)), 1, 1]; end myValueSize = arrayfun(@(n) ones(n, 1), valueSize, 'UniformOutput', false); diff --git a/+quantity/Domain.m b/+quantity/Domain.m index 1484f5c5e9645fe3aaf11cfbb27766423c64598f..fb8647eed11b52e69942e617b2ff9e9b2af21e63 100644 --- a/+quantity/Domain.m +++ b/+quantity/Domain.m @@ -1,7 +1,7 @@ classdef Domain %DOMAIN class to describes a range of values on which a function can be defined. - % todo: + % todo: % * EquidistantDomain % * multi dimensional @@ -9,16 +9,18 @@ classdef Domain % 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}; + % 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; + + isConstant = false ; end properties (Dependent) - n; % number of discretization points + n; % number of discretization points lower; % lower bound of the domain upper; % upper bound of the domain end @@ -29,13 +31,13 @@ classdef Domain % if nargin >= 1 parser = misc.Parser(); - parser.addParameter('grid', [], @isvector); + parser.addParameter('grid', [], @(g) isvector(g) || isempty(g)); parser.addParameter('name', '', @ischar); parser.parse(varargin{:}); - + % todo: assertions % * ascending ? - + obj.grid = parser.Results.grid(:); obj.name = parser.Results.name; else @@ -72,6 +74,124 @@ classdef Domain s = [obj.n]; end + function s = eq(obj, obj2) + s = all(obj.gridLength == obj2.gridLength); + for k = 1:numel(obj) + s = s && all(obj(k).grid == obj2(k).grid); + s = s && strcmp(obj(k).name, obj2(k).name); + end + end + + function l = ne(obj, obj2) + l = ~(obj == obj2); + end + + function [joinedDomain] = gridJoin(obj1, obj2) + %% gridJoin combines the grid and gridName of two objects (obj1, + % obj2), such that every gridName only occurs once and that the + % finer grid of both is used. + + [joinedGrid] = union({obj1.name}, {obj2.name}, 'sorted'); + + joinedDomain = quantity.Domain.empty; + + % 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] = obj1.gridIndex(currentGridName); + [index2, logicalIndex2] = obj2.gridIndex(currentGridName); + + % + % if ~any(logicalIndex1) + % joinedDomain(i) = obj2(index2); + % elseif ~any(logicalIndex2) + % joinedDomain(i) = obj1(index1); + % else + % + % Check if a domain is in both domains: + % -> then take the finer one of both + if any(logicalIndex1) && any(logicalIndex2) + tempDomain1 = obj1(index1); + tempDomain2 = obj2(index2); + + if ~tempDomain1.isConstant && ~tempDomain2.isConstant + assert(tempDomain1.lower == tempDomain2.lower, 'Grids must have same domain for gridJoin') + assert(tempDomain1.upper == tempDomain2.upper, 'Grids must have same domain for gridJoin') + end + if tempDomain1.gridLength > tempDomain2.gridLength + joinedDomain(i) = tempDomain1; + else + joinedDomain(i) = tempDomain2; + end + % If it is not in both, -> just take the normal grid + elseif any(logicalIndex1) + joinedDomain(i) = obj1(index1); + elseif any(logicalIndex2) + joinedDomain(i) = obj2(index2); + end + + end + end % gridJoin() + + function [idx, logicalIdx] = gridIndex(obj, names) + %% GRIDINDEX returns the index of the grid + % [idx, log] = gridIndex(obj, names) searches in the gridName + % properties of obj for the "names" and returns its index as + % "idx" and its logical index as "log" + + if nargin == 1 + names = {obj.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) + idx(k) = nArgIdx(log); + else + idx(k) = 0; + end + end + end % gridIndex + + function matGrid = ndgrid(obj) + % ndgrid calles ndgrid for the default grid, if no other grid + % is specified. Empty grid as input returns empty cell as + % result. + if isempty(obj) + matGrid = {}; + else + grids = {obj.grid}; + [matGrid{1:obj.ndims}] = ndgrid(grids{:}); + end + end % ndgrid() + + function [newDomain, idx] = permute(obj, order) + + if isa(order, 'quantity.Domain') + names = {order.name}; + elseif ischar([order{:}]) + names = order; + else + error('the input parameter order must be a array of quantity.Domain objects or a cell-array with string') + end + + idx = cellfun(@(v) obj.gridIndex(v), names); + + if isa(order, 'quantity.Domain') + newDomain = order; + else + newDomain = obj(idx); + end + end end methods (Static) @@ -97,7 +217,7 @@ classdef Domain g(k) = quantity.Domain('grid', O, 'name', name{k}); end - end + end end end diff --git a/+quantity/Function.m b/+quantity/Function.m index 4b090acdc57b53388e2b9c58fb142af9540ba511..c8eba8e971b35257a8e7ce2d83c70211470beb8e 100644 --- a/+quantity/Function.m +++ b/+quantity/Function.m @@ -74,57 +74,25 @@ classdef Function < quantity.Discrete %----------------------------- % --- overloaded functions --- %----------------------------- - function value = on(obj, myGrid, myGridName) - % evaluates obj.valueContinuous function and returns an array - % with the dimensions (n, m, ..., z_disc). n, m, ... are the - % array dimensions of obj.valueContinuous and z_disc is the - % dimensions of the grid. - if nargin == 1 - gridSize = obj(1).gridSize; - if gridSize > 2 - value = cat(length(gridSize),obj.valueDiscrete); - else % important to include this case for empty grids! - value = [obj.valueDiscrete]; % = cat(2,obj.valueDiscrete) - end - value = reshape(value, [gridSize, size(obj)]); - return - elseif nargin >= 2 && ~iscell(myGrid) - myGrid = {myGrid}; - end - gridPermuteIdx = 1:obj(1).nargin; - if isempty(gridPermuteIdx) - gridPermuteIdx = 1; - end - if nargin == 3 - if ~iscell(myGridName) - myGridName = {myGridName}; - end - assert(numel(myGrid) == numel(myGridName), ... - ['If on() is called by using gridNames as third input', ... - ', then the cell-array of grid and gridName must have ', ... - 'equal number of elements.']); - assert(numel(myGridName) == obj(1).nargin, ... - 'All (or none) gridName must be specified'); - gridPermuteIdx = cellfun(@(v) obj(1).gridIndex(v), myGridName); - myGrid = myGrid(gridPermuteIdx); - end - % at first the value has to be initialized by different - % dimensions, to simplify the evaluation of each entry: + function value = obj2value(obj, myDomain, recalculate) - gridSize = cellfun('length', myGrid); - if isempty(gridSize), gridSize = 1; end - - value = nan([numel(obj), gridSize]); - for k = 1:numel(obj) - ndGrd = obj.ndgrid( myGrid ); - tmp = obj(k).evaluateFunction( ndGrd{:} ); - % Replaced - % double(subs(obj(k).valueSymbolic, obj.variable, grid)); - % because this is very slow! - value(k,:) = tmp(:); + % check if the domain for the evaluation has changed. If not + % we can use the stored values in valueDiscrete: + if nargin < 3 + recalculate = false; + end + if ~recalculate && (myDomain == obj(1).domain) + value = obj2value@quantity.Discrete(obj, myDomain); + else + % otherwise the function has to be evaluated on the new + % domain + value = nan([numel(obj), myDomain.gridLength]); + for k = 1:numel(obj) + ndGrd = myDomain.ndgrid; + tmp = obj(k).evaluateFunction( ndGrd{:} ); + value(k,:) = tmp(:); + end end - value = reshape(permute(value, [1+(gridPermuteIdx), 1]), ... - [gridSize(gridPermuteIdx), size(obj)]); end function n = nargin(obj) diff --git a/+unittests/+quantity/testDiscrete.m b/+unittests/+quantity/testDiscrete.m index fc20f9e5dab6eb9c45f2b8ae6089972a00c088bc..e18b0fc973d26609401aaa282130e7fbd8fa5031 100644 --- a/+unittests/+quantity/testDiscrete.m +++ b/+unittests/+quantity/testDiscrete.m @@ -472,23 +472,28 @@ testCase.verifyEqual(myQuantityDZzeta(3).on(), zeros(11, 21), 'AbsTol', 10*eps); end function testOn(testCase) -%% init data -gridVecA = linspace(0, 1, 27); -gridVecB = linspace(0, 1, 41); -[value] = createTestData(gridVecA, gridVecB); +% init data +domainVecA = quantity.Domain('grid', linspace(0, 1, 27), 'name', 'z'); +domainVecB = quantity.Domain('grid', linspace(0, 1, 41), 'name', 'zeta'); +[value] = createTestData(domainVecA.grid, domainVecB.grid); a = quantity.Discrete(value, 'size', [2, 3], ... - 'grid', {gridVecA, gridVecB}, 'gridName', {'z', 'zeta'}, 'name', 'A'); + 'domain', [domainVecA, domainVecB], 'name', 'A'); -%% +% testCase.verifyEqual(value, a.on()); testCase.verifyEqual(permute(value, [2, 1, 3, 4]), ... - a.on({gridVecB, gridVecA}, {'zeta', 'z'})); + a.on([domainVecB, domainVecA])); testCase.verifyEqual(createTestData(linspace(0, 1, 100), linspace(0, 1, 21)), ... a.on({linspace(0, 1, 100), linspace(0, 1, 21)}), 'AbsTol', 100*eps); + +d24z = quantity.Domain('grid', linspace(0, 1, 24), 'name', 'z'); +d21zeta = quantity.Domain('grid', linspace(0, 1, 21), 'name', 'zeta'); + testCase.verifyEqual(createTestData(linspace(0, 1, 24), linspace(0, 1, 21)), ... - a.on({linspace(0, 1, 24), linspace(0, 1, 21)}, {'z', 'zeta'}), 'AbsTol', 24*eps); + a.on( [d24z, d21zeta] ), 'AbsTol', 24*eps); + testCase.verifyEqual(permute(createTestData(linspace(0, 1, 21), linspace(0, 1, 24)), [2, 1, 3, 4]), ... - a.on({linspace(0, 1, 24), linspace(0, 1, 21)}, {'zeta', 'z'}), 'AbsTol', 2e-4); + a.on( {linspace(0, 1, 24), linspace(0, 1, 21)}, {'zeta', 'z'}), 'AbsTol', 2e-4); function [value] = createTestData(gridVecA, gridVecB) [zGridA , zetaGridA] = ndgrid(gridVecA, gridVecB); @@ -531,7 +536,7 @@ b = quantity.Symbolic((eye(2, 2)), 'variable', zeta, ... 'grid', gridVecB); c = a*b; -%% +% testCase.verifyEqual(c.on(), a.on()); end @@ -575,7 +580,10 @@ KLdKR = KL \ KR; %% testCase.verifyEqual(vLdVR.on(), 2 .\ (linspace(1, 2, 21).').^2); -testCase.verifyEqual(KLdKR.on(), permute(repmat(2*eye(2), [1, 1, size(T)]), [4, 3, 1, 2])); + +% TODO #discussion: for the following case the operation KL \ KR seems to change the order of the grid in the old version. As this is very unexpected. I would like to change them to not change the order! +testCase.verifyEqual(KLdKR.on(), permute(repmat(2*eye(2), [1, 1, size(T)]), [3, 4, 1, 2])); + end function testMrdivide(testCase) @@ -601,7 +609,9 @@ KLdKR = KL / KR; %% testCase.verifyEqual(vLdVR.on(), 2 ./ (linspace(1, 2, 21).').^2); -testCase.verifyEqual(KLdKR.on(), permute(repmat(0.5*eye(2), [1, 1, size(T)]), [4, 3, 1, 2])); + +% TODO #discussion: for the following case the operation KL \ KR seems to change the order of the grid in the old version. As this is very unexpected. I would like to change them to not change the order! +testCase.verifyEqual(KLdKR.on(), permute(repmat(0.5*eye(2), [1, 1, size(T)]), [3, 4, 1, 2])); end function testInv(testCase) @@ -753,61 +763,6 @@ testCase.verifyTrue(all(B_z_1(:,:,2) == 1)); end -function testGridJoin(testCase) -s = linspace(0, 1); -z = linspace(0, 1, 71)'; -t = linspace(0, 1, 51); - -[Z, T, S] = ndgrid(z, t, s); - -a = quantity.Discrete(cat(4, sin(Z.*Z.*S), cos(Z.*T.*S)), ... - 'size', [2 1], 'grid', {z, t, s}, 'gridName', {'z', 't', 's'}); -b = quantity.Discrete(ones(numel(s), numel(t)), ... - 'size', [1 1], 'grid', {s, t}, 'gridName', {'p', 's'}); -c = quantity.Discrete(ones(numel(t), 2, 2), ... - 'size', [2 2], 'grid', {t}, 'gridName', {'p'}); - -[gridJoinedAB, gridNameJoinedAB] = gridJoin(a, b); -[gridJoinedCC, gridNameJoinedCC] = gridJoin(c, c); -[gridJoinedBC, gridNameJoinedBC] = gridJoin(b, c); - -testCase.verifyEqual(gridNameJoinedAB, {'p', 's', 't', 'z'}); -testCase.verifyEqual(gridJoinedAB, {s, s, t, z}); -testCase.verifyEqual(gridNameJoinedCC, {'p'}); -testCase.verifyEqual(gridJoinedCC, {t}); -testCase.verifyEqual(gridNameJoinedBC, {'p', 's'}); -testCase.verifyEqual(gridJoinedBC, {s, t}); -end - -function testGridIndex(testCase) - - -z = linspace(0, 2*pi, 71)'; -t = linspace(0, 3*pi, 51); -s = linspace(0, 1); - -[Z, T, S] = ndgrid(z, t, s); - -a = quantity.Discrete(cat(4, sin(Z.*Z.*S), cos(Z.*T.*S)), ... - 'size', [2 1], 'grid', {z, t, s}, 'gridName', {'z', 't', 's'}); - -idx = a.gridIndex('z'); - -testCase.verifyEqual(idx, 1); - - -idx = a.gridIndex({'z', 't'}); -testCase.verifyEqual(idx, [1 2]); - -idx = a.gridIndex({'z', 's'}); -testCase.verifyEqual(idx, [1 3]); - -idx = a.gridIndex('t'); -testCase.verifyEqual(idx, 2); - -end - - function testMTimes(testCase) % multiplication of a(z) * a(z)' % a(z) \in (3, 2), z \in (100) @@ -836,7 +791,7 @@ testCase.verifyTrue(numeric.near(b1b1', B1B1.on())); % vector multiplication of b(z, t) * b(v, t) b2 = cat(3, z .* t, z*0 +1); b2b2 = misc.multArray(b2, permute(b2, [1 2 4 3]), 4, 3, [1, 2]); -b2b2 = permute(b2b2, [2 1 3 4]); +b2b2 = permute(b2b2, [1 2 3 4]); B2 = quantity.Discrete(b2, 'size', [ 2, 1 ], 'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)}); B2B2 = B2*B2'; testCase.verifyTrue(numeric.near(b2b2, B2B2.on())); @@ -845,12 +800,12 @@ c2 = cat(4, sin(z), sin(t), cos(z)); b2c2 = misc.multArray(b2, c2, 4, 3, [1 2]); C2 = quantity.Discrete(c2, 'size', [1 3], 'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)}); B2C2 = B2 * C2; -testCase.verifyTrue(numeric.near(permute(b2c2, [2 1 3 4]), B2C2.on())); +testCase.verifyTrue(numeric.near(permute(b2c2, [1 2 3 4]), B2C2.on())); % matrix b(z,t) * b(z,t) b = cat(4, cat(3, z .* t, z*0 +1), cat(3, sin( z ), cos( z .* t ))); bb = misc.multArray(b, b, 4, 3, [1, 2]); -bb = permute(bb, [2 1 3 4]); +bb = permute(bb, [1 2 3 4]); B = quantity.Discrete(b, 'size', [ 2, 2 ], 'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)}); BB = B*B; testCase.verifyTrue(numeric.near(bb, BB.on)); @@ -859,8 +814,8 @@ testCase.verifyTrue(numeric.near(bb, BB.on)); c = cat(4, cat(3, v .* t2, v*0 +1), cat(3, sin( v ), cos( v .* t2 )), cat(3, cos( t2 ), tan( v .* t2 ))); bc = misc.multArray(b, c, 4, 3, 2); bc = permute(bc, [ 1 2 4 3 5 ]); -B = quantity.Discrete(b, 'size', [ 2, 2 ], 'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)}); -C = quantity.Discrete(c, 'size', [2, 3], 'gridName', {'v', 't'}, 'grid',{v(:,1), t2(1,:)}); +B = quantity.Discrete(b, 'size', [2, 2], 'gridName', {'z', 't'}, 'grid', {z(:,1), t(1,:)}); +C = quantity.Discrete(c, 'size', [2, 3], 'gridName', {'v', 't'}, 'grid', {v(:,1), t2(1,:)}); BC = B*C; testCase.verifyTrue(numeric.near(bc, BC.on)); @@ -876,7 +831,7 @@ testCase.verifyTrue( numeric.near( aa(2,2).on() , cos(z * pi) .* cos(z * pi))); %% test multiplicatin with constants: C = [3 0; 0 5]; -c = quantity.Discrete(C, 'gridName', {}, 'grid', {}, 'name', 'c'); +c = quantity.Discrete(C, 'domain', quantity.Domain.empty(), 'name', 'c'); testCase.verifyTrue( numeric.near( squeeze(double(a*c)), [sin(z * pi) * 3, cos(z * pi) * 5])); testCase.verifyTrue( numeric.near( squeeze(double(a*[3 0; 0 5])), [sin(z * pi) * 3, cos(z * pi) * 5])); testCase.verifyTrue( numeric.near( double(c*c), C*C)); @@ -897,7 +852,7 @@ end function testIsConstant(testCase) -C = quantity.Discrete(rand(3,7), 'gridName', {}); +C = quantity.Discrete(rand(3,7), 'domain', quantity.Domain.empty()); testCase.verifyTrue(C.isConstant()); z = linspace(0, pi)'; @@ -913,7 +868,7 @@ a = cat(3, [myGrid*0, myGrid, sin(myGrid)], [myGrid.^2, myGrid.^3, cos(myGrid)]) c = rand(2, 3); % dim = (2, 3) A = quantity.Discrete(a, 'size', [3, 2], 'grid', myGrid, 'gridName', {'z'}); -C = quantity.Discrete(c, 'gridName', {}); +C = quantity.Discrete(c, 'domain', quantity.Domain.empty()); ac = misc.multArray(a, c, 3, 1); % dim = (100, 3, 3) AC = A*C; @@ -1019,7 +974,11 @@ v = {sin(z * t * pi); cos(z * t * pi)}; V = cat(3, v{:}); b = quantity.Discrete(v, 'grid', {z, t}, 'gridName', {'z', 't'}); c = quantity.Discrete(V, 'grid', {z, t}, 'gridName', {'z', 't'}); -d = quantity.Discrete(V, 'gridName', {'z', 't'}); + +Z = quantity.Domain('grid', z, 'name', 'z'); +T = quantity.Domain('grid', t, 'name', 't'); + +d = quantity.Discrete(V, 'domain', [Z, T]); %% verifyTrue(testCase, misc.alln(b.on() == c.on())); @@ -1071,23 +1030,12 @@ verifyTrue(testCase, numeric.near(A, Anum, 1e-2)); end -% function testNDGrid(testCase) -% %% -% z = linspace(0,1).'; -% t = linspace(0,1,101); -% b = quantity.Discrete({sin(z * t * pi); cos(z * t * pi)}, 'grid', {z, t}, 'gridName', {'z', 't'}); -% % #TODO -% end - -function testDefaultGrid(testCase) -g = quantity.Discrete.defaultGrid([100, 42]); -testCase.verifyEqual(g{1}, linspace(0, 1, 100).'); -testCase.verifyEqual(g{2}, linspace(0, 1, 42)); -end - function testValue2Cell(testCase) v = rand([100, 42, 2, 3]); -V = quantity.Discrete( quantity.Discrete.value2cell(v, [100, 42], [2, 3]), 'gridName', {'z', 't'} ); +Z = quantity.Domain('grid', linspace(0,1,100), 'name', 'z'); +T = quantity.Domain('grid', linspace(0,1,42), 'name', 't'); +V = quantity.Discrete( quantity.Discrete.value2cell(v, [100, 42], [2, 3]), ... + 'domain', [Z, T]); verifyTrue(testCase, misc.alln(v == V.on())); end @@ -1139,6 +1087,7 @@ t = linspace(0, 1, 31); quan = quantity.Discrete({sin(z * t * pi); cos(z * t * pi)}, 'grid', {z, t}, 'gridName', {'z', 't'}); gridSampled = {linspace(0, 1, 11), linspace(0, 1, 21)}; quanCopy = copy(quan); +quanSampled = quanCopy.changeGrid({linspace(0,1,11)}, {'t'}); quanSampled = quanCopy.changeGrid(gridSampled, {'z', 't'}); testCase.verifyEqual(quanSampled.on(), quan.on(gridSampled)) @@ -1147,14 +1096,16 @@ quanSampled2 = quanCopy2.changeGrid(gridSampled, {'t', 'z'}); testCase.verifyEqual(quanSampled2.on(), permute(quan.on(gridSampled, {'t', 'z'}), [2, 1, 3])); end - - function testSubs(testCase) %% myTestArray = ones(21, 41, 2, 3); myTestArray(:,1,1,1) = linspace(0, 1, 21); myTestArray(:,:,2,:) = 2; -quan = quantity.Discrete(myTestArray, 'gridName', {'z', 'zeta'}, 'size', [2, 3]); +d1 = quantity.Domain('grid', linspace(0, 1, 21), 'name', 'z'); +d2 = quantity.Domain('grid', linspace(0, 1, 41), 'name', 'zeta'); + +quan = quantity.Discrete(myTestArray, 'domain', [d1, d2]); + quanZetaZ = quan.subs({'z', 'zeta'}, {'zeta', 'z'}); quan10 = quan.subs({'z', 'zeta'}, {1, 0}); quan01 = quan.subs({'z', 'zeta'}, {0, 1}); @@ -1162,7 +1113,7 @@ quant1 = quan.subs({'z', 'zeta'}, {'t', 1}); quanZetaZeta = quan.subs({'z'}, 'zeta'); quanPt = quan.subs({'z'}, {1}); quanEta = quan.subs({'z'}, {'eta'}); -%% +% testCase.verifyEqual(quan10, shiftdim(quan.on({1, 0}))); testCase.verifyEqual(quan01, shiftdim(quan.on({0, 1}))); testCase.verifyEqual(quanZetaZ.on(), quan.on()); @@ -1175,9 +1126,17 @@ testCase.verifyEqual(quanZetaZeta.on(), quanZetaZetaReference); testCase.verifyEqual(quanEta(1).gridName, {'eta', 'zeta'}) testCase.verifyEqual(quanPt.on(), shiftdim(quan.on({1, quan(1).grid{2}}))); -%% 4d-case +testCase.verifyEqual( gridSize( quanZetaZeta.subs( quantity.Domain('grid', linspace(0,1,3), 'name', 'zeta')) ), 3); + + +% 4d-case myTestArray4d = rand(11, 21, 31, 41, 1, 2); -quan4d = quantity.Discrete(myTestArray4d, 'gridName', {'z', 'zeta', 'eta', 'beta'}, 'name', 'fun5d'); +x1 = quantity.Domain('grid', linspace(0, 1, 11), 'name', 'z'); +x2 = quantity.Domain('grid', linspace(0, 1, 21), 'name', 'zeta'); +x3 = quantity.Domain('grid', linspace(0, 1, 31), 'name', 'eta'); +x4 = quantity.Domain('grid', linspace(0, 1, 41), 'name', 'beta'); + +quan4d = quantity.Discrete(myTestArray4d, 'domain', [x1, x2, x3, x4], 'name', 'fun5d'); quan4dAllEta = quan4d.subs({'z', 'zeta', 'eta', 'beta'}, {'eta', 'eta', 'eta', 'eta'}); testCase.verifyEqual(reshape(quan4d.on({1, 1, 1, 1}), size(quan4d)), ... reshape(quan4dAllEta.on({1}), size(quan4dAllEta))); @@ -1185,6 +1144,8 @@ testCase.verifyEqual(reshape(quan4d.on({1, 1, 1, 1}), size(quan4d)), ... quan4dArbitrary = quan4d.subs({'z', 'zeta', 'eta', 'beta'}, {'zeta', 'beta', 'z', 1}); testCase.verifyEqual(reshape(quan4d.on({1, 1, 1, 1}), size(quan4d)), ... reshape(quan4dArbitrary.on({1, 1, 1}), size(quan4dAllEta))); + + end function testZTTimes(testCase) diff --git a/+unittests/+quantity/testDomain.m b/+unittests/+quantity/testDomain.m index 3f14eb8a4ca10a2f3192b285e2841e6a45a6d9f1..da316cad0b01cbea9e3daebea28a35ec9cf86b63 100644 --- a/+unittests/+quantity/testDomain.m +++ b/+unittests/+quantity/testDomain.m @@ -42,4 +42,94 @@ d = quantity.Domain(); testCase.verifyTrue( isempty(d) ) -end \ No newline at end of file +end + + +function testDefaultGrid(testCase) + g = quantity.Domain.defaultGrid([100, 42]); + testCase.verifyEqual(g(1).grid, linspace(0, 1, 100).'); + testCase.verifyEqual(g(2).grid, linspace(0, 1, 42).'); +end + +function testGridJoin(testCase) +s = linspace(0, 1); +z = linspace(0, 1, 71)'; +t = linspace(0, 1, 51); + +S = quantity.Domain('grid', s, 'name', 's'); +Z = quantity.Domain('grid', z, 'name', 'z'); +T = quantity.Domain('grid', t, 'name', 't'); +Ps = quantity.Domain('grid', s, 'name', 'p'); +St = quantity.Domain('grid', t, 'name', 's'); +Pt = quantity.Domain('grid', t, 'name', 'p'); + +a = [Z, T, S]; +b = [Ps, St]; +c = Pt; +% +% +% a = quantity.Discrete(cat(4, sin(Z.*Z.*S), cos(Z.*T.*S)), ... +% 'size', [2 1], 'grid', {z, t, s}, 'gridName', {'z', 't', 's'}); +% b = quantity.Discrete(ones(numel(s), numel(t)), ... +% 'size', [1 1], 'grid', {s, t}, 'gridName', {'p', 's'}); +% c = quantity.Discrete(ones(numel(t), 2, 2), ... +% 'size', [2 2], 'grid', {t}, 'gridName', {'p'}); + +joinedDomainAB = gridJoin(a, b); +joinedDomainCC = gridJoin(c, c); +joinedDomainBC = gridJoin(b, c); + +testCase.verifyEqual({joinedDomainAB.name}, {'p', 's', 't', 'z'}); +testCase.verifyEqual({joinedDomainAB.grid}, {s(:), s(:), t(:), z(:)}); +testCase.verifyEqual({joinedDomainCC.name}, {'p'}); +testCase.verifyEqual({joinedDomainCC.grid}, {t(:)}); +testCase.verifyEqual({joinedDomainBC.name}, {'p', 's'}); +testCase.verifyEqual({joinedDomainBC.grid}, {s(:), t(:)}); +end + +function testGridIndex(testCase) +z = linspace(0, 2*pi, 71)'; +t = linspace(0, 3*pi, 51); +s = linspace(0, 1); + +S = quantity.Domain('grid', s, 'name', 's'); +Z = quantity.Domain('grid', z, 'name', 'z'); +T = quantity.Domain('grid', t, 'name', 't'); + +a = [Z, T, S]; + +idx = a.gridIndex('z'); + +testCase.verifyEqual(idx, 1); + + +idx = a.gridIndex({'z', 't'}); +testCase.verifyEqual(idx, [1 2]); + +idx = a.gridIndex({'z', 's'}); +testCase.verifyEqual(idx, [1 3]); + +idx = a.gridIndex('t'); +testCase.verifyEqual(idx, 2); + +end + + + + + + + + + + + + + + + + + + + +