gusucode.com > 模糊控制工具箱 fuzzy logic toolbox源码程序 > fuzzy/fuzzy/parsrule.m

    function [outFis,outTxtRuleList,errorStr]=parsrule(fis,inTxtRuleList,ruleFormat,lang)
%PARSRULE Parse fuzzy rules.
%   This function parses the text that defines the rules (txtRuleList) for a 
%   fuzzy system (fis) and returns a FIS structure with the appropriate rule 
%   list in place. If the original input FIS structure, fis, has any rules 
%   initially, they are replaced in the new matrix fis2. Three different rule 
%   formats (indicated by ruleFormat) are supported: *verbose*, *symbolic*, and 
%   *indexed*. The default format is *verbose*. When the optional language 
%   argument (lang) is used, the rules are parsed in verbose mode, assuming the 
%   key words are in the language, lang. This language must be either 
%   'english', 'francais', or 'deutsch'. The key language words in English are: 
%   IF, THEN, IS, AND, OR, and NOT. 
%   
%   outFIS = PARSRULE(inFIS,inRuleList) parses the rules in the string
%   matrix inRuleList and returns the updated FIS matrix outFIS. 
%
%   outFIS = PARSRULE(inFIS,inRuleList,ruleFormat) parses the rules using
%   the given ruleFormat.
%
%   outFIS = PARSRULE(inFIS,inRuleList,ruleFormat,lang) parses the 
%   rules in verbose mode assuming the key words are in the language, lang.
%
%   For example:
%
%           a=newfis('tipper');
%           a=addvar(a,'input','service',[0 10]);
%           a=addmf(a,'input',1,'poor','gaussmf',[1.5 0]);
%           a=addvar(a,'input','food',[0 10]);
%           a=addmf(a,'input',2,'rancid','trapmf',[-2 0 1 3]);
%           a=addvar(a,'output','tip',[0 30]);
%           a=addmf(a,'output',1,'cheap','trimf',[0 5 10]);
%           rule1='if service is poor or food is rancid then tip is cheap';
%           a=parsrule(a,rule1);
%           showrule(a)
%   
%   See also ADDRULE, RULEEDIT, SHOWRULE.

%   [outFIS,outRuleList,errorStr] = PARSRULE(...) returns the text version
%   of the newly-parsed rules. If there have been errors in the parsing,
%   the error message is returned in errorStr, and the # character is placed
%   at the beginning of the offending line in outRuleList.
%
%   Ned Gulley, 4-27-94
%   Copyright 1994-2002 The MathWorks, Inc. 
%   $Revision: 1.29 $  $Date: 2002/04/19 02:49:30 $

if nargin<3,
    ruleFormat='verbose';
end

if nargin<4,
    lang='english';
end
outTxtRuleList=[];
errorStr=[];
if ~strcmp(ruleFormat,'indexed'),
    if strcmp(lang,'english'),
        ifStr=' if ';
        andStr='and';
        orStr='or';
        thenStr='then';
        equalStr=' is ';
        isStr=' is ';
        notStr='not';
    elseif strcmp(lang,'francais'),
        ifStr=' si ';
        andStr='et';
        orStr='ou';
        thenStr='alors';
        equalStr=' est ';
        isStr=' est ';
        notStr='n''est_pas';
    elseif strcmp(lang,'deutsch'),
        ifStr=' wenn ';
        andStr='und';
        orStr='oder';
        thenStr='dann';
        equalStr=' ist ';
        isStr=' ist ';
        notStr='nicht';
    elseif strcmp(lang,'svenska'),
        ifStr=' om ';
        andStr='och';
        orStr='eller';
        thenStr='innebar_att';
        equalStr=' aer ';
        isStr=' aer ';
        notStr='inte';
    end
end

% Determine all the necessary constants
numInputs=length(fis.input);
numOutputs=length(fis.output);

for i=1:length(fis.input)
 numInputMFs(i)=length(fis.input(i).mf);
end
numOutputMFs=[];
for i=1:length(fis.output)
 numOutputMFs(i)=length(fis.output(i).mf);
end

if isempty(inTxtRuleList),
    outFis=setfis(fis,'ruleList',[]);
    return
end

