Search code examples
javaclojuretreeexpert-system

Dynamic (tree) structure of functions as process (and implementation in Clojure)


This will all sound probably very strange as I was not able to find an exact term for what I am trying to do.

I am developing an application that, given a set of rules (which are easily translated into functions) and input/output pairs (that are not so easily translated into the code), would allow to construct a tree of the rules to be applied to the given input to reach given output.

It is similar to expert system, except the goal is not to determine "better" (by some quality) rule/function tree (actually mapping input to output is secondary by itself) but to be able to build those trees according to some restrictions or business rules.

I am trying to do this in Clojure, but I will appreciate more general advice as well since I cannot even figure out how to call this sort of thing in short.

Going into details, supposedly I have a large flat map of details. I have a long list of functions that I transformed to do almost the same: each function takes this large flat map of details, and applies the rule to whatever value(s) this rule concerns. Function has side effects (logs what it does) and, supposedly, single boolean output that is used by the said (to be) tree structure to determine what branch to go into (if tree splits on this function).

The idea is that I can take one function and declare it as a root of the tree. Then take either one another function and declare it to be next function to do, or take two functions and declare them as two next branches from the root depending on the root function output. And so on and so forth. And I need to be able to do it as many times as I want, producing a tree that fits some requirements. I will be doing all of the logic, but I need a structure that would allow me to apply the tree of functions (that I can even construct myself as long as I only need to specify it as something as simple as a list) to the given input map, without having to manually code the whole process tree for every tree I will be trying to do.

Real life example would be a large tree-like data structure (input that we can flat down easily) that every client can want to be described (side effect of functions) according to his own set of rules when it is processed (reaches output).

Does this "procedure" have a more common name than this long description? Are there any functionalities withing Java/Clojure that can be used for it or should I try doing it myself? For those who know Clojure, I basically need a variation of one of the (->) family that can take a tree of functions, like

(tree-> 
input-buffer side-effect-buffer output-buffer (f1 (f2 f4 (f5 f7)) (f3 f6)))

Edit below: adding examples: This is just one part of a more general solution I am looking for:

A (mini)game that is based around alchemy (more generally, a mix of real chemistry and alchemy). In this case, input is grouped measurable/observable characteristics of a concoction, for example:

(def concoction 
    {:observable {:color {:r 50 :g 50 :b 50} :opacity 50}
     :measurable {:acidity 50 :density 50 :fluidity 50 :composition "TO DO"}
     :testable {:duration 10 :known-effects [list of maps] :safety 10 :after "TO DO"}})

Output is a vector of maps each of which is similar to:

{:ingredient "ingredient-a" :amount 20 :application {:type "mix" :order 0}}

The (stand-alone) function in general consist of 3 parts:

  1. Get one (or more) characteristics of the concoction.
  2. Apply some restricted logic to the chosen characteristics (few entries from table of individual effects of ingredient on the resulting concoction, table of application types or huge table of combined effects of two or more ingredients).
  3. Log processed characteristics into shared log/info output.
  4. Append result of application of the logic to the output.
  5. Return boolean (for now, it will be int later) that signals what level of success this step had in terms of producing output.

I changed logic around a bit so now I have only one function that applies a given piece of logic to the input (instead of having almost infinite amount of similar functions) similar to:

(defn apply-logic [input logic-to-apply]
    (let [chalist (apply (:input logic-to-apply) input)
          out (apply (:logic logic-to-apply) chalist)]
     (info-out (apply (:format logic-to-apply) out))
     (return-grade out chalist))))
; info-out uses info-output and output variables set with let somewhere outside

Then I would have a tree of logic to apply instead of functions:

(def tree-1112 '(logic1 
                 (logic2 
                  (logic3 
                   (logic4 logic5))) 
                 (logic6 
                   (logic7) 
                   (logic8 
                    (logic9)))))

And a some sort of apply-tree-logic:

(defn apply-tree-logic [some-tree input]
 (if (apply-logic input (take-root some-tree)) 
  (apply-tree-logic (take-branch first some-tree) input)
  (apply-tree-logic (take-branch last some-tree) input))

Practically if I could do exactly what I brought in these examples it would be pretty close to implementing it all myself. But then it would take me ages to optimize all of this.


Solution

  • It sounds like what you are trying to do is similar in some respects to Plumbing.

    A Graph is just a map from keywords to keyword functions. In this case, stats-graph represents the steps in taking a sequence of numbers (xs) and producing univariate statistics on those numbers (i.e., the mean m and the variance v). The names of arguments to each fnk can refer to other steps that must happen before the step executes. For instance, in the above, to execute :v, you must first execute the :m and :m2 steps (mean and mean-square respectively).