PrettyPFA
PFA was designed for data processing.
PrettyPFA provides a C-like syntax for PFA, with slight difference in syntax from mainstream progrmming languages.
A PrettyPFA document is split into sections, each of which has different rules. The syntax of these sections resembles PFA in YAML.
Not everything is built algorithmically in PFA; some parts of a PFA document, such as pre- and post-processing, are usually written by hand. For these parts, there are compilers that turn human-readable code into PFA.
Below are some examples of building models using PrettyPFA.
Link to complete PrettyPFA reference - https://github.com/opendatagroup/hadrian/wiki/PrettyPFA-Reference
In [1]:
    from titus.genpy import PFAEngine
from titus import prettypfa
Finding Square Root¶
In [2]:
    pfaDocument = prettypfa.jsonNode("""
name: SquareRoot
input: double
output: union(double, null)
action:
  if (input >= 0.0)
    m.sqrt(input)
  else
    null
""")
engine, = PFAEngine.fromJson(pfaDocument)
In [3]:
    engine.action(5)
Out[3]:
Rule Based Classification¶
JSON¶
In [4]:
    pfaDocument = """
{
    "input": {
        "type": "record",
        "name": "Iris",
        "fields": [
          {"name": "sepal_length_cm", "type": "double"},
          {"name": "sepal_width_cm", "type": "double"},
          {"name": "petal_length_cm", "type": "double"},
          {"name": "petal_width_cm", "type": "double"},
          {"name": "class", "type": "string"}
        ]
    },
    "output": "string",
    "action": [{
        "if": {"<": ["input.petal_length_cm", 2.5]},
        "then": {"string": "Iris-setosa"},
        "else":{
            "if": {"<": ["input.petal_length_cm", 4.8]},
            "then": {"string": "Iris-versicolor"},
            "else":{
                "if": {"<": ["input.petal_width_cm", 1.7]},
                "then": {"string": "Iris-versicolor"},
                "else": {"string": "Iris-virginica"}
            }
        }
    }]
}
"""
engine, = PFAEngine.fromJson(pfaDocument)
In [5]:
    engine.action({"sepal_length_cm": 5.1, "sepal_width_cm": 3.5,
               "petal_length_cm": 1.4, "petal_width_cm": 0.2,
               "class": "Iris-setosa"})
Out[5]:
In [6]:
    import csv
dataset = csv.reader(open("../../assets/iris.csv"))
fields = next(dataset)
numCorrect = 0.0
numTotal = 0.0
for datum in dataset:
    asRecord = dict(zip(fields, datum))
    if engine.action(asRecord) == asRecord["class"]:
        numCorrect += 1.0
    numTotal += 1.0
print("accuracy", numCorrect/numTotal)
PrettyPFA (Conditional Statements)¶
In [7]:
    pfaDocument = prettypfa.jsonNode('''
input: record(sepal_length_cm: double,
              sepal_width_cm: double,
              petal_length_cm: double,
              petal_width_cm: double)
output: string
action:
  if (input.petal_length_cm < 2.5)
    "Iris-setosa"
  else if (input.petal_length_cm < 4.8)
    "Iris-versicolor"
  else if (input.petal_width_cm < 1.7)
    "Iris-versicolor"
  else
    "Iris-virginica"
''')
engine, = PFAEngine.fromJson(pfaDocument)
In [8]:
    engine.action({"sepal_length_cm": 5.1, "sepal_width_cm": 3.5,
               "petal_length_cm": 1.4, "petal_width_cm": 0.2,
               "class": "Iris-setosa"})
Out[8]:
PrettyPFA (Rule Based)¶
In [9]:
    pfaDocument = prettypfa.jsonNode('''
input: record(sepal_length_cm: double,
              sepal_width_cm: double,
              petal_length_cm: double,
              petal_width_cm: double)
output: string
types:
  Rules = array(record(field: string,
                       cut: double,
                       result: string))
cells:
  rules(Rules) = [
    {field: "petal_length_cm", cut: 2.5, result: "Iris-setosa"},
    {field: "petal_length_cm", cut: 4.8, result: "Iris-versicolor"},
    {field: "petal_width_cm", cut: 1.7, result: "Iris-versicolor"},
    {field: "none", cut: -1, result: "Iris-virginica"}
  ]
action:
  var result = "";
  for (index = 0; result == ""; index = index + 1) {
    var rule = rules[index];
    var fieldValue =
      if (rule.field == "sepal_length_cm") input.sepal_length_cm
      else if (rule.field == "sepal_width_cm") input.sepal_width_cm
      else if (rule.field == "petal_length_cm") input.petal_length_cm
      else if (rule.field == "petal_width_cm") input.petal_width_cm
      else -1.0;
    if (rule.field == "none"  ||  fieldValue < rule.cut)
      result = rule.result;
  };
  result
''')
engine, = PFAEngine.fromJson(pfaDocument)
In [10]:
    engine.action({"sepal_length_cm": 5.1, "sepal_width_cm": 3.5,
               "petal_length_cm": 1.4, "petal_width_cm": 0.2,
               "class": "Iris-setosa"})
Out[10]:
Quadratic Formula¶
In [11]:
    pfaDocument = prettypfa.json('''
input: record(a: double, b: double, c: double)
output: union(null,
              record(Output,
                     solution1: double,
                     solution2: double))
action:
  var a = input.a, b = input.b, c = input.c;
  var discriminant = b**2 - 4*a*c;
  if (discriminant >= 0.0) {
    // if there are any real solutions, return them
    var x1 = -b + m.sqrt(discriminant)/(2*a);
    var x2 = -b - m.sqrt(discriminant)/(2*a);
    new(Output, solution1: x1, solution2: x2)
  }
  else
    // otherwise, return null (N/A)        
    null
''')
engine, = PFAEngine.fromJson(pfaDocument)
In [12]:
    print(engine.action({"a": 1, "b": 8, "c": 4}))
In [13]:
    print(engine.action({"a": 1, "b": 2, "c": 3}))
Applying Function¶
In [14]:
    pfa = prettypfa.json("""
input: enum([linear, square, cube])
output: int
action:
  apply(input, 2)
fcns:
  linear = fcn(x: int -> int) x;
  square = fcn(x: int -> int) x**2;
  cube = fcn(x: int -> int) x**3;
""")
engine, = PFAEngine.fromJson(pfa)
In [15]:
    engine.action("cube")
Out[15]: