diff --git a/html/about.html b/html/about.html index 6356580..9721c32 100644 --- a/html/about.html +++ b/html/about.html @@ -113,6 +113,7 @@ Helano Póvoas de Lima DefuzzifierContinuous graphic bug. Nikola Nikolovski RuleBlock name bug. Ivan De Falco "AND Bounded Diff" bug. + Marcin Szczepanski FCL Compiler (JavaScript).

diff --git a/html/fcl/tipper.cpp b/html/fcl/tipper.cpp index 45abea9..ae9860a 100644 --- a/html/fcl/tipper.cpp +++ b/html/fcl/tipper.cpp @@ -1,7 +1,7 @@ //-------------------------------------------------------------------------------- // Code generated by jFuzzyLogic -// jFuzzyLogic Version : JFuzzyLogic 3.2g (build 2013-10-20), by Pablo Cingolani -// jFuzzyLogic creted by Pablo Cingolani +// jFuzzyLogic Version : JFuzzyLogic 4.0 (build 2022-05-12), by Pablo Cingolani +// jFuzzyLogic created by Pablo Cingolani //-------------------------------------------------------------------------------- diff --git a/html/fcl/tipper.js b/html/fcl/tipper.js new file mode 100644 index 0000000..65be240 --- /dev/null +++ b/html/fcl/tipper.js @@ -0,0 +1,208 @@ +//-------------------------------------------------------------------------------- +// Code generated by jFuzzyLogic +// jFuzzyLogic Version : JFuzzyLogic 4.0 (build 2022-05-12), by Pablo Cingolani +// jFuzzyLogic created by Pablo Cingolani +//-------------------------------------------------------------------------------- + +function ruleAccumulationMethod_max(defuzzifierValue, valueToAggregate) { + return (defuzzifierValue > valueToAggregate ? defuzzifierValue : valueToAggregate); +} + +function ruleActivationMethod_min(degreeOfSupport, membership) { + return (degreeOfSupport < membership ? degreeOfSupport : membership); +} + +function ruleConnectionMethod_and(antecedent1, antecedent2) { + return (antecedent1 < antecedent2 ? antecedent1 : antecedent2); +} + +function ruleConnectionMethod_or(antecedent1, antecedent2) { + return (antecedent1 > antecedent2 ? antecedent1 : antecedent2); +} + +class FunctionBlock_tipper { + + // VAR_INPUT + food; + service; + + // VAR_OUTPUT + tip; + + // FUZZIFY food + food_delicious; + food_rancid; + + // FUZZIFY service + service_excellent; + service_good; + service_poor; + + // DEFUZZIFY tip + defuzzify_tip; + + // Constructor + constructor() { + this.tip = 0.0; + this.defuzzify_tip = new Array(1000); + } + + // Calculate function block + calc() { + this.reset(); + this.fuzzify(); + this.calc_No1(); + this.defuzzify(); + } + + // RULEBLOCK No1 + calc_No1() { + // RULE 1 : IF (service IS poor) OR (food IS rancid) THEN tip IS cheap; + let degreeOfSupport_1 = 1.0 * (ruleConnectionMethod_or(this.service_poor, this.food_rancid)); + if (degreeOfSupport_1 > 0) { + for (let i = 0; i < 1000; i++) { + let x = 0.0 + i * 0.03; + let membership = this.membership_tip_cheap(x); + let y = ruleActivationMethod_min(degreeOfSupport_1 , membership); + this.defuzzify_tip[i] = ruleAccumulationMethod_max(this.defuzzify_tip[i], y); + } + } + + // RULE 2 : IF service IS good THEN tip IS average; + let degreeOfSupport_2 = 1.0 * (this.service_good); + if (degreeOfSupport_2 > 0) { + for (let i = 0; i < 1000; i++) { + let x = 0.0 + i * 0.03; + let membership = this.membership_tip_average(x); + let y = ruleActivationMethod_min(degreeOfSupport_2 , membership); + this.defuzzify_tip[i] = ruleAccumulationMethod_max(this.defuzzify_tip[i], y); + } + } + + // RULE 3 : IF (service IS excellent) AND (food IS delicious) THEN tip IS generous; + let degreeOfSupport_3 = 1.0 * (ruleConnectionMethod_and(this.service_excellent, this.food_delicious)); + if (degreeOfSupport_3 > 0) { + for (let i = 0; i < 1000; i++) { + let x = 0.0 + i * 0.03; + let membership = this.membership_tip_generous(x); + let y = ruleActivationMethod_min(degreeOfSupport_3 , membership); + this.defuzzify_tip[i] = ruleAccumulationMethod_max(this.defuzzify_tip[i], y); + } + } + + } + + // Defuzzify + defuzzify() { + let sum_tip = 0.0; + let wsum_tip = 0.0; + let x; + for (let i = 0; i < 1000; i++) { + x = 0.0 + i * 0.03; + sum_tip += this.defuzzify_tip[i]; + wsum_tip += x * this.defuzzify_tip[i]; + } + this.tip = wsum_tip / sum_tip; + } + + // Fuzzify all variables + fuzzify() { + this.food_delicious = this.membership_food_delicious(this.food); + this.food_rancid = this.membership_food_rancid(this.food); + this.service_excellent = this.membership_service_excellent(this.service); + this.service_good = this.membership_service_good(this.service); + this.service_poor = this.membership_service_poor(this.service); + } + + // Membership functions + membership_food_delicious(x) { + if (x <= 7.0) return 0.0; + if (x > 9.0) return 1.0; + if (x <= 9.0) return 0.0 + (1.0 - 0.0) * ((x - 7.0) / (9.0 - 7.0)); + } + + membership_food_rancid(x) { + if (x <= 0.0) return 1.0; + if (x > 3.0) return 0.0; + if (x <= 1.0) return 1.0 + (1.0 - 1.0) * ((x - 0.0) / (1.0 - 0.0)); + if (x <= 3.0) return 1.0 + (0.0 - 1.0) * ((x - 1.0) / (3.0 - 1.0)); + } + + membership_service_excellent(x) { + if (x <= 6.0) return 0.0; + if (x > 9.0) return 1.0; + if (x <= 9.0) return 0.0 + (1.0 - 0.0) * ((x - 6.0) / (9.0 - 6.0)); + } + + membership_service_good(x) { + if (x <= 1.0) return 0.0; + if (x > 9.0) return 0.0; + if (x <= 4.0) return 0.0 + (1.0 - 0.0) * ((x - 1.0) / (4.0 - 1.0)); + if (x <= 6.0) return 1.0 + (1.0 - 1.0) * ((x - 4.0) / (6.0 - 4.0)); + if (x <= 9.0) return 1.0 + (0.0 - 1.0) * ((x - 6.0) / (9.0 - 6.0)); + } + + membership_service_poor(x) { + if (x <= 0.0) return 1.0; + if (x > 4.0) return 0.0; + if (x <= 4.0) return 1.0 + (0.0 - 1.0) * ((x - 0.0) / (4.0 - 0.0)); + } + + membership_tip_average(x) { + if (x <= 10.0) return 0.0; + if (x > 20.0) return 0.0; + if (x <= 15.0) return 0.0 + (1.0 - 0.0) * ((x - 10.0) / (15.0 - 10.0)); + if (x <= 20.0) return 1.0 + (0.0 - 1.0) * ((x - 15.0) / (20.0 - 15.0)); + } + + membership_tip_cheap(x) { + if (x <= 0.0) return 0.0; + if (x > 10.0) return 0.0; + if (x <= 5.0) return 0.0 + (1.0 - 0.0) * ((x - 0.0) / (5.0 - 0.0)); + if (x <= 10.0) return 1.0 + (0.0 - 1.0) * ((x - 5.0) / (10.0 - 5.0)); + } + + membership_tip_generous(x) { + if (x <= 20.0) return 0.0; + if (x > 30.0) return 0.0; + if (x <= 25.0) return 0.0 + (1.0 - 0.0) * ((x - 20.0) / (25.0 - 20.0)); + if (x <= 30.0) return 1.0 + (0.0 - 1.0) * ((x - 25.0) / (30.0 - 25.0)); + } + + + // Print + print() { + console.log("Function block tipper:\n"); + console.log(" Input %s: %f\n", "food" , this.food); + console.log(" %s: %f\n", "food_delicious" , this.food_delicious); + console.log(" %s: %f\n", "food_rancid" , this.food_rancid); + console.log(" Input %s: %f\n", "service" , this.service); + console.log(" %s: %f\n", "service_excellent" , this.service_excellent); + console.log(" %s: %f\n", "service_good" , this.service_good); + console.log(" %s: %f\n", "service_poor" , this.service_poor); + console.log(" Output %s: %f\n", "tip" , this.tip); + } + + // Reset output + reset() { + for (let i = 0 ; i < 1000; i++) { + this.defuzzify_tip[i] = 0.0; + } + } + +}; + +function main(food, service) { + // Create function blocks + const tipper = new FunctionBlock_tipper(); + + // Parse input + tipper.food = food; + tipper.service = service; + + // Calculate + tipper.calc(); + + // Show results + tipper.print(); +} diff --git a/html/images/js.png b/html/images/js.png new file mode 100644 index 0000000..efc1d68 Binary files /dev/null and b/html/images/js.png differ diff --git a/html/index.html b/html/index.html index f4d21b6..be232b8 100644 --- a/html/index.html +++ b/html/index.html @@ -180,6 +180,7 @@