% Remove any rows that are nothing but blanks
index=find(inTxtRuleList==0);
inTxtRuleList(index)=32*ones(size(index));
inTxtRuleList(all(inTxtRuleList'==32),:)=[];
if isempty(inTxtRuleList),
    outFis=setfis(fis,'ruleList',[]);
    return
end

% Replace all punctuation (and zeros) with spaces (ASCII 32)
inTxtRuleList2=inTxtRuleList;
index=[
    find(inTxtRuleList2==',')
    find(inTxtRuleList2=='#')
    find(inTxtRuleList2==':')
    find(inTxtRuleList2=='(')
    find(inTxtRuleList2==')')];
inTxtRuleList2(index)=32*ones(size(index));
inTxtRuleList2(all(inTxtRuleList2'==32),:)=[];

if isempty(inTxtRuleList2),
    outFis=setfis(fis,'ruleList',[]);
    return
end

inTxtRuleList2=setstr(inTxtRuleList2);
numRules=size(inTxtRuleList2,1);

if strcmp(ruleFormat,'symbolic'),
    symbFlag=1;
else
    symbFlag=0;
end

if strcmp(ruleFormat,'indexed'),
    ruleList=zeros(numRules,numInputs+numOutputs+2);
    errRuleIndex=[];
    goodRuleIndex=[];
    errorFlag=0;
    for ruleIndex=1:numRules,
        str=['[' inTxtRuleList2(ruleIndex,:) ']'];
        rule=eval(str,'[]');
        if isempty(rule),
            errorStr=['Rule did not evaluate properly.'];
            if nargout<3,
                error(errorStr);
            else
                errorFlag=1;
            end
        elseif length(rule)<(numInputs+numOutputs)+2,
            errorStr=['Not enough columns for this rule. The rule is not complete.'];
            if nargout<3,
                error(errorStr);
            else
                errorFlag=1;
            end
        elseif length(rule)>(numInputs+numOutputs)+2,
            errorStr=['Too many columns for this rule.'];
            if nargout<3,
                error(errorStr);
            else
                errorFlag=1;
            end
        elseif any(abs(rule(1:numInputs))>numInputMFs),
            errorStr=['Input MF index is too high'];
            if nargout<3,
                error(errorStr);
            else
                errorFlag=1;
            end
        elseif any(abs(rule((1:numOutputs)+numInputs))>numOutputMFs),
            errorStr=['Output MF index is too high'];
            if nargout<3,
                error(errorStr);
            else
                errorFlag=1;
            end
        elseif rule(numOutputs+numInputs+1)>1 | rule(numOutputs+numInputs+1)<0,
            errorStr=['Rule weight must be between 0 and 1'];
            if nargout<3,
                error(errorStr);
            else
                errorFlag=1;
            end
        elseif rule(numOutputs+numInputs+2)~=1 & rule(numOutputs+numInputs+2)~=2,
            errorStr=['Fuzzy operator must be either 1 (AND) or 2 (OR)'];
            if nargout<3,
                error(errorStr);
            else
                errorFlag=1;
            end
        else
            ruleList(ruleIndex,:)=rule;
        end

        if errorFlag,
            errRuleIndex=[errRuleIndex ruleIndex];
        else
            goodRuleIndex=[goodRuleIndex ruleIndex];
        end
        errorFlag=0;
    end

else
    % symbolic format is first converted to verbose and then
    % everything is parsed as verbose text

    inLabels=lower(getfis(fis,'inLabels'));
    inMFLabels=lower(getfis(fis,'inMFLabels'));
    outLabels=lower(getfis(fis,'outLabels'));
    outMFLabels=lower(getfis(fis,'outMFLabels'));

    % String pre-processing
    % Use "lower" to make it case insensitive
    inTxtRuleList2=lower(inTxtRuleList2);

    antCode=zeros(numRules,numInputs);
    consCode=zeros(numRules,numOutputs);
    andOrCode=ones(numRules,1);
    wtCode=ones(numRules,1);

    errorFlag=0;
    errRuleIndex=[];
    goodRuleIndex=[];

    for ruleIndex=1:numRules,
        txtRule=inTxtRuleList2(ruleIndex,:);

        if symbFlag
            % If the rules are in symbolic format, a little pre-processing
            % will save the day
            txtRule=strrep(txtRule,'=>',[' ' thenStr ' ']);
            txtRule=strrep(txtRule,'&',[' ' andStr ' ']);
            txtRule=strrep(txtRule,'|',[' ' orStr ' ']);
            txtRule=strrep(txtRule,'~=',[' ' notStr ' ']);
            txtRule=strrep(txtRule,'=',' ');
        else
            txtRule=[' ' txtRule];
            txtRule=strrep(txtRule,ifStr,' ');
            txtRule=strrep(txtRule,isStr,' ');
        end

        % Compress all multiple spaces down into one space
        % This is a crucial maneuver that allows me to separate words
        % quickly and painlessly.
        spaceIndex=find(txtRule==' ');
        dblSpaceIndex=find(diff(spaceIndex)==1);
        txtRule(spaceIndex(dblSpaceIndex))=[];

        % Form the antecedent and consequent strings
        antStart=1;
        antEnd=findstr(txtRule,[' ' thenStr ' ']);
        if isempty(antEnd),
            errorStr=['Rule is not an if-then rule.'];
            if nargout<3,
                error(errorStr);
            else
                errorFlag=1;
            end
        end

        antStr=deblank(txtRule((antStart):(antEnd-1)));
        consStr=deblank(txtRule((antEnd+6):size(txtRule,2)));

        % Decode the antecedent
        spaceIndex=[findstr(antStr,' ') length(antStr)];
        if spaceIndex(1)~=1, spaceIndex=[1 spaceIndex]; end

        % Ignore the first word if it's a line number
        firstWord=antStr(spaceIndex(1):spaceIndex(2));
        if all(abs(firstWord)<58),
            % If all characters are less than ASCII 58 ('9') then it's a number,
            % so start reading from the second word.
            antPtr=2;
        else
            antPtr=1;
        end

        % You need at least two words in your antecedent in order to play
        if (length(spaceIndex)-antPtr)<2,
            errorStr=['Rule antecedent is incomplete'];
            if nargout<3, 
                error(errorStr); 
            else 
                errorFlag=1;
            end
        end                 

        while antPtr<length(spaceIndex) & ~errorFlag,
            nextWord=antStr(spaceIndex(antPtr):spaceIndex(antPtr+1));
            nextWord(find(nextWord==32))=[];
            if strcmp(nextWord,andStr),
                andOrCode(ruleIndex)=1;
            elseif strcmp(nextWord,orStr),
                andOrCode(ruleIndex)=2;
            else
                varName=nextWord;
                varIndex=findrow(varName,inLabels);
                if isempty(varIndex),
                    errorStr=['There is no input variable called ' varName];
                    if nargout<3, 
                        error(errorStr); 
                    else 
                        errorFlag=1;
                    end
                end

                antPtr=antPtr+1;
                nextWord=antStr(spaceIndex(antPtr):spaceIndex(antPtr+1));
                nextWord(find(nextWord==32))=[];
                % Handle potential usage of the word NOT
                if strcmp(nextWord,notStr),
                    MFIndexMultiplier=-1;
                    antPtr=antPtr+1;
                    nextWord=antStr(spaceIndex(antPtr):spaceIndex(antPtr+1));
                else
                    MFIndexMultiplier=1;
                end

                MFIndexBegin=sum(numInputMFs(1:(varIndex-1)))+1;
                MFIndexEnd=MFIndexBegin+numInputMFs(varIndex)-1;
                MFList=inMFLabels(MFIndexBegin:MFIndexEnd,:);
                mfIndex=findrow(nextWord,MFList)*MFIndexMultiplier;
                if isempty(mfIndex) & ~errorFlag,
                    errorStr=['There is no MF called ' nextWord ...
                        ' for the input variable ' varName];
                    if nargout<3, 
                        error(errorStr); 
                    else 
                        errorFlag=1;
                    end
                end
                if ~errorFlag,
                    antCode(ruleIndex,varIndex)=mfIndex;
                end
            end

        antPtr=antPtr+1;
        end

        % Decode the consequent
        spaceIndex=[1 findstr(consStr,' ') length(consStr)];
        consPtr=1;

        % You need at least two words in your consequent in order to play
        if (length(spaceIndex)-consPtr)<2,
            errorStr=['Rule consequent is incomplete'];
            if nargout<3, 
                error(errorStr); 
            else 
                errorFlag=1;
            end
        end                 

        while consPtr<length(spaceIndex) & ~errorFlag,
            nextWord=consStr(spaceIndex(consPtr):spaceIndex(consPtr+1));
            nextWord(find(nextWord==32))=[];

            if all((nextWord>=32) & (nextWord<=57)) & ~isempty(nextWord),
                wtCode(ruleIndex)=eval(nextWord);
            elseif ~isempty(nextWord),
                varName=nextWord;
                varIndex=findrow(varName,outLabels);
                if isempty(varIndex),
                    errorStr=['There is no output variable called ' varName];
                    if nargout<3, 
                        error(errorStr); 
                    else 
                        errorFlag=1;
                    end
                end

                consPtr=consPtr+1;
                nextWord=consStr(spaceIndex(consPtr):spaceIndex(consPtr+1));
                nextWord(find(nextWord==32))=[];
                % Handle potential usage of the word NOT
                if strcmp(nextWord,notStr),
                    MFIndexMultiplier=-1;
                    consPtr=consPtr+1;
                    nextWord=consStr(spaceIndex(consPtr):spaceIndex(consPtr+1));
                else
                    MFIndexMultiplier=1;
                end

                MFIndexBegin=sum(numOutputMFs(1:(varIndex-1)))+1;
                MFIndexEnd=MFIndexBegin+numOutputMFs(varIndex)-1;
                MFList=outMFLabels(MFIndexBegin:MFIndexEnd,:);
                mfIndex=findrow(nextWord,MFList)*MFIndexMultiplier;
                if isempty(mfIndex) & ~errorFlag,
                    errorStr=['There is no MF called ' nextWord ...
                        ' for the output variable ' varName];
                    if nargout<3, 
                        error(errorStr); 
                    else 
                        errorFlag=1;
                    end
                end
                if ~errorFlag,
                    consCode(ruleIndex,varIndex)=mfIndex;
                end
            end

        consPtr=consPtr+1;
        end
        
        if errorFlag,
            errRuleIndex=[errRuleIndex ruleIndex];
        else
            goodRuleIndex=[goodRuleIndex ruleIndex];
        end
        errorFlag=0;

    end
    ruleList=[antCode consCode wtCode andOrCode];

end    % if strcmp(ruleFormat, ...

% At this point, we're nearly done. Compile the parsed rules along with
% the rules that have been flagged with errors. Make sure and 
% don't include any error-ridden rules in the parsed output
% Get back the cleaned rules that were properly parsed

ruleList(errRuleIndex,:)=[];
outFis=setfis(fis,'ruleList',ruleList);
numRules=length(goodRuleIndex);
goodRuleList=showrule(outFis,1:numRules,ruleFormat,lang);
goodWid=size(goodRuleList,2);

numErrs=length(errRuleIndex);
errWid=0;
if numErrs>0,
    inTxtRuleList(goodRuleIndex,:)=[];
    % Replace any already existing # signs with spaces so they don't keep piling up
    inTxtRuleList(find(inTxtRuleList=='#'))= ...
        32*ones(size(find(inTxtRuleList=='#')));
    % Add new # signs to indicate the fact that there's been an error
    inTxtRuleList=[abs('#')*ones(numErrs,1) inTxtRuleList];

    % Compress all multiple spaces down into one space
    errTxtRuleList=[];
    for count=1:numErrs,
        errTxtRule=inTxtRuleList(count,:);
        spaceIndex=find(errTxtRule==' ');
        dblSpaceIndex=find(diff(spaceIndex)==1);
        errTxtRule(spaceIndex(dblSpaceIndex))=[];
        errTxtRuleList=fstrvcat(errTxtRuleList,errTxtRule);
    end;

    errTxtRuleList=setstr(errTxtRuleList);
    errWid=size(errTxtRuleList,2);
end

% Combine the good and bad lines for display
outTxtRuleList=32*ones(numRules,max(goodWid,errWid));
if ~isempty(goodRuleIndex),
    outTxtRuleList(goodRuleIndex,1:goodWid)=goodRuleList;
end
if ~isempty(errRuleIndex),
    outTxtRuleList(errRuleIndex,1:errWid)=errTxtRuleList;
end