Fuzzy Logic IF-THEN Rules in Amzi! Prolog

(Scalable Monotonic Chaining Version)
Cox's Case Study: A Project Risk Assesment Model
Reported in "The Fuzzy Systems Handbook", AP Professional, 1994, pp. 436-447.
Alberto Pacheco © 1997


/*
	RISK.PRO:
		Developed by Alberto Pacheco, 1996-1997
		e-mail: apacheco@campus.chi.itesm.mx

		This version supports:
		- One-goal, one-sample-at-a-time
		- Linear Fuzzy Membership Representations
	--------> Sigmoid Curve Membership Function (S-Curve Fuzzy Set) (as/ds)
		- Zadeh Fuzzy Set Operators
	--------> Mapping from Membreship Degree to I/O Domain (member_to_domain)
	--------> Scalable Monotonic Chaining (monotonic_scaling)

  To run, type:

	?- main.

  Test results:

	--- SAMPLE #1: OUTPUT FOR A LOW RISK ASSESMENT MODEL ---

	proj_duration: 8.000000
	proj_staffing: 12.000000
	proj_funding: 40.000000
	proj_priority: 3.000000

	project_risk :  amount = 142.222244  membership = 0.142222
	project_risk :  amount = 500.000000  membership = 0.500000
	project_risk :  amount = 600.000000  membership = 0.600000
	project_risk :  amount = 700.000000  membership = 0.700000

	Cummulative Risk is 1942.222168

	project_risk = 388.444427


	--- SAMPLE #2: OUTPUT FOR A HIGH RISK ASSESMENT MODEL ---

	proj_duration: 25.000000
	proj_staffing: 19.000000
	proj_funding: 10.000000
	proj_priority: 1.000000

	project_risk :  amount = 944.444397  membership = 0.944444
	project_risk :  amount = 791.666687  membership = 0.791667
	project_risk :  amount = 900.000000  membership = 0.900000
	project_risk :  amount = 900.000000  membership = 0.900000

	Cummulative Risk is 3536.111084

	project_risk = 707.222229

	--- END OF SAMPLE'S OUTPUT ---


	---> ..HERE ARE COX'S RESULTS FOR THE SAME INPUT STREAM <---


	--- SAMPLE #1: COX'S OUTPUT FOR A LOW RISK ASSESMENT MODEL ---

	proj_duration: 8.000000
	proj_staffing: 12.000000
	proj_funding: 40.000000
	proj_priority: 3.000000

	project_risk :  amount = 292.97  membership = 0.141
	project_risk :  amount = 515.62  membership = 0.498
	project_risk :  amount = 574.22  membership = 0.603
	project_risk :  amount = 632.81  membership = 0.704

	Cummulative Risk is 2015.62

	project_risk = 323.76


	--- SAMPLE #2: COX'S OUTPUT FOR A HIGH RISK ASSESMENT MODEL ---

	proj_duration: 25.000000
	proj_staffing: 19.000000
	proj_funding: 10.000000
	proj_priority: 1.000000

	project_risk :  amount = 839.84  membership = 0.944
	project_risk :  amount = 687.50  membership = 0.786
	project_risk :  amount = 792.97  membership = 0.903
	project_risk :  amount = 792.97  membership = 0.903

	Cummulative Risk is 3113.28

	project_risk = 712.86

*/


% Operator Definitions

:- op(700, xfx, is).
:- op(720, xfy, and).
:- op(740, xfy, or).
:- op(760, xfx, then).
:- op(780, fx,  if).


% Main Procedure:
%	1) Initialization;
%	2) Goal with Output Variable

main :-
	init(project_risk),
	one_goal(project_risk).


% Initialization: Clear global working memory

init(Var) :-
	retractall(_),
	assert(sum1(Var,0.0)),
	nl,write('Fuzzy Project Risk Assesment Model'),
	nl,write('Example taken from: Cox, E. "The Fuzzy Systems Handbook",'),
	write(' AP Professional, 1994, pp. 436-447.'),nl,nl,
	input_value( _, Input_Variable, Value ),
	write(Input_Variable),write(': '),write(Value),nl,
     fail.
init(_):-nl,!.



% Probes all rules with conclusion 'Var' 

one_goal(Var) :-
	prove( Var is X ),
   fail.

% Reports the composite solution

one_goal(Var) :-
	output_value(Var,X),
	nl, write(Var), write(' = '), write(X), nl.

/*
 :::  PRODUCTION RULES (FUZZY MODEL'S RULES)  :::

	A Project Risk Assesment Model
	Based on Earl Cox's Case Study
	Book:
		Cox, E. "The Fuzzy Systems Handbook", AP Professional, 1994
		pp. 436-447.
*/


