khigia / ocaml-fuzlog

Simple fuzzy logic inference for control

This URL has Read+Write access

ocaml-fuzlog / fuzlog / inference.ml
100644 361 lines (257 sloc) 8.16 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
module FuzIO = struct
 
    type t = {
        inputs: (string, float) Hashtbl.t;
        outputs: (string, FuzzySet.t option) Hashtbl.t;
    }
 
    let create () = {
        inputs = Hashtbl.create 10;
        outputs = Hashtbl.create 10;
    }
 
    let clear ctx =
        Hashtbl.clear ctx.inputs;
        Hashtbl.clear ctx.outputs
 
    let get_input ctx name =
        Hashtbl.find ctx.inputs name
 
    let set_input ctx name value =
        Hashtbl.replace ctx.inputs name value
 
    let set_output ctx name value =
        Hashtbl.replace ctx.outputs name (Some value)
    
    let apply_output ctx name f =
        let v0 = try Hashtbl.find ctx.outputs name with Not_found -> None in
        let v1 = f v0 in
        Hashtbl.replace ctx.outputs name v1
 
    let apply_outputs ctx f =
        Hashtbl.iter
            (fun name value -> Hashtbl.replace ctx.outputs name (f value))
            ctx.outputs
 
    let fold_outputs ctx f acc =
        Hashtbl.fold
            f
            ctx.outputs
            acc
 
    let debug ctx =
        Printf.printf "Fuzzy IO context:\n";
        Printf.printf " Inputs:\n";
        Hashtbl.iter
            (Printf.printf " %s=%f\n")
            ctx.inputs;
        Printf.printf " Outputs:\n";
        Hashtbl.iter
            (fun name value -> Printf.printf
                " %s=%s\n"
                name
                (match value with
                    | Some set -> FuzzySet.to_s set
                    | None -> "None"
                )
            )
            ctx.outputs
 
end (* module FuzIO *)
 
 
module Var = struct
    
    type t = {
        name: string;
        set: FuzzySet.t;
    }
 
    let create name set = {
        name = name;
        set = set;
    }
 
    let name var = var.name
 
    let value var = var.set
 
end (* module Var *)
 
 
module Vocabulary = struct
    
    let creators = Hashtbl.create 5
 
    let register_creator (name:string) (creator:(float list -> FuzzySet.t)) =
        Hashtbl.replace creators name creator
 
    let find_creator name =
        Hashtbl.find creators name
 
 
    type t = {
        vars: (string, Var.t) Hashtbl.t;
    }
 
    let create () = {
        vars = Hashtbl.create 10;
    }
    
    let get voc symb =
        Hashtbl.find voc.vars symb
 
    let set voc symb value =
        let var = Var.create symb value in
        Hashtbl.replace voc.vars symb var
 
    let length voc =
        Hashtbl.length voc.vars
 
end (* module Vocabulary *)
 
 
module Norm = struct
    
    type t = float -> float -> float
 
    let minimum x y = if x < y then x else y
 
    let product x y = x *. y
 
end (* module Norm *)
 
 
module Implication = struct
 
    type t = float -> FuzzySet.t -> FuzzySet.t
 
    let larsen activation set =
        FuzzySet.product set activation
 
end (* module Implication *)
 
 
module IsAlso = struct
 
    type t = FuzzySet.t -> FuzzySet.t -> FuzzySet.t
 
    let maximum set1 set2 =
        FuzzySet.combine_max set1 set2
 
end (* module IsAlso *)
 
 
module Defuzzyfication = struct
 
    type t = FuzzySet.t -> float
 
    let barycenter set =
        FuzzySet.x_cog set
 
