Uploaded image for project: 'Drools'
  1. Drools
  2. DROOLS-1422

Make AccumulateFunction interface simpler and more powerful

    XMLWordPrintable

Details

    • Feature Request
    • Resolution: Won't Do
    • Major
    • None
    • None
    • core engine
    • None
    • 2020 Week 52-03 (from Dec 21)
    • NEW
    • NEW

    Description

      • Replace the AccumulateFunction interface with the AccumulateFunction2 interface:
        • All core runtime code uses the AccumulateFunction2 interface.
        • Deprecate AccumulateFunction and use a bridge class at DRL compilation time so old implementations still work because they are bridged into the new interface
      • Changes of the AccumulateFunction2:
        • Methods no longer throw checked exceptions (no "throws Exception"), so drools doesn't have to catch them. This might improve performance.
        • Remove "Serializable context". The class itself contains the state.
          • Remove method createContext()
          • Replace method init(Serializeble) with constructor call (no-args normally)
          • Remove parameter Serializable from accumulate(value), reserve(value) and getResult() methods
          • Instead of making 1 AccFunction instance and n context instances, make n AccFunction instances. This uses less memory.
          • This might improve performance (data locality).
        • It should Serializable, but doesn't have to Externalizable. Removes methods writeExternal() and readExternal() in the user implementation.
      • Interface ReversableAccumulateFunction2 extends AccumulateFunction2
        • Only ReversableAccumulateFunction2 has method reverse(value)
        • Remove method supportsReverse(): the custom accumulate is reverseable if it also implements this interface
      • Opportunities
        • construction parameters. For example: fixed average for standard deviation (very useful for OptaPlanner)
        • Multi-argument accumulates, for example in DRL: `$total : standardDeviation($groupBy, $weight)`

      Notice how clean the user implementation would become.

      // NEW
      public class SumAccumulateFunction
              implements ReversableAccumulateFunction2<Integer, Integer> {
      
          public int total;
      
          public SumAccumulateFunction() {
              total = 0;
          }
      
          public void accumulate(Integer value) {
              total += value;
          }
      
          public void reverse(Integer value) {
              total -= value;
          }
      
          public Integer getResult() {
              return total;
          }
      
      }
      

      Compare that with the old way:

      // OLD
      public class SumAccumulateFunction implements AccumulateFunction {
      
          public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { }
      
          public void writeExternal(ObjectOutput out) throws IOException { }
      
          protected static class SumData implements Externalizable {
              public double total = 0;
      
              public SumData() {}
      
              public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
                  total   = in.readDouble();
              }
      
              public void writeExternal(ObjectOutput out) throws IOException {
                  out.writeDouble(total);
              }
      
          }
      
          public Serializable createContext() {
              return new SumData();
          }
      
          public void init(Serializable context) {
              SumData data = (SumData) context;
              data.total = 0;
          }
      
          public void accumulate(Serializable context,
                                 Object value) {
              SumData data = (SumData) context;
              data.total += ((Number) value).doubleValue();
          }
      
          public void reverse(Serializable context,
                              Object value) {
              SumData data = (SumData) context;
              data.total -= ((Number) value).doubleValue();
          }
      
          public Object getResult(Serializable context) {
              return ((SumData) context).total;
          }
      
          public boolean supportsReverse() {
              return true;
          }
      
          public Class<?> getResultType() {
              return Double.class;
          }
      }
      

      Some other cases:

      Different result type than value type:

      public class AverageAccumulateFunction
              implements ReversableAccumulateFunction2<Integer, Double> {
      
          public int total;
          public int count;
      
          public AverageAccumulateFunction() {
              total = 0;
              count = 0;
          }
      
          public void accumulate(Integer value) {
              total += value;
              count++;
          }
      
          public void reverse(Integer value) {
              total -= value;
              count--;
          }
      
          public Double getResult() {
              return (double) total / count;
          }
      
      }
      

      Construction parameter:

      public class StdDeviationAccumulateFunction
              implements ReversableAccumulateFunction2<Integer, Double> {
      
          public final double average;
          public double variance;
      
          public StdDeviationAccumulateFunction(double average) {
              this.average = average;
              variance = 0;
          }
      
          public void accumulate(double value) {
              variance += (value - average)²; // TODO
          }
      
          public void reverse(double value) {
              variance -= (value - average)²; // TODO
          }
      
          public Double getResult() {
              return Math.sqrt(variance);
          }
      
      }
      

      TODO multi-argument accumulate:

      ...

      Attachments

        Issue Links

          Activity

            People

              mfusco@redhat.com Mario Fusco
              gdesmet@redhat.com Geoffrey De Smet (Inactive)
              Votes:
              1 Vote for this issue
              Watchers:
              3 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: