Commit 2cabaaab authored by Jakob Gabriel's avatar Jakob Gabriel
Browse files

revised subs: done?

parent 9fe59c24
function cellArray = str2cell(stringArray)
%STR2CELL Converts a string-array to a cell-array of that strings.
% This is usefull for sym/subs.
% Example:
% misc.str2cell(["a", "sd", "f"]);
cellArray = cell(size(stringArray));
for it = 1 : numel(stringArray)
cellArray{it} = stringArray(it);
end % for it = 1 : numel(stringArray)
end
......@@ -1121,15 +1121,12 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete ...
newDomain (1, 1) quantity.Domain;
end
if strcmp(oldDomainName, newDomain.name)
if strcmp(oldDomainName, newDomain.name) % no renaming
obj = obj.changeDomain(newDomain);
elseif any(strcmp([obj(1).domain.name], newDomain.name))
elseif any(strcmp([obj(1).domain.name], newDomain.name)) % merge two domains
obj = obj.subsDomainMerge(oldDomainName, newDomain);
else
oldDomainNameNewGrid = quantity.Domain(oldDomainName, newDomain.grid);
obj = obj.changeDomain(oldDomainNameNewGrid);
newDomainComplete = obj(1).domain.rename(newDomain.name, oldDomainName);
[obj.domain] = deal(newDomainComplete);
else % renaming of one domain neccessary
obj = obj.subsDomainRename(oldDomainName, newDomain);
end
end % subsDomain()
......@@ -1446,14 +1443,13 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete ...
assert(isequal([newDomain.name], obj(1).gridName), ...
'rearranging grids failed');
end
[newObj.domain] = deal(newDomain);
for it = 1 : numel(obj)
newObj(it).valueDiscrete = obj(it).on(newDomain);
end
end % changeDomain()
function newObj = replaceGrid(obj, myNewDomain, optArgs)
% REPALCEGRID change the grid of the quantity.
% newObj = REPLACEGRID(obj, MYNEWDOMAIN, "gridName", NEWGRIDNAME)
......@@ -2756,8 +2752,10 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete ...
% subsDomainMerge() Symbolic substitution for which 2 domains need to be merged.
% Example:
% z = quantity.Domain("z", linspace(0, 1, 11));
% zLr = quantity.Domain("z", linspace(0, 1, 5));
% zeta = quantity.Domain("zeta", linspace(0, 1, 11));
% f.subsDomainMerge("zeta", zLr) % = f(z)
% f = z.Discrete() + zeta.Discrete() % = f(z, zeta)
% f.subsDomainMerge("zeta", zLr) % = f(z)
% -> the finest grid is chosen in the latter case, hence z and not zLr.
% This is a helper function for subsDomain, hence it is protected.
......@@ -2781,6 +2779,22 @@ classdef (InferiorClasses = {?quantity.Symbolic}) Discrete ...
obj = quantity.Discrete(newValueMerged, newDomain, "name", obj(1).name);
end % subsDomainMerge()
function obj = subsDomainRename(obj, oldDomainName, newDomain)
% subsDomainMerge() Symbolic substitution for which 2 domains need to be merged.
% Example:
% z = quantity.Domain("z", linspace(0, 1, 11));
% zeta = quantity.Domain("zeta", linspace(0, 1, 11));
% f = z.Discrete() % = f(z)
% f.subsDomainRename("z", zeta) % = f(zeta)
% -> the finest grid is chosen in the latter case, hence z and not zLr.
% This is a helper function for subsDomain, hence it is protected.
oldDomainNameNewGrid = quantity.Domain(oldDomainName, newDomain.grid);
obj = obj.changeDomain(oldDomainNameNewGrid);
newDomainComplete = obj(1).domain.rename(newDomain.name, oldDomainName);
[obj.domain] = deal(newDomainComplete);
end % subsDomainRename()
function [valDiscrete] = expandValueDiscrete(obj, newDomain)
% EXPANDVALUEDISCRETE expand the discrete value on the
% newDomain
......
......@@ -234,144 +234,31 @@ classdef Symbolic < quantity.Function
c = cat@quantity.Discrete(dim, objCell{:});
end % cat()
function solution = subs(obj, gridName2Replace, values)
% SUBS substitues gridName and values
% solution = subs(obj, DOMAIN) can be used to change the grid
% of this quantity. The domains with the same domain name as
% DOMAIN will be replaced with the corresponding grid in
% DOMAIN.
%J
% solution = subs(obj, DOMAINNAME, GRID) can be used to
% change the grid of this quantity. The grid of the domains with the
% DOMAINNAME will be replaced by the values specified in
% GRID. DOMAINNAME must be a cell-array containing the names
% and GRID must be a cell-array containing the new grids.
function solution = subsNumeric(obj, oldDomainName, value)
% subsNumeric Numeric substitution.
%
% solution = sub(obj, DOMAINNAME, NEWDOMAIN) can be used to
% change the grid and/or the name of a domain. DOMAINNAME
% must be a cell-array of domain names, or a oboject-array of
% quantity.Domain objects. The NEWDOMAIN must be an
% object-array of quantity.Domain.
if isa(gridName2Replace, 'quantity.Domain')
if nargin == 2
gridName2Replace = {gridName2Replace.name};
values = {gridName2Replace.grid};
else
gridName2Replace = {gridName2Replace.name};
end
end
gridName2Replace = misc.ensureString(gridName2Replace);
if nargin == 3 && isa(values, 'quantity.Domain')
% replacement of domains:
% 1) replace the domain
assert( numel(gridName2Replace) == numel(values) );
for it = 1:numel(gridName2Replace)
newDomain(it) = values(it).rename(gridName2Replace(it));
end
solution = obj.changeDomain( newDomain );
% 2) replace the name
solution = solution.subs( gridName2Replace, {values.name} );
return
end
values = misc.ensureIsCell(values);
isNumericValue = false(numel(values), 1);
for it = 1 : numel(values)
% check which values are substitutions with numerical values in the domain
if isnumeric(values{it})
isNumericValue(it) = true;
end
end % for it = 1 : numel(values)
% subsNumeric(obj, oldDomainName, value) replaces the old domains of obj specified by the
% string-array oldDomainName with the numeric values in value.
%
% Example: subs(f(z, zeta, t), ["z", "zeta"], [1, 2]) = f(1, 2, t) = g(t).
% new domain is the old one without the domains that are evaluated at specific numeric
% values. Furthermore, the other domains are renamed.
newDomain = obj(1).domain.remove(gridName2Replace(isNumericValue));
if any(~isNumericValue)
% rename domains that are affected by symbolic substitution
gridName2ReplaceSymbolicOld = gridName2Replace(~isNumericValue);
gridName2ReplaceSymbolicNew = string(values(~isNumericValue));
resultingName = [newDomain.name];
for it = 1 : numel(gridName2ReplaceSymbolicOld)
resultingName(strcmp([newDomain.name], gridName2ReplaceSymbolicOld(it))) ...
= gridName2ReplaceSymbolicNew(it);
end % for it = 1 : numel(gridName2ReplaceSymbolicOld)
if misc.isunique(resultingName)
newDomain = newDomain.rename(gridName2ReplaceSymbolicNew, gridName2ReplaceSymbolicOld);
else % complicated case, for instance f(x, y).subs("x", "y") = f(y, y)
% for this, join is used.
for it = 1 : numel(gridName2ReplaceSymbolicNew)
if any(strcmp([newDomain.name], gridName2ReplaceSymbolicNew(it)))
% remove domain to be removed and join it with the single renamed domain.
newDomain = join(newDomain.remove(gridName2ReplaceSymbolicOld(it)), ...
newDomain.find(gridName2ReplaceSymbolicOld(it)).rename(...
gridName2ReplaceSymbolicNew(it)));
else
newDomain = newDomain.rename(...
gridName2ReplaceSymbolicNew(it), gridName2ReplaceSymbolicOld(it));
end
end % for it = 1 : numel(gridName2ReplaceSymbolicNew)
end
% convert elements of values to symbolic expressions
for it = 1 : numel(values)
if ~isnumeric(values{it})
values{it} = sym(values{it});
end
end % for it = 1 : numel(values)
end
arguments
obj quantity.Discrete;
oldDomainName (:, 1) string;
value (:, 1) double;
end % arguments
if numel(newDomain) > 0
solution = quantity.Symbolic(...
subs(obj.sym(), sym(gridName2Replace), values), ...
newDomain, ...
'name', obj(1).name);
else
solution = double(subs(obj.sym(), sym(gridName2Replace), values));
end
end % subs()
function solution = subsValueWithArray(obj, gridName, values)
% This function substitutes the variables specified with
% gradName with values. Values must be a numerical scalar for
% each gridName.
% This method works for value-arrays but not for obj-arrays.
% In contrast to on() or at(), only some, but not necessarily
% all variables are evaluated.
if numel(obj) ~= 1
error('Yet this method is not implemented for quanitity arrays');
end
if ~iscell(gridName)
gridName = {gridName};
end
if ~iscell(values)
values = {values};
end
if numel(values) ~= numel(gridName)
error(['Number of variables to be substituted and number ', ...
'of values do not coincide']);
end
if isa(values{1}, 'sym')
error('subs is yet only implemented for numeric values');
end
valueSize = [cellfun(@numel, values), 1];
if all(strcmp(obj(1).gridName, gridName))
% if all grids are evaluated, solution is a double array.
solution = obj.at(values);
else
symbolicSolution = reshape(subs(obj.valueSymbolic, ...
obj(1).variable(strcmp(obj(1).gridName, gridName)), values{:}), valueSize);
solution = quantity.Symbolic(symbolicSolution, ...
'variable', obj(1).variable(~strcmp(obj(1).gridName, gridName)),...
'gridName', obj(1).gridName(~strcmp(obj(1).gridName, gridName)), ...
'grid', obj(1).grid(~strcmp(obj(1).gridName, gridName)));
assert(numel(oldDomainName) == numel(value), "for every domain to be substituted, ", ...
"there must be defined one numeric scalar value");
remainingDomains = obj(1).domain.remove(oldDomainName);
solution = subs(obj.sym, misc.str2cell(oldDomainName), value);
if ~isempty(remainingDomains)
solution = quantity.Symbolic(solution, remainingDomains, "name", obj(1).name);
else
solution = double(solution);
end
end % subsValueWithArray()
end % subsNumeric()
function solution = solveAlgebraic(obj, rhs, gridName, varargin)
%% this method solves
......@@ -802,7 +689,49 @@ classdef Symbolic < quantity.Function
result(l).valueSymbolic = diff(obj(l).valueSymbolic, diffGridName, k);
result(l).valueContinuous = obj.setValueContinuous(result(l).valueSymbolic, [obj(1).domain.name]);
end
end
end % diff_inner()
function obj = subsDomainMerge(obj, oldDomainName, newDomain)
% subsDomainMerge() Symbolic substitution for which 2 domains need to be merged.
% Example:
% z = quantity.Domain("z", linspace(0, 1, 11));
% zLr = quantity.Domain("z", linspace(0, 1, 5));
% zeta = quantity.Domain("zeta", linspace(0, 1, 11));
% f = z.Discrete() + zeta.Discrete() % = f(z, zeta)
% f.subsDomainMerge("zeta", zLr) % = f(z)
% -> the finest grid is chosen in the latter case, hence z and not zLr.
% This is a helper function for subsDomain, hence it is protected.
% find domain to be merged
[idxOldDomain, idxOldDomainLogical] = gridIndex(obj(1).domain, oldDomainName);
[idxNewDomain, idxNewDomainLogical] = gridIndex(obj(1).domain, newDomain.name);
% pick finest grid.
newDomain = join(join(newDomain, obj(1).domain(idxNewDomain)), ...
obj(1).domain(idxOldDomain).rename(newDomain.name));
newDomainComplete = [newDomain, obj(1).domain(~(idxOldDomainLogical | idxNewDomainLogical))];
% create new obj
obj = quantity.Symbolic(subs(obj.sym, oldDomainName, newDomain.name), ...
newDomainComplete, "name", obj(1).name);
end % subsDomainMerge()
function obj = subsDomainRename(obj, oldDomainName, newDomain)
% subsDomainMerge() Symbolic substitution for which 2 domains need to be merged.
% Example:
% z = quantity.Domain("z", linspace(0, 1, 11));
% zeta = quantity.Domain("zeta", linspace(0, 1, 11));
% f = z.Discrete() % = f(z)
% f.subsDomainRename("z", zeta) % = f(zeta)
% -> the finest grid is chosen in the latter case, hence z and not zLr.
% This is a helper function for subsDomain, hence it is protected.
oldDomainNameNewGrid = quantity.Domain(oldDomainName, newDomain.grid);
obj = obj.changeDomain(oldDomainNameNewGrid);
newDomainComplete = obj(1).domain.rename(newDomain.name, oldDomainName);
obj = quantity.Symbolic(subs(obj.sym, oldDomainName, newDomain.name), ...
newDomainComplete, "name", obj(1).name);
end % subsDomainRename()
end % (Access = protected)
......@@ -835,6 +764,7 @@ classdef Symbolic < quantity.Function
end % methods (Static)
methods (Static, Access = protected)
function var = getVariable(symbolicFunction, nVar)
% nVar is needed for overloaded functionality in PolynomialOperator
......
......@@ -4,6 +4,12 @@ function [tests] = testMisc()
tests = functiontests(localfunctions());
end
function testStr2cell(tc)
testData = ["a", "sd", "f"; "bli", "asdf", "blub"];
testDataCell = {"a", "sd", "f"; "bli", "asdf", "blub"};
tc.verifyEqual(misc.str2cell(testData), testDataCell);
end % testStr2celll(tc)
function testGetDimensions(tc)
d = quantity.Domain("z", linspace(0, 1, 3));
......
This diff is collapsed.
......@@ -4,6 +4,60 @@ function [tests ] = testSymbolic()
tests = functiontests(localfunctions());
end
function testSubsDomain(tc)
z = quantity.Domain("z", linspace(0, 1, 11));
zeta = quantity.Domain("zeta", linspace(0, 1, 11));
zLr = quantity.Domain("z", linspace(0, 1, 5));
eta = quantity.Domain("eta", linspace(0, 1, 11));
f = z.Symbolic() * zeta.Symbolic();
%
fofZEta = f.subsDomain("zeta", eta); % = f(z, eta)
tc.verifyEqual(fofZEta.on(), f.on());
tc.verifyEqual([fofZEta(1).domain.name], ["z", "eta"]);
%
fofZlrZeta = f.subsDomain("z", zLr); % = f(z, zeta)
tc.verifyEqual(fofZlrZeta.on(), f.on([zLr, zeta]));
tc.verifyEqual([fofZlrZeta(1).domain.name], ["z", "zeta"]);
%
fofZ = f.subsDomain("zeta", zLr); % = f(z)
tc.verifyEqual(fofZ.on(), diag(f.on([z, quantity.Domain("zeta", z.grid)])));
tc.verifyEqual([fofZ(1).domain.name], "z");
end % testSubsDomain()
function testSubsNumeric(tc)
z = quantity.Domain("z", linspace(0, 1, 5));
zeta = quantity.Domain("zeta", linspace(0, 1, 4));
t = quantity.Domain("t", linspace(0, 1, 6));
quanScalar = z.Symbolic() + 1 + zeta.Symbolic() * t.Symbolic();
tc.verifyEqual(quanScalar.subsNumeric(["z", "t"], [0, 0]).on(), ones(zeta.n, 1));
tc.verifyEqual(quanScalar.subsNumeric(["t", "z", "zeta"], [0.1, 0.2, 0.3]), 0.2 + 1 + 0.3 * 0.1);
quanMatrix = quantity.Symbolic(...
[sym("z") + 1 + sym("zeta") * sym("t"), 1; ...
sym("z") + sym("t"), -sym("zeta")], [zeta, t, z]);
tc.verifyEqual(quanMatrix.subsNumeric(["z", "zeta", "t"], [0.1, 0.2, 0.3]), ...
[0.1 + 1 + 0.2 * 0.3, 1; 0.1 + 0.3, -0.2], "AbsTol", 10*eps);
tc.verifyEqual(quanMatrix.subsNumeric(["z", "zeta", "t"], [0, 0, 0]), [1, 1; 0, 0]);
tc.verifyEqual(quanMatrix.subsNumeric(["z", "zeta", "t"], [1, 1, 1]), [3, 1; 2, -1]);
tc.verifyEqual(quanMatrix(1, 1).subsNumeric(["z", "zeta", "t"], [0.1, 0.2, 0.3]), ...
0.1 + 1 + 0.2 * 0.3, "AbsTol", 10*eps);
end % testSubsNumeric()
function testChangeDomain(tc)
zeta = quantity.Domain("zeta", linspace(0, 1, 4));
t = quantity.Domain("t", linspace(0, 1, 4));
thisSym = sym("zeta") + sym("t");
quan = quantity.Symbolic(thisSym, [zeta, t]);
newDomain = [quantity.Domain("zeta", linspace(0, 1, 11)), quantity.Domain("t", linspace(0, 1, 21))];
newQuan = quan.changeDomain(newDomain);
tc.verifyEqual(newQuan.on(), quan.on(newDomain));
tc.verifyTrue(isa(newQuan, "quantity.Symbolic"));
end % testChangeDomain()
function testOn2(tc)
zeta = quantity.Domain("zeta", linspace(0, 1, 4));
t = quantity.Domain("t", linspace(0, 1, 4));
......@@ -1051,7 +1105,8 @@ function testSubs3(tc)
Id_zetaZ = quantity.Symbolic( [sym("zeta"), 1; 2 sym("z")], zZeta([2 1]));
tc.verifyEqual(Id.subs("z", 1).on(), Id_z1.on(), "AbsTol", 10*eps);
tc.verifyEqual( Id.subs({"z", "zeta"}, {"zeta", "z"}).on(), Id_zetaZ.on())
tc.verifyEqual( Id.subs(["z", "zeta"], ["zeta", "z"]).on(), Id_zetaZ.on())
tc.verifyEqual( Id.subs(["z", "zeta"], "zeta", "z").on(), Id_zetaZ.on())
end % testSubs3()
function testSubs2(tc)
......@@ -1077,19 +1132,21 @@ Ffz = quantity.Symbolic([z, z^2; z+z, 1], Z);
Fyx = quantity.Symbolic([w, x^2; x+w, 1], [X W]);
% test 1: replace all domains by numerical values
fOf1 = f.subs({"x", "y"}, {1, 1});
fOf1 = f.subs(["x", "y"], 1, 1);
% test 2: replace one domain by a numerical value
fOf2 = f.subs({"x"}, {1});
fOf2 = f.subs("x", 1);
Z0 = quantity.Domain("z", 0);
fOfz = f.subs([X, Y], [Z Z]);
fOfyx = f.subs({"x", "y"}, {"w", "x"});
fOfz = f.subs([X.name, Y.name], [Z Z]);
fOfyx = f.subs(["x", "y"], "w", "x");
fOfyx2 = f.subs(["x", "y"], ["w", "x"]);
tc.verifyEqual(Ff1, fOf1);
tc.verifyEqual(Ffz.sym(), fOfz.sym());
tc.verifyEqual(Fyx.sym(), fOfyx.sym());
tc.verifyEqual(Fyx.sym(), fOfyx2.sym());
%%
syms z zeta
......@@ -1101,8 +1158,8 @@ tc.verifyEqual(Feta(1).gridName, ["eta", "zeta"]);
%%
fMessy = quantity.Symbolic(x^1*y^2*z^4*w^5, [X, Y, Z, W]);
fMessyA = fMessy.subs({"x", "y", "z", "w"}, {"a", "a", "a", "a"});
fMessyYY = fMessy.subs({"w", "x", "z"}, {"xi", "y", "y"});
fMessyA = fMessy.subs(["x", "y", "z", "w"], ["a", "a"], "a", "a");
fMessyYY = fMessy.subs(["w", "x", "z"], "xi", "y", "y");
tc.verifyEqual(fMessyA.gridName, "a");
tc.verifyEqual(fMessyA.grid, {linspace(0, 1, 11)'});
......
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