Tons of advanced features

@@ -956,11 +957,32 @@ java -jar jFuzzyLogic.jar -c tipper.fcl > tipper.cpp

+
+ +

+ jFuzzyLogic can compile FCL into JavaScript code. +

+

+ A built in compiler allows to compiler FCL code into JavaScript. + For instance, by simply running the following command, we can create a JavaScript program that has the same functionality as the FCL program: +

+java -jar jFuzzyLogic.jar -j tipper.fcl > tipper.js
+
+ + The previous command creates this JavaScript code. +
+
+ +

+
+

jFuzzyLogic provides a parameter optimization framework, allowing to learn or refine fuzzy parameters using machine learning algorithms. diff --git a/out/artifacts/jFuzzyLogic_jar/jFuzzyLogic.jar b/out/artifacts/jFuzzyLogic_jar/jFuzzyLogic.jar index 7887a30..685a68e 100644 Binary files a/out/artifacts/jFuzzyLogic_jar/jFuzzyLogic.jar and b/out/artifacts/jFuzzyLogic_jar/jFuzzyLogic.jar differ diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/CompileJS.java b/src/main/java/net/sourceforge/jFuzzyLogic/CompileJS.java new file mode 100644 index 0000000..247cff5 --- /dev/null +++ b/src/main/java/net/sourceforge/jFuzzyLogic/CompileJS.java @@ -0,0 +1,11 @@ +package net.sourceforge.jFuzzyLogic; + +/** + * Create JS code + * + * @author marcin-szczepanski + */ +public interface CompileJS { + + public String toStringJS(); +} diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/FIS.java b/src/main/java/net/sourceforge/jFuzzyLogic/FIS.java index c4e8ce2..fae55c3 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/FIS.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/FIS.java @@ -257,7 +257,7 @@ public class FIS extends FclObject implements Iterable, CompileCp out.append("//--------------------------------------------------------------------------------\n"); out.append("// Code generated by jFuzzyLogic\n"); out.append("// jFuzzyLogic Version : " + JFuzzyLogic.VERSION + " \n"); - out.append("// jFuzzyLogic creted by Pablo Cingolani\n"); + out.append("// jFuzzyLogic created by Pablo Cingolani\n"); out.append("//--------------------------------------------------------------------------------\n\n"); out.append("\n#include \n"); out.append("\n#include \n"); @@ -337,6 +337,117 @@ public class FIS extends FclObject implements Iterable, CompileCp return out.toString(); } + /** + * Create a JS code for this FIS + * @return + */ + @Override + public String toStringJS() { + StringBuffer out = new StringBuffer(); + + out.append("//--------------------------------------------------------------------------------\n"); + out.append("// Code generated by jFuzzyLogic\n"); + out.append("// jFuzzyLogic Version : " + JFuzzyLogic.VERSION + " \n"); + out.append("// jFuzzyLogic created by Pablo Cingolani\n"); + out.append("//--------------------------------------------------------------------------------\n\n"); + + // Sort function blocks by name + ArrayList fbNames = new ArrayList(functionBlocks.keySet()); + Collections.sort(fbNames); + + // Ge all activations and accumulation methods + HashSet raccs = new HashSet(); + HashSet racts = new HashSet(); + HashSet rcons = new HashSet(); + for (String name : fbNames) { + FunctionBlock functionBlock = getFunctionBlock(name); + + for (RuleBlock rb : functionBlock.getRuleBlocks().values()) { + raccs.add(rb.getRuleAccumulationMethod()); + racts.add(rb.getRuleActivationMethod()); + for (Rule r : rb) { + RuleExpression rexp = r.getAntecedents(); + rcons.addAll(rexp.getRuleConnectionMethods()); + } + } + } + + // Show code + for (RuleAccumulationMethod racc : raccs) + out.append(racc.toStringJSFunction() + "\n"); + + for (RuleActivationMethod ract : racts) + out.append(ract.toStringJSFunction() + "\n"); + + for (RuleConnectionMethod rcon : rcons) + out.append(rcon.toStringJSFunction() + "\n"); + + // Iterate over each function block and append it to output string + for (String name : fbNames) { + FunctionBlock functionBlock = getFunctionBlock(name); + out.append(functionBlock.toStringJS()); + } + + // Main method + out.append("function main("); + + int inV = 0; + for (String name : fbNames) { + FunctionBlock functionBlock = getFunctionBlock(name); + for (Variable var : functionBlock.variablesSorted()) { + if (var.isInput()) { + out.append(var.getName()); + if (inV < (functionBlock.variablesSorted().size() - 1)) { + out.append(", "); + } + inV++; + } + } + } + + String outWithMainStr = out.toString(); + if (outWithMainStr.endsWith(", ")) { + outWithMainStr = outWithMainStr.substring(0, outWithMainStr.length() - 2); + out = new StringBuffer(outWithMainStr); + } + + out.append(") {\n"); + + // Create function blocks + out.append("\t// Create function blocks\n"); + for (String name : fbNames) + out.append("\tconst " + name + " = new FunctionBlock_" + name + "();\n"); + out.append("\n"); + + // Assign values + out.append("\t// Parse input\n"); + int inVar = 1; + for (String name : fbNames) { + FunctionBlock functionBlock = getFunctionBlock(name); + for (Variable var : functionBlock.variablesSorted()) { + if (var.isInput()) { + out.append("\t" + name + "." + var.getName() + " = " + var.getName() + ";\n"); + inVar++; + } + } + } + out.append("\n"); + + // Calculate + out.append("\t// Calculate\n"); + for (String name : fbNames) + out.append("\t" + name + ".calc();\n"); + out.append("\n"); + + // Show results + out.append("\t// Show results\n"); + for (String name : fbNames) + out.append("\t" + name + ".print();\n"); + out.append("}"); + + return out.toString(); + } + @Override public String toStringFcl() { StringBuffer out = new StringBuffer(); diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/FunctionBlock.java b/src/main/java/net/sourceforge/jFuzzyLogic/FunctionBlock.java index 827f73c..888d98d 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/FunctionBlock.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/FunctionBlock.java @@ -740,6 +740,131 @@ public class FunctionBlock extends FclObject implements Iterable, Com ; // } + @Override + public String toStringJS() { + StringBuffer calcMethod = new StringBuffer(); // Method calc() + StringBuffer constructor = new StringBuffer(); // Constructor + StringBuffer defuzzifyMethod = new StringBuffer(); // Method defuzzify() + StringBuffer fuzzifyMethod = new StringBuffer(); // Fuzzify method + StringBuffer membershipMethods = new StringBuffer(); // All membership functions + StringBuffer printMethod = new StringBuffer(); // Method print() + StringBuffer resetMethod = new StringBuffer(); // Method reset() + StringBuffer varDefuzzifiers = new StringBuffer(); // All defuzzifier variables + StringBuffer varsFuzzify = new StringBuffer(); // Fuzzify variables + StringBuffer varsIn = new StringBuffer(); // Input vars + StringBuffer varsOut = new StringBuffer(); // Output vars + + varsIn.append("\t// VAR_INPUT\n"); + varsOut.append("\t// VAR_OUTPUT\n"); + + // Methods + String className = "FunctionBlock_" + name; + calcMethod.append("\t// Calculate function block\n\tcalc() {\n\t\tthis.reset();\n\t\tthis.fuzzify();\n"); + constructor.append("\t// Constructor\n\tconstructor() {\n"); + defuzzifyMethod.append("\t// Defuzzify \n\tdefuzzify() {\n"); + fuzzifyMethod.append("\t// Fuzzify all variables\n\tfuzzify() {\n"); + membershipMethods.append("\t// Membership functions \n"); + printMethod.append("\t// Print \n\tprint() {\n\t\tconsole.log(\"Function block " + name + ":\\n\");\n"); + resetMethod.append("\t// Reset output\n\treset() {\n"); + + //--- + // Show variables (sorted by name) + //--- + for (Variable var : variablesSorted()) { + var.estimateUniverse(); + + if (!Double.isNaN(var.getDefaultValue())) constructor.append("\t\tthis." + var.getName() + " = " + var.getDefaultValue() + ";\n"); + + if (var.isInput()) { + // Add input variables + varsIn.append("\t" + var.getName()); + varsIn.append(";\n"); + + // Add to print method + printMethod.append("\t\tconsole.log(\"\tInput %s: %f\\n\", \"" + var.getName() + "\" , this." + var.getName() + ");\n"); + + // Add fuzzyfiers + varsFuzzify.append("\t// FUZZIFY " + var.getName() + "\n"); + for (LinguisticTerm linguisticTerm : var.linguisticTermsSorted()) { + String ltVar = var.getName() + "_" + linguisticTerm.getTermName(); + varsFuzzify.append("\t" + ltVar + ";\n"); + fuzzifyMethod.append("\t\tthis." + ltVar + " = this." + linguisticTerm.toStringJSMethodName(var) + "(this." + var.getName() + ");\n"); + + // Membership function + membershipMethods.append(linguisticTerm.toStringJS(var) + "\n"); + + // Add to print method + printMethod.append("\t\tconsole.log(\"\t %s: %f\\n\", \"" + ltVar + "\" , this." + ltVar + ");\n"); + } + varsFuzzify.append("\n"); + + } else { + int len = ((DefuzzifierContinuous) var.getDefuzzifier()).getLength(); + + + constructor.append("\t\tthis." + var.toStringJSDefuzzifyVarName() + " = new Array(" + len + ");\n"); + + // Add output variables + varsOut.append("\t" + var.getName()); + varsOut.append(";\n"); + + // Add to print method + printMethod.append("\t\tconsole.log(\"\tOutput %s: %f\\n\", \"" + var.getName() + "\" , this." + var.getName() + ");\n"); + + // Add defuzzyfier variable + varDefuzzifiers.append("\t// DEFUZZIFY " + var.getName() + "\n"); + varDefuzzifiers.append("\t" + var.toStringJSDefuzzifyVarName() + ";\n"); + + // Add to reset method + resetMethod.append("\t\tfor (let i = 0 ; i < " + len + "; i++) {\n\t\t\tthis." + var.toStringJSDefuzzifyVarName() + "[i] = 0.0;\n\t\t}\n"); + + for (LinguisticTerm linguisticTerm : var.linguisticTermsSorted()) { + // Membership function + membershipMethods.append(linguisticTerm.toStringJS(var) + "\n"); + + } + varDefuzzifiers.append("\n"); + + defuzzifyMethod.append(var.getDefuzzifier().toStringJS()); + } + } + + //--- + // Iterate over each ruleSet and append it to output string + // Sort ruleBlocks by name + //--- + StringBuffer ruleBlocksStr = new StringBuffer(); + for (RuleBlock ruleBlock : ruleBlocksSorted()) { + ruleBlocksStr.append(ruleBlock.toStringJS()); + calcMethod.append("\t\tthis.calc_" + ruleBlock.getName() + "();\n"); + } + + constructor.append("\t}\n"); + calcMethod.append("\t\tthis.defuzzify();\n\t}\n"); + defuzzifyMethod.append("\t}\n"); + fuzzifyMethod.append("\t}\n"); + printMethod.append("\t}\n"); + resetMethod.append("\t}\n"); + + // Build the whole thing + return "class FunctionBlock_" + name + " {\n\n" // + + varsIn + "\n" // + + varsOut // + + "\n" // + + varsFuzzify // + + varDefuzzifiers // + + constructor + "\n" // + + calcMethod + "\n" // + + ruleBlocksStr + "\n" // + + defuzzifyMethod + "\n" // + + fuzzifyMethod + "\n" // + + membershipMethods + "\n" // + + printMethod + "\n" // + + resetMethod + "\n" // + + "};\n\n" // + ; // + } + @Override public String toStringFcl() { StringBuffer varsIn = new StringBuffer(); diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/JFuzzyLogic.java b/src/main/java/net/sourceforge/jFuzzyLogic/JFuzzyLogic.java index d28a34f..dadb6e1 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/JFuzzyLogic.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/JFuzzyLogic.java @@ -1,6 +1,5 @@ package net.sourceforge.jFuzzyLogic; -import net.sourceforge.jFuzzyLogic.ruleConnectionMethod.Szczepanski; import org.antlr.runtime.RecognitionException; import net.sourceforge.jFuzzyLogic.demo.tipper.TipperAnimation; @@ -24,7 +23,7 @@ public class JFuzzyLogic { public static final String BUILD = "2022-05-12"; public static final String VERSION_MAJOR = "4.0"; public static final String VERSION_SHORT = VERSION_MAJOR + REVISION; - public static final String VERSION_NO_NAME = VERSION_SHORT + " (build " + BUILD + "), by " + Pcingola.BY + " and " + Szczepanski.BY; + public static final String VERSION_NO_NAME = VERSION_SHORT + " (build " + BUILD + "), by " + Pcingola.BY; public static final String VERSION = SOFTWARE_NAME + " " + VERSION_NO_NAME; public static boolean debug = false; @@ -56,6 +55,21 @@ public class JFuzzyLogic { } } + /** + * Compile an FCL program into JavaScript + */ + void compileJS(String fileName) { + load(fileName); // Read FIS + System.out.println(fis.toStringJS()); // Show JS code + + // Dump JS code to a file (debug) + if (debug) { + String jsfile = Gpr.HOME + "/x.js"; + Gpr.debug("Writing to file " + jsfile); + Gpr.toFile(jsfile, fis.toStringJS()); + } + } + /** * Run demo */ @@ -170,6 +184,11 @@ public class JFuzzyLogic { String fileName = args[++i]; compile(fileName); return; + } else if (arg.equals("-j")) { + // Sanity check + String fileName = args[++i]; + compileJS(fileName); + return; } else if (arg.equals("-e")) { evaluate(i + 1); return; @@ -202,11 +221,12 @@ public class JFuzzyLogic { System.err.println("Usage: java -jar jFuzzyLogic.jar [-noCharts] [{-e|-c}] file.fcl [in_1 ... in_N]"); System.err.println("Options:"); - System.err.println("\t file.fcl : Load FCL file and show memebership functions (default, when no option is provided)."); + System.err.println("\t file.fcl : Load FCL file and show membership functions (default, when no option is provided)."); System.err.println("\t-c file.fcl : Compile. Generate C++ code from FCL file (to STDOUT)"); + System.err.println("\t-j file.fcl : Compile. Generate JavaScript code from FCL file (to STDOUT)"); System.err.println("\t-e file.fcl in_1 in_2 ... in_N : Evaluate. Load FCL file, assign inputs i_1, i_2, ..., i_n and evaluate (variables sorted alphabetically)."); System.err.println("\t-noCharts : Use a mock class for charts. This is used when not compiled using JFreeCharts."); - System.err.println("\tdemo : Run a demo exmaple (tipper.fcl)"); + System.err.println("\tdemo : Run a demo example (tipper.fcl)"); System.exit(1); } } diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/defuzzifier/Defuzzifier.java b/src/main/java/net/sourceforge/jFuzzyLogic/defuzzifier/Defuzzifier.java index 71f81e4..8059749 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/defuzzifier/Defuzzifier.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/defuzzifier/Defuzzifier.java @@ -68,4 +68,9 @@ public abstract class Defuzzifier extends FclObject { public String toStringCpp() { throw new RuntimeException("Unimplemented method for class " + this.getClass().getCanonicalName()); } + + @Override + public String toStringJS() { + throw new RuntimeException("Unimplemented method for class " + this.getClass().getCanonicalName()); + } } diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/defuzzifier/DefuzzifierCenterOfGravity.java b/src/main/java/net/sourceforge/jFuzzyLogic/defuzzifier/DefuzzifierCenterOfGravity.java index 25ac828..bb98cd5 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/defuzzifier/DefuzzifierCenterOfGravity.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/defuzzifier/DefuzzifierCenterOfGravity.java @@ -51,6 +51,27 @@ public class DefuzzifierCenterOfGravity extends DefuzzifierContinuous { return out.toString(); } + @Override + public String toStringJS() { + StringBuilder out = new StringBuilder(); + + String defuzzName = "defuzzify_" + variable.getName(); + String sumName = "sum_" + variable.getName(); + String wsumName = "wsum_" + variable.getName(); + + out.append("\t\tlet " + sumName + " = 0.0;\n"); + out.append("\t\tlet " + wsumName + " = 0.0;\n"); + out.append("\t\tlet x;\n"); + + out.append("\t\tfor (let i = 0; i < " + getLength() + "; i++) {\n"); + out.append("\t\t\tx = " + min + " + i * " + stepSize + ";\n"); + out.append("\t\t\t" + sumName + " += this." + defuzzName + "[i];\n"); + out.append("\t\t\t" + wsumName + " += x * this." + defuzzName + "[i];\n"); + out.append("\t\t}\n"); + out.append("\t\tthis." + variable.getName() + " = " + wsumName + " / " + sumName + ";\n"); + return out.toString(); + } + @Override public String toStringFcl() { return "METHOD : COG;"; diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/fcl/FclObject.java b/src/main/java/net/sourceforge/jFuzzyLogic/fcl/FclObject.java index 1067940..0ebc160 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/fcl/FclObject.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/fcl/FclObject.java @@ -1,6 +1,7 @@ package net.sourceforge.jFuzzyLogic.fcl; import net.sourceforge.jFuzzyLogic.CompileCpp; +import net.sourceforge.jFuzzyLogic.CompileJS; /** * The root of all FCL objects @@ -8,7 +9,7 @@ import net.sourceforge.jFuzzyLogic.CompileCpp; * @author pcingola * */ -public abstract class FclObject implements CompileCpp { +public abstract class FclObject implements CompileCpp, CompileJS { @Override public String toString() { @@ -20,5 +21,10 @@ public abstract class FclObject implements CompileCpp { return "// " + this.getClass().getName(); } + @Override + public String toStringJS() { + return "// " + this.getClass().getName(); + } + public abstract String toStringFcl(); } diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/membership/MembershipFunction.java b/src/main/java/net/sourceforge/jFuzzyLogic/membership/MembershipFunction.java index b58f47b..8b8906f 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/membership/MembershipFunction.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/membership/MembershipFunction.java @@ -110,6 +110,11 @@ public abstract class MembershipFunction extends FclObject { throw new RuntimeException("Unimplemented method 'toStringCpp()' for class " + this.getClass().getCanonicalName()); } + @Override + public String toStringJS() { + throw new RuntimeException("Unimplemented method 'toStringJS()' for class " + this.getClass().getCanonicalName()); + } + @Override public String toStringFcl() { return getName(); diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/membership/MembershipFunctionPieceWiseLinear.java b/src/main/java/net/sourceforge/jFuzzyLogic/membership/MembershipFunctionPieceWiseLinear.java index 42b5472..0e082f2 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/membership/MembershipFunctionPieceWiseLinear.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/membership/MembershipFunctionPieceWiseLinear.java @@ -157,6 +157,17 @@ public class MembershipFunctionPieceWiseLinear extends MembershipFunctionContinu return sb.toString(); } + @Override + public String toStringJS() { + StringBuilder sb = new StringBuilder(); + int i, len = x.length; + sb.append("\t\tif (x <= " + x[0].getValue() + ")\t\treturn " + y[0].getValue() + ";\n"); + sb.append("\t\tif (x > " + x[len - 1].getValue() + ")\t\treturn " + y[len - 1].getValue() + ";\n"); + for (i = 1; i < len; i++) + sb.append("\t\tif (x <= " + x[i].getValue() + ")\t\treturn " + y[i - 1].getValue() + " + (" + y[i].getValue() + " - " + y[i - 1].getValue() + ") * ((x - " + x[i - 1].getValue() + ") / (" + x[i].getValue() + " - " + x[i - 1].getValue() + "));\n"); + return sb.toString(); + } + /** FCL representation */ @Override public String toStringFcl() { diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/membership/functions/MffFunction.java b/src/main/java/net/sourceforge/jFuzzyLogic/membership/functions/MffFunction.java index 5d00e1b..eb08146 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/membership/functions/MffFunction.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/membership/functions/MffFunction.java @@ -116,6 +116,18 @@ public abstract class MffFunction extends FclObject { return out + ")"; } + @Override + public String toStringJS() { + if (terms == null) return ""; + String out = this.getClass().getSimpleName() + "("; + for (int i = 0; i < terms.length; i++) { + out += terms[i].toStringFcl(); + if (i < terms.length - 1) out += ", "; + } + + return out + ")"; + } + @Override public String toStringFcl() { if (terms == null) return ""; diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/rule/LinguisticTerm.java b/src/main/java/net/sourceforge/jFuzzyLogic/rule/LinguisticTerm.java index 00f87a5..4fb36cb 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/rule/LinguisticTerm.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/rule/LinguisticTerm.java @@ -59,10 +59,22 @@ public class LinguisticTerm extends FclObject implements Comparable, Comparable, Com return str; } + @Override + public String toStringJS() { + String str = ""; + String connector = ruleConnectionMethod.toStringJS(); + + if ((term1 == null) || (term2 == null)) { + // Only one term? + if (term1 != null) str += term1.toStringJS(); + if (term2 != null) str += term2.toStringJS(); + } else { + str += connector + "("; + + // Both terms connected + if (isFuzzyRuleExpression(term1)) str += ((RuleExpression) term1).toStringJS(); + else if (isFuzzyRuleTerm(term1)) str += ((RuleTerm) term1).toStringJS(); + + str += ", "; + + if (isFuzzyRuleExpression(term2)) str += ((RuleExpression) term2).toStringJS(); + else if (isFuzzyRuleTerm(term2)) str += ((RuleTerm) term2).toStringJS(); + + str += ")"; + } + + if (negated) str = "1.0 - (" + str + ")"; + + return str; + } + @Override public String toStringFcl() { String str = ""; diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/rule/RuleTerm.java b/src/main/java/net/sourceforge/jFuzzyLogic/rule/RuleTerm.java index b472633..dda33fa 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/rule/RuleTerm.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/rule/RuleTerm.java @@ -73,10 +73,21 @@ public class RuleTerm extends FclObject { return neg + variable.getName() + "_" + termName; } + @Override + public String toStringJS() { + String neg = ""; + if (negated) neg = "1 -"; + return neg + "this." + variable.getName() + "_" + termName; + } + public String toStringCppDeffName() { return getVariable().getName() + "_" + getTermName(); } + public String toStringJSDeffName() { + return getVariable().getName() + "_" + getTermName(); + } + @Override public String toStringFcl() { String is = "IS"; diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/rule/Variable.java b/src/main/java/net/sourceforge/jFuzzyLogic/rule/Variable.java index 8446405..4ed2f71 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/rule/Variable.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/rule/Variable.java @@ -338,6 +338,27 @@ public class Variable extends FclObject implements Comparable, Iterabl return "defuzzify_" + getName(); } + @Override + public String toStringJS() { + String str = name + " : \n"; + + // Show defuzifier for "output" variables, value for "input" variables + if (defuzzifier != null) str += "\tDefuzzifier : " + defuzzifier.toString() + "\n\tLatest defuzzified value: " + latestDefuzzifiedValue + "\n"; + else str += "\tValue: " + value + "\n"; + + if (!Double.isNaN(defaultValue)) str += "\tDefault value: " + defaultValue + "\n"; + + // Show each 'termName' and it's membership function + for (LinguisticTerm linguisticTerm : this) + str += "\t" + linguisticTerm.toString(value) + "\n"; + + return str; + } + + public String toStringJSDefuzzifyVarName() { + return "defuzzify_" + getName(); + } + @Override public String toStringFcl() { return name; diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/ruleAccumulationMethod/RuleAccumulationMethod.java b/src/main/java/net/sourceforge/jFuzzyLogic/ruleAccumulationMethod/RuleAccumulationMethod.java index 09edb03..64be3e7 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/ruleAccumulationMethod/RuleAccumulationMethod.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/ruleAccumulationMethod/RuleAccumulationMethod.java @@ -49,8 +49,17 @@ public abstract class RuleAccumulationMethod extends FclObject { return "ruleAccumulationMethod_" + getName(); } + @Override + public String toStringJS() { + return "ruleAccumulationMethod_" + getName(); + } + public String toStringCppFunction() { - throw new RuntimeException("Unimplemented method foe class " + this.getClass().getCanonicalName()); + throw new RuntimeException("Unimplemented method for class " + this.getClass().getCanonicalName()); + } + + public String toStringJSFunction() { + throw new RuntimeException("Unimplemented method for class " + this.getClass().getCanonicalName()); } @Override diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/ruleAccumulationMethod/RuleAccumulationMethodMax.java b/src/main/java/net/sourceforge/jFuzzyLogic/ruleAccumulationMethod/RuleAccumulationMethodMax.java index 0b2d23e..953625b 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/ruleAccumulationMethod/RuleAccumulationMethodMax.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/ruleAccumulationMethod/RuleAccumulationMethodMax.java @@ -24,6 +24,11 @@ public class RuleAccumulationMethodMax extends RuleAccumulationMethod { return "double " + toStringCpp() + "(double defuzzifierValue, double valueToAggregate)\t{ return ( defuzzifierValue > valueToAggregate ? defuzzifierValue : valueToAggregate ); }\n"; } + @Override + public String toStringJSFunction() { + return "function " + toStringJS() + "(defuzzifierValue, valueToAggregate) {\n\treturn (defuzzifierValue > valueToAggregate ? defuzzifierValue : valueToAggregate);\n}\n"; + } + /** * @see net.sourceforge.jFuzzyLogic.ruleAccumulationMethod.RuleAccumulationMethod#toStringFcl() */ diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/ruleActivationMethod/RuleActivationMethod.java b/src/main/java/net/sourceforge/jFuzzyLogic/ruleActivationMethod/RuleActivationMethod.java index 91a3748..5ce3e83 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/ruleActivationMethod/RuleActivationMethod.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/ruleActivationMethod/RuleActivationMethod.java @@ -157,7 +157,49 @@ public abstract class RuleActivationMethod extends FclObject { } public String toStringCppFunction() { - throw new RuntimeException("Unimplemented method foe class " + this.getClass().getCanonicalName()); + throw new RuntimeException("Unimplemented method for class " + this.getClass().getCanonicalName()); + } + + @Override + public String toStringJS() { + return "ruleActivationMethod_" + name; + } + + public String toStringJS(RuleTerm fuzzyRuleTerm, RuleAccumulationMethod ruleAccumulationMethod, String degreeOfSupportName) { + StringBuilder out = new StringBuilder(); + + Variable variable = fuzzyRuleTerm.getVariable(); + Defuzzifier defuzzifier = variable.getDefuzzifier(); + + if (fuzzyRuleTerm.getMembershipFunction().isDiscrete()) { + throw new RuntimeException("Unimplemented for discre cases!"); + } else { + //--- + // Continuous case + //--- + DefuzzifierContinuous defuzzifierContinuous = (DefuzzifierContinuous) defuzzifier; + + // Add membership function to defuzzifier + out.append("\t\tif (" + degreeOfSupportName + " > 0) {\n"); + out.append("\t\t\tfor (let i = 0; i < " + defuzzifierContinuous.getLength() + "; i++) {\n"); + out.append("\t\t\t\tlet x = " + defuzzifierContinuous.getMin() + " + i * " + defuzzifierContinuous.getStepSize() + ";\n"); + // Is term negated? + if (fuzzyRuleTerm.isNegated()) out.append("\t\t\tlet membership = 1 - mf.membership(x);\n"); + else out.append("\t\t\t\tlet membership = this." + fuzzyRuleTerm.getLinguisticTerm().toStringJSMethodName(fuzzyRuleTerm.getVariable()) + "(x);\n"); + + out.append("\t\t\t\tlet y = " + toStringJS() + "(" + degreeOfSupportName + " , membership);\n"); + + // Aggregate value + out.append("\t\t\t\tthis." + variable.toStringJSDefuzzifyVarName() + "[i] = " + ruleAccumulationMethod.toStringJS() + "(this." + variable.toStringJSDefuzzifyVarName() + "[i], y);\n"); + out.append("\t\t\t}\n"); + out.append("\t\t}\n"); + } + + return out.toString(); + } + + public String toStringJSFunction() { + throw new RuntimeException("Unimplemented method for class " + this.getClass().getCanonicalName()); } } diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/ruleActivationMethod/RuleActivationMethodMin.java b/src/main/java/net/sourceforge/jFuzzyLogic/ruleActivationMethod/RuleActivationMethodMin.java index 2cfe1e5..3fa60ac 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/ruleActivationMethod/RuleActivationMethodMin.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/ruleActivationMethod/RuleActivationMethodMin.java @@ -22,6 +22,11 @@ public class RuleActivationMethodMin extends RuleActivationMethod { return "double " + toStringCpp() + "(double degreeOfSupport, double membership)\t{ return (degreeOfSupport < membership ? degreeOfSupport : membership); }\n"; } + @Override + public String toStringJSFunction() { + return "function " + toStringJS() + "(degreeOfSupport, membership) {\n\treturn (degreeOfSupport < membership ? degreeOfSupport : membership);\n}\n"; + } + /** Printable FCL version */ @Override public String toStringFcl() { diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethod.java b/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethod.java index 655da22..c6a64a1 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethod.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethod.java @@ -40,8 +40,17 @@ public abstract class RuleConnectionMethod extends FclObject { return "ruleConnectionMethod_" + name; } + @Override + public String toStringJS() { + return "ruleConnectionMethod_" + name; + } + public String toStringCppFunction() { - throw new RuntimeException("Unimplemented method foe class " + this.getClass().getCanonicalName()); + throw new RuntimeException("Unimplemented method for class " + this.getClass().getCanonicalName()); + } + + public String toStringJSFunction() { + throw new RuntimeException("Unimplemented method for class " + this.getClass().getCanonicalName()); } } diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethodAndMin.java b/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethodAndMin.java index 151881a..5995ee1 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethodAndMin.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethodAndMin.java @@ -31,6 +31,11 @@ public class RuleConnectionMethodAndMin extends RuleConnectionMethod { return "double " + toStringCpp() + "(double antecedent1, double antecedent2)\t{ return (antecedent1 < antecedent2 ? antecedent1 : antecedent2); }\n"; } + @Override + public String toStringJSFunction() { + return "function " + toStringJS() + "(antecedent1, antecedent2) {\n\treturn (antecedent1 < antecedent2 ? antecedent1 : antecedent2);\n}\n"; + } + @Override public String toStringFcl() { return "AND : MIN;"; diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethodOrMax.java b/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethodOrMax.java index a1af61e..83f38b7 100644 --- a/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethodOrMax.java +++ b/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/RuleConnectionMethodOrMax.java @@ -31,6 +31,11 @@ public class RuleConnectionMethodOrMax extends RuleConnectionMethod { return "double " + toStringCpp() + "(double antecedent1, double antecedent2)\t{ return (antecedent1 > antecedent2 ? antecedent1 : antecedent2); }\n"; } + @Override + public String toStringJSFunction() { + return "function " + toStringJS() + "(antecedent1, antecedent2) {\n\treturn (antecedent1 > antecedent2 ? antecedent1 : antecedent2);\n}\n"; + } + @Override public String toStringFcl() { return "OR: MAX;"; diff --git a/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/Szczepanski.java b/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/Szczepanski.java deleted file mode 100644 index a77d999..0000000 --- a/src/main/java/net/sourceforge/jFuzzyLogic/ruleConnectionMethod/Szczepanski.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.sourceforge.jFuzzyLogic.ruleConnectionMethod; - -/** - * Author's data - * @author marcin-szczepanski - */ -public class Szczepanski { - - public static final String EMAIL = "marcin.szczepanski@amu.edu.pl"; - public static final String BY = "Marcin Szczepanski"; -}