end (* module Defuzzyfication *)
 
 
module Premisse = struct
 
    (* TODO Premisse.Input and Conclusion.Output could use inheritance ...*)
 
    module Input = struct
        (* fuzzyfier *)
 
        type t = {
            name: string;
            var: Var.t;
        }
 
        let create name var = {
            name = name;
            var = var;
        }
 
        let fuzzyfy input value =
            FuzzySet.mu (Var.value input.var) value
            
        let name input = input.name
 
        let to_s input =
            Printf.sprintf "%s IS %s" input.name (Var.name input.var)
 
    end (* module Input *)
 
 
    type t =
        | Input of Input.t
        | ConnectiveAnd of t * t
 
    let create_input name var =
        Input (Input.create name var)
 
    let connect_and prem1 prem2 =
        ConnectiveAnd (prem1, prem2)
 
    let rec eval premisse norm conorm ctx = match premisse with
        | Input input ->
            Input.fuzzyfy input (FuzIO.get_input ctx (Input.name input))
        | ConnectiveAnd (prem1, prem2) ->
            let act1 = eval prem1 norm conorm ctx in
            let act2 = eval prem2 norm conorm ctx in
            norm act1 act2
 
    let rec to_s prem = match prem with
        | Input input -> Input.to_s input
        | ConnectiveAnd (prem1, prem2) -> Printf.sprintf "((%s) AND (%s))" (to_s prem1) (to_s prem2)
 
end (* module Premisse *)
 
 
module Conclusion = struct
 
    module Output = struct
 
        type t = {
            name: string;
            var: Var.t;
        }
 
        let create name var = {
            name = name;
            var = var;
        }
 
        let eval output activation implication isAlso fuzvalue =
            let res = implication activation (Var.value output.var) in
            match fuzvalue with
            | Some value -> Some (isAlso value res)
            | None -> Some res
            
        let to_s output =
            Printf.sprintf "%s IS %s" output.name (Var.name output.var)
 
    end (* module Output *)
 
 
    type t =
        | Output of Output.t
 
    let create_output name var =
        Output (Output.create name var)
 
    let eval concl activation implication isAlso ctx =
        match concl with
        | Output output ->
            FuzIO.apply_output
                ctx
                output.Output.name
                (Output.eval output activation implication isAlso)
 
    let to_s concl = match concl with
        | Output output -> Output.to_s output
 
end (* module Conclusion *)
 
 
type operators = {
    opAnd: Norm.t;
    opImply: Implication.t;
    opIsAlso: IsAlso.t;
    opDefuzz: Defuzzyfication.t;
}
 
 
module Rule = struct
    
    type t = {
        cond: Premisse.t;
        concl: Conclusion.t;
    }
 
    let create cond concl = {
        cond = cond;
        concl = concl;
    }
 
    let to_s rule =
        Printf.sprintf
            "IF %s THEN %s END"
            (Premisse.to_s rule.cond)
            (Conclusion.to_s rule.concl)
 
    let _eval_activation rule ops ctx =
        Premisse.eval rule.cond ops.opAnd None ctx
 
    let _eval_conclusion rule act ops ctx =
        Conclusion.eval
            rule.concl
            act
            ops.opImply
            ops.opIsAlso
            ctx
 
    let eval rule ops ctx =
        let act = _eval_activation rule ops ctx in
        let _ = _eval_conclusion rule act ops ctx in
        act
 
end (* module Rule *)
 
 
(* a model or controller is:
- a set of rules defining the logic
- a set of functions defining the fuzzy evaluation
*)
module Controller = struct
 
    type t = {
        operators: operators;
        rules: Rule.t list;
    }
 
    let add_rule ctrl rule =
        {ctrl with rules = ctrl.rules @ [ rule; ]}
 
    let create
        ?(norm=Norm.minimum) (* "a AND b" in rule premisse *)
        ?(implication=Implication.larsen)
        ?(opIsAlso=IsAlso.maximum) (* "output IS V1 AND output IS V2" in rule conclusion *)
        ?(defuzz=Defuzzyfication.barycenter) (* from fuzzy value to crisp one *)
        rules
    =
        let ops = {
            opAnd = norm;
            opImply = implication;
            opIsAlso = opIsAlso;
            opDefuzz = defuzz
        } in
        let ctrl0 = {
            operators = ops;
            rules = [];
        } in
        List.fold_left
            (fun ctrl rule -> add_rule ctrl rule)
            ctrl0
            rules
 
    let eval ctrl ctx =
        List.fold_left
            (fun idx rule ->
                let act = Rule.eval rule ctrl.operators ctx in
                Printf.printf "Rule %d [%f]: %s\n"
                    idx
                    act
                    (Rule.to_s rule)
                ;
                idx + 1
            )
            0
            ctrl.rules
        
    let defuzz ctrl ctx =
        FuzIO.fold_outputs
            ctx
            (fun name value acc -> (match value with
                | None -> acc
                | Some set -> (name, ctrl.operators.opDefuzz set) :: acc
            ))
            []
 
end (* module Controller *)