/// <summary> /// Apply the Minimum implication method to a membership function. /// </summary> /// <param name="inMf">membership function to implicate</param> /// <param name="outMf">membership function to hold the output</param> /// <param name="n">The antecedent to reshape the membership function with.</param> /// <param name="weight">The weight of the rule</param> public static MemberFunction ImpMin(MemberFunction inMf, double n, double weight) { MemberFunction outMf = new MemberFunction(); outMf.Coords.Add(new double[] { inMf.Coords[0][0], inMf.Coords[0][1] * weight }); int i; for (i = 1; i < inMf.Coords.Count - 1; i++) { if (inMf.Coords[i][1] > n) { // Note: passing the indeces in backwards is fine and tested outMf.Coords.Add(new double[] { inMf.getX(i, i - 1, n), n * weight }); break; } else { outMf.Coords.Add(new double[] { inMf.Coords[i][0], inMf.Coords[i][1] * weight }); } } // Now let's continue and fill in the rest (thus the i=i) for (int j = i; j < inMf.Coords.Count; j++) { if (inMf.Coords[j][1] < n) { // Note: passing the ideces in backwards is fine and tested outMf.Coords.Add(new double[] { inMf.getX(j, j - 1, n), n * weight }); outMf.Coords.Add(new double[] { inMf.Coords[j][0], inMf.Coords[j][1] * weight }); } } return(outMf); }
/// <summary> /// Copy one member function into another /// </summary> /// <param name="mFunc"></param> public void Copy(MemberFunction mFunc) { Coords.Clear(); foreach (double[] coord in mFunc.Coords) { Coords.Add(coord); } MaxY = mFunc.MaxY; }
/// <summary> /// The product Implicator /// Apply the Product implication method to a membership function. /// </summary> /// <param name="inMf">membership function to implicate</param> /// <param name="outMf">membership function to hold the output</param> /// <param name="n">The antecedent to reshape the membership function with.</param> /// <param name="weight">The weight of the rule</param> public static MemberFunction ImpProduct(MemberFunction inMf, double n, double weight) { MemberFunction outMf = new MemberFunction(); for (int i = 0; i < inMf.Coords.Count; i++) { outMf.Coords.Add(new double [] { inMf.Coords[i][0], inMf.Coords[i][1] * n * weight }); } return(outMf); }
/// <summary> /// Generalized Implicator /// </summary> /// <param name="inMf"></param> /// <param name="outMf"></param> /// <param name="n"></param> /// <param name="weight"></param> public MemberFunction ImplicatorOp(MemberFunction inMf, double n, double weight) { switch (_implicator) { case FISImplicator.FISImp_Min: return(FISOperators.ImpMin(inMf, n, weight)); case FISImplicator.FISImp_Product: return(FISOperators.ImpProduct(inMf, n, weight)); default: throw new ArgumentException("Invalid operator"); } }
/// <summary> /// Generalized Aggregator /// </summary> /// <param name="inMf"></param> /// <param name="outMf"></param> private void AggregatorOp(MemberFunction inMf, MemberFunction outMf) { switch (_aggregator) { case FISAggregator.FISAgg_Max: FISOperators.AggMax(inMf, outMf); break; //case FISAggregator.FISAgg_Probor: // FISOperators.AggProbor(inMf, outMf); // break; case FISAggregator.FISAgg_Sum: default: throw new ArgumentException("Invalid operator"); } }
/// <summary> /// Generalized Defizzifier /// </summary> /// <param name="mf"></param> private double DefuzzifierOp(MemberFunction mf) { switch (_defuzzifier) { case FISDefuzzifier.FISDefuzz_Bisect: return(Defuzzify.DefuzzBisect(mf)); case FISDefuzzifier.FISDefuzz_Centroid: return(Defuzzify.DefuzzCentroid(mf)); case FISDefuzzifier.FISDefuzz_LargeMax: return(Defuzzify.FISDefuzzLargeMax(mf)); case FISDefuzzifier.FISDefuzz_MidMax: return(Defuzzify.FISDefuzzMidMax(mf)); case FISDefuzzifier.FISDefuzz_SmallMax: return(Defuzzify.FISDefuzzSmallMax(mf)); default: throw new ArgumentException("Invalid operator"); } }
/// <summary> /// Add a member function to the set. /// </summary> /// <param name="sName">The name of the member function. Cannot contain spaces</param> /// <param name="mf">The member function to add.</param> public void addMF(string sName, MemberFunction mf) { if (0 == mf.Length) { throw new ArgumentException("The membership function cannot be added to the set because it has no vertices."); } else if ((mf.Coords[0][0] < _min) || (mf.Coords[mf.Length - 1][0] > _max)) { throw new ArgumentException(string.Format("Membership function bounds ({0} {1}) do not fit in the set range ({2}) for this object.", mf.Coords[0][0], mf.Coords[mf.Length - 1][0], _min)); } else if (Indices.ContainsKey(sName)) { throw new ArgumentException(string.Format("The name '{0}' is already in use.", sName)); } else if (sName.Contains(" ")) { throw new ArgumentException(string.Format("Invalid name '{0}'. Spaces are not allowed.", sName)); } else { MFunctions.Add(mf); Indices[sName] = MFunctions.Count - 1; } }
/// <summary> /// /// </summary> private void parseInputOutput(List <string> sLines, bool isInput) { string name = ""; double low = 0; double high = 0; int numMfs = 0; int inputNum = 0; //starts at 1 Dictionary <string, MemberFunction> mfs = new Dictionary <string, MemberFunction>(); foreach (string line in sLines) { // First let's see if we can figure out what input index we have if (regexes[FISFileSection.INPUT].IsMatch(line)) { //[Input1] needs to be a number string lineTrimmed = line.Trim(); if (!int.TryParse(lineTrimmed.Substring(6, lineTrimmed.Length - 7), out inputNum)) { throw new Exception("Could not extract input/output index: " + line); } } else { string[] sLSplit = line.Split('='); sLSplit[0] = sLSplit[0].ToLower(); // Example: Name='Depth' if (sLSplit[0] == "name") { name = sLSplit[1].Replace("'", ""); } // Example: Range=[0 4] else if (sLSplit[0] == "range") { List <double> sRanges = RangeSquareBrackets(sLSplit[1]); if (sRanges.Count != 2) { throw new Exception("Wrong number of ranges: " + line); } low = sRanges[0]; high = sRanges[1]; } // Example: NumMFs=4 else if (sLSplit[0] == "nummfs") { if (!int.TryParse(sLSplit[1], out numMfs)) { throw new Exception("Could not parse number of member functions: " + line); } } // Example: MF2='Moderate':'trapmf',[0.09 0.17 0.32 0.46] else if (sLSplit[0].StartsWith("mf")) { int mfnum; int.TryParse(sLSplit[0].Substring(2), out mfnum); if (mfnum != mfs.Count + 1) { throw new Exception("Wrong number of memberfunctions: " + line); } string[] mflinesplit = sLSplit[1].Split(','); string[] mflinesplitsplit = mflinesplit[0].Split(':'); string mfname = mflinesplitsplit[0].Replace("'", ""); string mftype = mflinesplitsplit[1].Replace("'", ""); if (mfs.ContainsKey(mfname)) { throw new Exception("Duplicate MF name detected: " + line); } List <double> vertices = RangeSquareBrackets(mflinesplit[1]); switch (mftype) { case "trapmf": if (vertices.Count != 4) { throw new Exception("Wrong number of vertices: " + line); } mfs[mfname] = new MemberFunction(vertices[0], vertices[1], vertices[2], vertices[3], 1); break; case "trimf": if (vertices.Count != 3) { throw new Exception("Wrong number of vertices: " + line); } mfs[mfname] = new MemberFunction(vertices[0], vertices[1], vertices[2], 1); break; default: throw new Exception("Unsupported MF type: " + line); } } } } // Now try to create a memberfunction set from all our parsed functions MemberFunctionSet mfSet = new MemberFunctionSet(low, high); foreach (KeyValuePair <string, MemberFunction> mfkvp in mfs) { mfSet.addMF(mfkvp.Key, mfkvp.Value); } if (isInput) { ruleset.setInputMFSet(inputNum - 1, name, mfSet); } else { ruleset.addOutputMFSet(name, mfSet); } }
/// <summary> /// Aggregate two membership functions using the Maximum method. /// </summary> /// <param name="inMf">One of the membership functions to aggregate.This one will not be modified.</param> /// <param name="outMf"> The other membership function to aggregate. This one will be modified to hold the</param> public static void AggMax(MemberFunction inMf, MemberFunction outMf) { if (0 == outMf.Coords.Count) { outMf.Copy(inMf); } else { // Key is X and Value is Y SortedDictionary <double, double> coords = new SortedDictionary <double, double>(); foreach (double[] inMfXY in inMf.Coords) { if (coords.ContainsKey(inMfXY[0])) { coords[inMfXY[0]] = Math.Max(coords[inMfXY[0]], Math.Max(inMfXY[1], outMf.fuzzify(inMfXY[0]))); } else { coords[inMfXY[0]] = Math.Max(inMfXY[1], outMf.fuzzify(inMfXY[0])); } } foreach (double[] outMfXY in outMf.Coords) { if (coords.ContainsKey(outMfXY[0])) { coords[outMfXY[0]] = Math.Max(coords[outMfXY[0]], Math.Max(inMf.fuzzify(outMfXY[0]), outMfXY[1])); } else { coords[outMfXY[0]] = Math.Max(inMf.fuzzify(outMfXY[0]), outMfXY[1]); } } // Let's empty out the MF and build it up again outMf.clear(); // Now loop over every line segment in inMf and see if it intersects with any segment in outMf for (int i = 1; i < inMf.Coords.Count; i++) { for (int j = 1; j < outMf.Coords.Count; j++) { Tuple <double, double, bool> intersect = IntersectLines(inMf.Coords[i - 1][0], inMf.Coords[i - 1][1], inMf.Coords[i][0], inMf.Coords[i][1], outMf.Coords[j - 1][0], outMf.Coords[j - 1][1], outMf.Coords[j][0], outMf.Coords[j][1]); // Is there an intersection? if (intersect.Item3 == true) { if (coords.ContainsKey(intersect.Item1)) { coords[intersect.Item1] = Math.Max(coords[intersect.Item1], intersect.Item2); } else { coords[intersect.Item1] = intersect.Item2; } } } } int counter = 0; foreach (KeyValuePair <double, double> kvp in coords) { double x = kvp.Key; // If the first value isn't zero then push a zero value onto the stack if (counter == 0 && x != 0.0) { outMf.Coords.Add(new double[] { x, 0 }); } outMf.Coords.Add(new double[] { x, coords[x] }); counter++; // Push a zero onto the stack if the last value isn't zero if (counter == coords.Count && coords[x] != 0.0) { outMf.Coords.Add(new double[] { x, 0 }); } } } }
/// <summary> /// Apply a FIS to a set of arrays. /// All data sets are assumed to be doubleing point. The data sets are referenced in the DoDData object /// with the keywords "old", "new", and "dod". ****** UPDATE ******* /// </summary> /// <param name="dataArrays"></param> /// <param name="n"></param> /// <param name="checkNoData"></param> /// <param name="noDataValues"></param> /// <param name="noData"></param> /// <returns></returns> public double calculate(List <double[]> dataArrays, int n, bool checkNoData, List <double> noDataValues, double noData) { List <List <double> > _fuzzyInputs = new List <List <double> >(); MemberFunction impMf = new MemberFunction(); MemberFunction aggMf = new MemberFunction(); Rule rule; double impValue; double v; if (checkNoData) { bool ok = true; for (int i = 0; i < Inputs.Count; i++) { v = dataArrays[i][n]; if (v == noDataValues[i]) { ok = false; break; } string inputName = InputLookupMap.Forward[i]; List <double> fuzzymflst = new List <double>(); for (int j = 0; j < Inputs[inputName].MFunctions.Count; j++) { fuzzymflst.Add(Inputs[inputName].MFunctions[j].fuzzify(v)); } _fuzzyInputs.Add(fuzzymflst); } if (ok) { for (int r = 0; r < Rules.Count; r++) { rule = Rules[r]; // This is where the NOT calculation happens. First pick the first fuzzified value impValue = _fuzzyInputs[rule.InputInd[0]][rule.MFSInd[0]]; // Now operate for (int m = 1; m < rule.InputInd.Count; m++) { impValue = RuleOperator(rule, impValue, _fuzzyInputs[rule.InputInd[m]][rule.MFSInd[m]]); } impMf = ImplicatorOp(rule.Output, impValue, rule.Weight); AggregatorOp(impMf, aggMf); } return(DefuzzifierOp(aggMf)); } else { return(noData); } } else { for (int i = 0; i < Inputs.Count; i++) { v = dataArrays[i][n]; string inputName = InputLookupMap.Forward[i]; for (int j = 0; j < Inputs[inputName].MFunctions.Count; j++) { _fuzzyInputs[i][j] = Inputs[inputName].MFunctions[j].fuzzify(v); } } for (int r = 0; r < Rules.Count; r++) { rule = Rules[r]; // This is where the NOT calculation happens. impValue = _fuzzyInputs[rule.InputInd[0]][rule.MFSInd[0]]; for (int m = 1; m < rule.InputInd.Count; m++) { impValue = RuleOperator(rule, impValue, _fuzzyInputs[rule.InputInd[m]][rule.MFSInd[m]]); } impMf = ImplicatorOp(rule.Output, impValue, rule.Weight); AggregatorOp(impMf, aggMf); } return(DefuzzifierOp(aggMf)); } }
/// <summary> /// Calculate a crisp output based on a set of inputs to this rule set. /// </summary> /// <param name="lInputs">Inputs The list of input values. These MUST be in the same order that the input variables</param> /// <returns></returns> public double calculate(List <double> lInputs, bool debug = false) { List <List <double> > _fuzzyInputs = new List <List <double> >(); if (lInputs.Count != Inputs.Count) { return(-1); } int idx, jdx; for (idx = 0; idx < Inputs.Count; idx++) { string inputName = InputLookupMap.Forward[idx]; List <double> _fin = new List <double>(); for (jdx = 0; jdx < Inputs[inputName].Count; jdx++) { _fin.Add(Inputs[inputName].MFunctions[jdx].fuzzify(lInputs[idx])); } _fuzzyInputs.Add(_fin); } if (debug == true) { Debug.WriteLine("Debug================="); _fuzzyInputs.ForEach(x => Debug.WriteLine(String.Format("INPUT: Fuzzy: ({0})", String.Join(", ", x)))); } MemberFunction impMf = new MemberFunction(); MemberFunction aggMf = new MemberFunction(); // Loop over the rules and run the aggregator and implicators for each. for (int r = 0; r < Rules.Count; r++) { Rule rule = Rules[r]; double impValue = getFuzzyVal(rule, 0, _fuzzyInputs); // Loop over rule items (probably same as number of inputs but not // if some are 0 value for (int i = 1; i < rule.InputInd.Count; i++) { // For now this is "and" or "or" double fuzzyInput = getFuzzyVal(rule, i, _fuzzyInputs); impValue = RuleOperator(rule, impValue, fuzzyInput); } impMf = ImplicatorOp(rule.Output, impValue, rule.Weight); AggregatorOp(impMf, aggMf); if (debug == true) { List <string> coords = new List <string>(); impMf.Coords.ForEach(x => coords.Add(String.Format("({0})", String.Join(", ", x)))); Debug.WriteLine(String.Format("IMP Coords Rule{1}: {0}", String.Join(" ", coords), r)); } } double defuzzed = DefuzzifierOp(aggMf); if (debug == true) { List <string> coords = new List <string>(); aggMf.Coords.ForEach(x => coords.Add(String.Format("({0})", String.Join(", ", x)))); Debug.WriteLine(String.Format("Aggregated Coords: {0}", String.Join(" ", coords))); Debug.WriteLine(String.Format("Defuzzified: {0}", DefuzzifierOp(aggMf))); Debug.WriteLine("ENDDebug================="); } // The defuzzifier is where composite shape gets reduced // to a single number return(defuzzed); }