% R-1
if	proj_duration	is  long
then	project_risk	is  increased.

% R-2
if	proj_staffing	is  large
then	project_risk	is  increased.

% R-3
if	proj_funding	is  low
then	project_risk	is  increased.

% R-4
if	proj_priority	is  high
then	project_risk	is  increased.


%:::  FUZZY SETS DEFINITIONS  :::

% Note: The Maximun Domain Limit acts as a Weighted Measure for its corresponding set

fuzzy_set( proj_duration,  long,	as,	0.0,   15.0,  30.0,   0.0 ).
fuzzy_set( proj_staffing,  large,	at,	0.0,   24.0,   0.0,   0.0 ).
fuzzy_set( proj_funding,   low,		dt,     0.0,  100.0,   0.0,   0.0 ). % Greatest Weight
fuzzy_set( proj_priority,  high,	dt,     0.0,   10.0,   0.0,   0.0 ). % Lowest Weight


%:::  OUTPUT FUZZY SETS  :::

fuzzy_set( project_risk,   increased,	as,	0.0, 1000.0,   0.0,  0.0 ).
fuzzy_set( project_risk,   high_risk,	at,	0.0, 5000.0,   0.0,  0.0 ).


%:::  INPUT VALUES  :::

% SAMPLE #1: A LOW RISK ASSESMENT MODEL INPUT
 
input_value( 1, proj_duration,	8.0  ).
input_value( 1, proj_staffing, 12.0  ).
input_value( 1, proj_funding,  40.0  ).
input_value( 1, proj_priority,  3.0  ).

/*
% SAMPLE #2: A HIGH RISK ASSESMENT MODEL INPUT

input_value( 1, proj_duration, 25.0  ).
input_value( 1, proj_staffing, 19.0  ).
input_value( 1, proj_funding,  10.0  ).
input_value( 1, proj_priority,  1.0  ).
*/

/***************************************************************
	Prolog Interpreter in Prolog
	Based on Dennis Merrit's Article
	"Building Custom Rule Engines," PC AI, Mar/Apr 1996.
****************************************************************/

prove(ATTR is VALUE and REST) :-	% AND
	getav(ATTR, VALUE),
	prove(REST),
	apply_fuzzy_oper(and_z).
prove(ATTR is VALUE or REST) :-		% OR
	getav(ATTR, VALUE),
	prove(REST),
	apply_fuzzy_oper(or_z).
prove(ATTR is VALUE) :-			% IS
	getav(ATTR,VALUE).

getav(ATTR,VALUE) :-			% IF/THEN's CONCLUSION
	if CONDITIONS then ATTR is VALUE,
	prove(CONDITIONS),
	retract(prem(Mx)),
	monotonic_scaling(ATTR,VALUE,Mx).
getav(ATTR,VALUE) :-
	not(if _ then ATTR is _),
	rule_translation(ATTR,VALUE).


%:::  IS A BOOLEAN OR A FUZZY RULE?  :::


% FUZZY RULE PROCESSING

rule_translation( T, Cj ) :-				
	clause( fuzzy_set(T,_,_,_,_,_,_), _ ), !,
	input_value( _, T, X ), !,
	fuzzification( T, Cj, X ).

% DISCRETE INFERENCE RULE PROCESSING

rule_translation( T, Cj ) :-
	input_value( _, T, Cj ), !,
	is_true.
rule_translation( T, Cj ) :-
	is_false,
	nl,write('Error in rule_translation(): Undefined set'),nl,write(T),nl,write(Cj).


%:::  FUZZIFICACION  :::

fuzzification( Name, Set, X_Value ) :-
	fuzzy_set( Name, Set, Type, A, B, C, D ),
	degree_of_membership( Type, A, B, C, D, X_Value, Membership ),
	assert( prem(Membership) ), !.

/*
	LINEAR DECREASING FUZZY SET (i,i,i,i,i,i,o)
	dt
		A - Minimum Value
		B - Maximum Value
*/

degree_of_membership( dt, A, _, _, _, X, 1.0 ) :-
	X =< A, !.

degree_of_membership( dt, _, B, _, _, X, 0.0 ) :-
	X >= B, !.

degree_of_membership( dt, A, B, _, _, X, M ) :-
	line_eq( dt, A, B, X, M ), !.


/*
	LINEAR INCREASING FUZZY SET
	at
		A - Minimum Value
		B - Maximum Value
*/

degree_of_membership( at, A, _, _, _, X, 0.0 ) :-
	X =< A, !.

