gusucode.com > symbolic工具箱matlab源码程序 > symbolic/solve.m
function varargout = solve(varargin) %SOLVE Symbolic solution of algebraic equations. % S = SOLVE(eqn1,eqn2,...,eqnM,var1,var2,...,varN) % S = SOLVE(eqn1,eqn2,...,eqnM,var1,var2,...,varN,'ReturnConditions',true) % % [S1,...,SN] = SOLVE(eqn1,eqn2,...,eqnM,var1,var2,...,varN) % [S1,...,SN,params,conds] = SOLVE(eqn1,...,eqnM,var1,var2,...,varN,'ReturnConditions',true) % % The eqns are symbolic expressions, equations, or inequalities. The % vars are symbolic variables specifying the unknown variables. % If the expressions are not equations or inequalities, % SOLVE seeks zeros of the expressions. % Otherwise SOLVE seeks solutions. % If not specified, the unknowns in the system are determined by SYMVAR, % such that their number equals the number of equations. % If no analytical solution is found, a numeric solution is attempted; % in this case, a warning is printed. % % Three different types of output are possible. For one variable and one % output, the resulting solution is returned, with multiple solutions to % a nonlinear equation in a symbolic vector. For several variables and % several outputs, the results are sorted in the same order as the % variables var1,var2,...,varN in the call to SOLVE. In case no variables % are given in the call to SOLVE, the results are sorted in lexicographic % order and assigned to the outputs. For several variables and a single % output, a structure containing the solutions is returned. % % SOLVE(...,'ReturnConditions', VAL) controls whether SOLVE should in % addition return a vector of all newly generated parameters to express % infinite solution sets and about conditions on the input parameters % under which the solutions are correct. % If VAL is TRUE, parameters and conditions are assigned to the last two % outputs. Thus, if you provide several outputs, their number must equal % the number of specified variables plus two. % If you provide a single output, a structure is returned % that contains two additional fields 'parameters' and 'conditions'. % No numeric solution is attempted even if no analytical solution is found. % If VAL is FALSE, then SOLVE may warn about newly generated parameters or % replace them automatically by admissible values. It may also fall back % to the numerical solver. % The default is FALSE. % % SOLVE(...,'IgnoreAnalyticConstraints',VAL) controls the level of % mathematical rigor to use on the analytical constraints of the solution % (branch cuts, division by zero, etc). The options for VAL are TRUE or % FALSE. Specify FALSE to use the highest level of mathematical rigor % in finding any solutions. The default is FALSE. % % SOLVE(...,'PrincipalValue',VAL) controls whether SOLVE should return multiple % solutions (if VAL is FALSE), or just a single solution (when VAL is TRUE). % The default is FALSE. % % SOLVE(...,'IgnoreProperties',VAL) controls if SOLVE should take % assumptions on variables into account. VAL can be TRUE or FALSE. % The default is FALSE (i.e., take assumptions into account). % % SOLVE(...,'Real',VAL) allows to put the solver into "real mode." % In "real mode," only real solutions such that all intermediate values % of the input expression are real are searched. VAL can be TRUE or FALSE. % The default is FALSE. % % SOLVE(...,'MaxDegree',n) controls the maximum degree of polynomials % for which explicit formulas will be used during the computation. % n must be a positive integer. The default is 3. % % Example 1: % syms p x r % solve(p*sin(x) == r) chooses 'x' as the unknown and returns % % ans = % asin(r/p) % pi - asin(r/p) % % Example 2: % syms x y % [Sx,Sy] = solve(x^2 + x*y + y == 3,x^2 - 4*x + 3 == 0) returns % % Sx = % 1 % 3 % % Sy = % 1 % -3/2 % % Example 3: % syms x y % S = solve(x^2*y^2 - 2*x - 1 == 0,x^2 - y^2 - 1 == 0) returns % the solutions in a structure. % % S = % x: [8x1 sym] % y: [8x1 sym] % % Example 4: % syms a u v % [Su,Sv] = solve(a*u^2 + v^2 == 0,u - v == 1) regards 'a' as a % parameter and solves the two equations for u and v. % % Example 5: % syms a u v w % S = solve(a*u^2 + v^2,u - v == 1,a,u) regards 'v' as a % parameter, solves the two equations, and returns S.a and S.u. % % When assigning the result to several outputs, the order in which % the result is returned depends on the order in which the variables % are given in the call to solve: % [U,V] = solve(u + v,u - v == 1, u, v) assigns the value for u to U % and the value for v to V. In contrast to that % [U,V] = solve(u + v,u - v == 1, v, u) assigns the value for v to U % and the value of u to V. % % Example 6: % syms a u v % [Sa,Su,Sv] = solve(a*u^2 + v^2,u - v == 1,a^2 - 5*a + 6) solves % the three equations for a, u and v. % % Example 7: % syms x % S = solve(x^(5/2) == 8^(sym(10/3))) returns all three complex solutions: % % S = % 16 % - 4*5^(1/2) - 4 + 4*2^(1/2)*(5 - 5^(1/2))^(1/2)*i % - 4*5^(1/2) - 4 - 4*2^(1/2)*(5 - 5^(1/2))^(1/2)*i % % Example 8: % syms x % S = solve(x^(5/2) == 8^(sym(10/3)), 'PrincipalValue', true) % selects one of these: % % S = % - 4*5^(1/2) - 4 + 4*2^(1/2)*(5 - 5^(1/2))^(1/2)*i % % Example 9: % syms x % S = solve(x^(5/2) == 8^(sym(10/3)), 'IgnoreAnalyticConstraints', true) % ignores branch cuts during internal simplifications and, in this case, % also returns only one solution: % % S = % 16 % % Example 10: % syms x % S = solve(sin(x) == 0) returns 0 % % S = solve(sin(x) == 0, 'ReturnConditions', true) returns a structure expressing % the full solution: % % S.x = k*pi % S.parameters = k % S.conditions = in(k, 'integer') % % Example 11: % syms x y real % [S, params, conditions] = solve(x^(1/2) = y, x, 'ReturnConditions', true) % assigns solution, parameters and conditions to the outputs. % In this example, no new parameters are needed to express the solution: % % S = % y^2 % % params = % Empty sym: 1-by-0 % % conditions = % 0 <= y % % Example 12: % syms a x y % [x0, y0, params, conditions] = solve(x^2+y, x, y, 'ReturnConditions', true) % generates a new parameter z to express the infinitely many solutions. % This z can be any complex number, both solutions are valid without % restricting conditions: % % x0 = % -(-z)^(1/2) % (-z)^(1/2) % % y0 = % z % z % % params = % z % % conditions = % true % true % % Example 13: % syms t positive % solve(t^2-1) % % ans = % 1 % % solve(t^2-1, 'IgnoreProperties', true) % % ans = % 1 % -1 % % Example 14: % solve(x^3-1) returns all three complex roots: % % ans = % 1 % - 1/2 + (3^(1/2)*i)/2 % - 1/2 - (3^(1/2)*i)/2 % % solve(x^3-1, 'Real', true) only returns the real root: % % ans = % 1 % % See also DSOLVE, SUBS. % Copyright 1993-2015 The MathWorks, Inc. eng = symengine; % split the input into equations, variables, and options [eqns,vars,options] = getEqns(varargin{:}); % parse the options boolvalidator = @(X) X==sym(true) || X==sym(false); intvalidator = @(X) logical(feval(symengine, 'testtype', X, 'Type::PosInt')); parser = inputParser; addParameter(parser, 'IgnoreAnalyticConstraints', false); addParameter(parser, 'IgnoreProperties', false, boolvalidator); addParameter(parser, 'PrincipalValue', false, boolvalidator); addParameter(parser, 'Real', false, boolvalidator); addParameter(parser, 'ReturnConditions', false, boolvalidator); addParameter(parser, 'MaxDegree', sym(2), intvalidator); parser.parse(options{:}); options = parser.Results; options.IgnoreAnalyticConstraints = sym.checkIgnoreAnalyticConstraintsValue(options.IgnoreAnalyticConstraints); % initialize output structure varargout = cell(1, nargout); if isempty(eqns) error(message('symbolic:solve:NoEquation')) end if isempty(vars) % The variables have not been given by the user if nargout <=1 N = numel(eqns); elseif logical(options.ReturnConditions) N = nargout-2; if N == 0 error(message('symbolic:solve:NargoutReturnConditions')) end else N = nargout; end vars = sort(symvar(eqns, N)); if isempty(vars) error(message('symbolic:solve:NoVariableGiven')) end if numel(vars) < N && nargout > 1 error(message('symbolic:dsolve:errmsg5', numel(vars), N)); end elseif nargout > 1 % The number of outputs must match the number of explicitly given vars if logical(options.ReturnConditions) N = nargout - 2; else N = nargout; end if N ~= numel(vars) error(message('symbolic:dsolve:errmsg5', numel(vars), N)) end end if any(vars == sym('parameters')) || any(vars == sym('conditions')) % it is not allowed to solve for 'parameters' and 'conditions' error(message('symbolic:solve:VarnameParametersOrConditions')) end % convert the options into MuPAD's table format solveOptions = struct2sym(options); % the main work is done in the MuPAD solver sol = eng.feval('solve', eqns, vars, solveOptions); % Check whether we have found a symbolic solution. % In compatible mode, resort to a numerical solver if not. numericMode = false; if sol == eng.evalin('FAIL') if ~logical(options.ReturnConditions) sol = eng.feval('solve::float', eqns, vars, solveOptions); if numel(sol) > 0 % We have found a numerical solution, and warn that it is only numerical. warning(message('symbolic:solve:FallbackToNumerical')); % The solution is a DOM_SET. Extract its first element. sol = feval(symengine, 'op', sol, 1); if numel(sol) > 1 % The solution is a vector. Put it inside a list sol = eng.feval('DOM_LIST', sol); end numericMode = true; end end if ~numericMode % numeric solution is unwanted or has not been found % return empty sym below warning(message('symbolic:solve:warnmsg3')); sol = eng.evalin('[]'); end end if numericMode % A numerical solution never depends on parameters and conditions solutions = sol; parameters = sym(zeros(1, 0)); conditions = eng.evalin('TRUE'); else % sol is a list of lists [expression, parameters, condition] parameterMatrix = eng.feval('map', sol, '_index', sym(2)); parameters = sym(zeros(1, 0)); for i=1:numel(parameterMatrix) parameters = union(parameters, parameterMatrix(i)); end conditions = transpose(eng.feval('map', sol, '_index', sym(3))); solutions = eng.feval('map', sol, '_index', sym(1)); end if nargout <= 1 if numel(vars) == 1 if logical(options.ReturnConditions) varargout{1}.(char(vars)) = transpose(solutions); else varargout{1} = transpose(solutions); end else % output format for systems for i=1:numel(vars) varargout{1}.(char(vars(i))) = transpose(eng.feval('map', solutions, '_index', i)); end end if logical(options.ReturnConditions) varargout{1}.parameters = parameters; varargout{1}.conditions = conditions; else warnIfParams(parameters, conditions); end elseif ~logical(options.ReturnConditions) for i=1:numel(vars) varargout{i} = transpose(eng.feval('map', solutions, '_index', i)); end warnIfParams(parameters, conditions); else if numel(vars) == 1 varargout{1} = transpose(solutions); else for i=1:numel(vars) varargout{i} = transpose(eng.feval('map', solutions, '_index', i)); end end % parameters: varargout{numel(vars)+1} = parameters; % conditions: varargout{numel(vars)+2} = conditions; end % end of the main program % local methods % getEqns - split the input into equations, variables, and options function [eqns,vars,options] = getEqns(varargin) % process strings, even when they contain several arguments argv = varargin; stringInput = false; k=1; % preprocess strings, and seek the first option while k <= numel(argv) && ~isOption(argv{k}) a = argv{k}; if isa(a, 'logical') % handle true and false in a special way % we do not want them to become 1 and 0 if all(a(:)) argv{k} = evalin(symengine, 'TRUE'); else argv{k} = evalin(symengine, 'FALSE'); end else if ischar(a) stringInput = true; end a = sym(a); if strcmp(char(feval(symengine, 'type', a)), '_exprseq') % replace the k-th input by the elements of the sequence a = num2cell(children(a)); argv = [argv(1:k-1), a, argv(k+1:end)]; % in this case, we should not increase k, as we have to look % at the elements of the sequence individually again k = k-1; elseif numel(a) == 0 error(message('symbolic:solve:EmptyEquationOrVariable')) else % accept also higher-dimensional objects and column vectors if ~isa(a, 'symfun') && numel(a) > 1 a = reshape(a, 1, numel(a)); end argv{k} = a; end end k = k+1; end if k == 1 % already the first argument is an option error(message('symbolic:solve:NoEquation')) end % consider all following arguments as options options = argv(k:end); argv = argv(1:k-1); % convert the 1st, 3rd, etc. element of options to strings, % but leave the option values as syms for k=1:2:numel(options) options{k} = char(options{k}); if stringInput && strcmpi(options{k}, 'ReturnConditions') && ... k < numel(options) && options{k+1} == true % For option ReturnConditions, we do not allow string input error(message('symbolic:solve:DeprecateStringInputError')) end end % throw warning if some argument has been passed as a string if stringInput warning(message('symbolic:solve:DeprecateStringInputWarning')); end % use function getEqnsVars to distinguish equations from variables % we do not warn anymore if the number of equations differs from the number % of variables [eqns, vars] = sym.getEqnsVars(argv{:}); % local function isOption % returns true if its argument is a solve option % we cannot test equality in the sense of validateString because we do not % want to identify 'm' with 'MaxDegree' function b = isOption(a) b = ~isa(a, 'logical') && any(strcmpi(char(a), ... {'MaxDegree', 'IgnoreAnalyticConstraints', 'IgnoreProperties', ... 'PrincipalValue', 'Real', 'ReturnConditions'})); % local function struct2sym % converts a MATLAB struct containing all options into a MuPAD table function T = struct2sym(s) f = fieldnames(s); entries = sym(zeros(1, numel(f)+1)); for i=1:numel(f) v = s.(f{i}); if strcmp(f{i}, 'MaxDegree') v = sym(v); elseif strcmp(f{i}, 'ReturnConditions') f{i} = 'OutputType'; if v == true v = evalin(symengine, '"FullMode"'); else v = evalin(symengine, '"CompatibleMode"'); end elseif v == true v = evalin(symengine, 'TRUE'); else % v is false v = evalin(symengine, 'FALSE'); end entries(i) = feval(symengine, '_equal', sym(f{i}), v); end entries(end) = evalin(symengine, 'VectorFormat = TRUE'); entries = feval(symengine, 'op', entries); T = feval(symengine, 'table', entries); % local function warnIfParams % warn if the solution depends on parameters and conditions function warnIfParams(parameters, conditions) if ~isempty(parameters) paramstring = char(parameters(1)); for i = 2:numel(parameters) paramstring = [paramstring ', ' char(parameters(i))]; %#ok end warning(message('symbolic:solve:ParametrizedFamily', sprintf([paramstring '.\n']))); end if any(conditions ~= evalin(symengine, 'TRUE')) conditionstring = char(conditions(1)); for i=2:numel(conditions) conditionstring = [conditionstring '; \n' char(conditions(i)) ]; %#ok end warning(message('symbolic:solve:SolutionsDependOnConditions', sprintf([conditionstring '.\n']))); end