degree_of_membership( at, _, B, _, _, X, 1.0 ) :-
	X >= B, !.

degree_of_membership( at, A, B, _, _, X, M ) :-
	line_eq( at, A, B, X, M ), !.
	

/*
	TRAPEZOIDAL OR TRIANGULAR FUZZY SET
	tp	 -- TRAPEZOIDAL --	 -- TRIANGULAR --
		A - Minimum Value	A - Minimum Value
		B - Left Shoulder	B - Center
		C - Right Shoulder	C - Center
		D - Maximum Value	D - Maximum Value
*/

degree_of_membership( tp, A, _, _, _, X, 0.0 ) :-
	X =< A, !.

degree_of_membership( tp, A, B, _, _, X, M ) :-
	X > A, X =< B,
	line_eq( at, A, B, X, M ), !.

degree_of_membership( tp, _, B, C, _, X, 1.0 ) :-
	X > B, X < C, !.

degree_of_membership( tp, _, _, C, D, X, M ) :-
	X > C, X < D,
	line_eq( dt, C, D, X, M ), !.

degree_of_membership( tp, _, _, _, _, _, 0.0 ).  % X>D


/*
	INCREASING S-CURVE FUZZY SET
	as
		A - Minimum Value
		B - Inflexion Point (point at wich the domain value is 50% true
		C - Maximum Value
*/

degree_of_membership( as, A, _, _, _, X, 0.0 ) :-
	X =< A, !.

degree_of_membership( as, A, B, C, _, X, M ) :-
	X > A, X =< B,
	M is (2.0 * ((X-A)/(C-A))**2.0), !.

degree_of_membership( as, A, B, C, _, X, M ) :-
	X > B, X < C,
	M is (1.0 - 2.0*(((C-X)/(C-A))**2.0)), !.  % Cox's Bug? Eq. 3.2 p.52
/*
degree_of_membership( as, A, B, C, _, X, M ) :-
	X > B, X < C,
	M is (1.0 - 2.0*((X-C)/(C-A))**2.0), !.
*/
degree_of_membership( as, _, _, _, _, _, 1.0 ).  % X>=C



% FINDS THE EXPECTED OUTPUT VALUE OF THE FUZZY SET GIVEN ITS DEGREE OF MEMBERSHIP
% === member_to_domain( A, B, Membership-Degree, X-Domain-Value ) - (i,i,i,o) ===

member_to_domain( X1, X2, Memb, X ) :-
	X is ( Memb * (X2 - X1) + X1 ), !.


%:::  FUZZY OPERATORS  :::

apply_fuzzy_oper( and_z ) :-
	retract(prem(M1)),
	write(M1),
	retract(prem(M2)), !,
	write(' and '), write(M2), nl,
	min(M1,M2,M),
	assert(prem(M)), !.

apply_fuzzy_oper( or_z ) :-
	retract(prem(M1)),
	write(M1),
	retract(prem(M2)),!,
	write(' or '), write(M2), nl,
	max(M1,M2,M),
	assert(prem(M)), !.


%:::  MONOTONIC SCALING CHAINING MODEL - (i,i,i)  :::

monotonic_scaling( Var, OutSet, Memb ) :-
	fuzzy_set( Var, OutSet, _, X1, X2, _, _ ), !,
	member_to_domain( X1, X2, Memb, X ),
	retract( sum1(Var,Q) ),
	R is ( X + Q ),
	assert( sum1(Var,R) ),
	write(Var),write(' :  amount = '),write(X),
	write('  membership = '),write(Memb),nl,!.


%:::  END OF DEFUZZIFICATION METHOD  ::::

output_value( Var, X ) :-
	retract( sum1( Var, Cummulative_Risk ) ),
	nl, write('Cummulative Risk is '),write(Cummulative_Risk),nl,
	fuzzification( Var, high_risk, Cummulative_Risk ),
	retract( prem( Memb ) ),
	X is (Memb * 1000.0).


%:::  LINEAR INTERPOLATION  :::

line_eq( dt, X1, X2, X, Y ) :-
	Y is (X2 - X) / (X2 - X1).

line_eq( at, X1, X2, X, Y ) :-
	Y is (X - X1) / (X2 - X1).


%::: BOOLEAN VALUES  :::

is_true :- assert(prem(1.0)), !.

is_false :- assert(prem(0.0)), !.


%:::  FUZZY FUNCTION PRIMITIVES  :::

min( X, Y, X ) :- X < Y, !.
min( _, Y, Y ).

max( X, Y, X ) :- X > Y, !.
max( _, Y, Y ).