# --------------------------------
# FGCM plugin for EYE -- Gijs Muys
# --------------------------------
#
# Decision support systems are extremely useful in the medical domain.
# The creation of such systems requires precise medical parameters which
# are not always available. One possible solution is to approximate such
# parameters, knowing they won’t be completely correct. However, this
# error can have a big impact on the result. Recently prof. J. L. Salmeron
# has proposed a new technology named fuzzy grey cognitive maps. This
# technology enables someone to deal with a lot of uncertainty. Therefore
# fuzzy grey cognitive maps seem like a perfect fit to use for medical
# purposes.
# 
# Fuzzy cognitive maps use directed graphs to represent knowledge and the
# fuzzy set theory to give variables a membership between zero and one
# instead of only the values zero and one. On top of that it allows us to
# denote the relation between two variables with a correlation coefficient.
# Decisions are made by using an iterative algorithm.
# 
# An extension of fuzzy cognitive maps, named fuzzy grey cognitive maps,
# introduces the use of intervals as the value for both the relation
# between two variables and the variable itself. This severely improves
# the expressivity and the possibility to handle uncertain variables and
# relations. We implement the algorithm using N3 and the EYE reasoner.
# The fact that relations do not have to be given a precise value is a
# tremendous benefit.


PREFIX log: <http://www.w3.org/2000/10/swap/log#>
PREFIX fl: <http://eulersharp.sourceforge.net/2003/03swap/fl-rules#>
PREFIX e: <http://eulersharp.sourceforge.net/2003/03swap/log-rules#>
PREFIX prolog: <http://eulersharp.sourceforge.net/2003/03swap/prolog#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX fgcm: <http://eulersharp.sourceforge.net/2006/02swap/fgcm-plugin#>
PREFIX math: <http://www.w3.org/2000/10/swap/math#>
PREFIX dco: <http://www.debugit.eu/ontology/1.0/dco.owl#>

{(?A ?B ?UnknownVar) fl:gpi (?C1 ?C2)} <=
{   
        ?SCOPE e:call {
		(	{("fnet"^^prolog:atom "done"^^prolog:atom) prolog:nb_getval true}
			true
			{	(	{	
                                                () fgcm:expandClosureGmu true.
						() fgcm:expandClosureGsigma true.
                                                (?UnknownVar) fgcm:handleUnknownVariables true.
                                                (?A 1) fgcm:iterate ?IterationsNeeded.
                                                ("IterationsNeeded"^^prolog:atom ?IterationsNeeded) prolog:nb_setval true.

					}
					{("fnet"^^prolog:atom "done"^^prolog:atom) prolog:nb_setval true}
				) prolog:disjunction true
			}
		) prolog:if_then_else true.
                ("IterationsNeeded"^^prolog:atom ?IterationsNeeded) prolog:nb_getval true.               
                ((fgcm:gpi ?A ?B ?MembershipLower ?MembershipUpper ?IterationsNeeded)^prolog:univ) prolog:call true.
		(	{?SCOPE e:closure {(?A ?B) fl:gpi (?FixedMembershipLower ?FixedMembershipUpper)}}
			{
                                ?C1 log:equalTo ?FixedMembershipLower.
                                ?C2 log:equalTo ?FixedMembershipUpper.                        
                        }
			{
                                ?C1 log:equalTo ?MembershipLower.
                                ?C2 log:equalTo ?MembershipUpper.
                        }
		) prolog:if_then_else true.
	}
}.

{(?A ?B ?UnknownVar) fl:gpiWhitenized ?C} <=
{   
        ?SCOPE e:call {
		(	{("fnet"^^prolog:atom "done"^^prolog:atom) prolog:nb_getval true}
			true
			{	(	{	
                                                () fgcm:expandClosureGmu true.
						() fgcm:expandClosureGsigma true.
                                                
                                                (?UnknownVar) fgcm:handleUnknownVariables true.

                                                (?A 1) fgcm:iterate ?IterationsNeeded.
                                                ("IterationsNeeded"^^prolog:atom ?IterationsNeeded) prolog:nb_setval true.

					}
					{("fnet"^^prolog:atom "done"^^prolog:atom) prolog:nb_setval true}
				) prolog:disjunction true
			}
		) prolog:if_then_else true.
                ("IterationsNeeded"^^prolog:atom ?IterationsNeeded) prolog:nb_getval true.               
                ((fgcm:gpi ?A ?B ?MembershipLower ?MembershipUpper ?IterationsNeeded)^prolog:univ) prolog:call true.
		(	{?SCOPE e:closure {(?A ?B) fl:gpi (?FixedMembershipLower ?FixedMembershipUpper)}}
			{                  
                                (?FixedMembershipLower ?FixedMembershipUpper) fgcm:whitenize ?C.
                        }
			{
                                (?MembershipLower ?MembershipUpper) fgcm:whitenize ?C.                                                            
                        }
		) prolog:if_then_else true.
	}
}.

#Expand deductive closure
#In the context of statements, a deductive closure is the set of all the statements that can be deduced from a given set of statements.

#Initialize given variable values
{() fgcm:expandClosureGmu true} <= 
{
        (	{(?X ?Y) fl:gmu (?Z ?Q)}
		{
                	(	{(?X) fgcm:fm true}
			        true
        			{({(?X) fgcm:fm true}) prolog:assertz true}
			) prolog:if_then_else true.
		        ((fgcm:gpi ?X ?Y ?Z ?Q 0)^prolog:univ) prolog:assertz true.
		}
	) prolog:forall true.
}.

#Setup model
{() fgcm:expandClosureGsigma true} <= 
{
        (	{(?X ?Y) fl:gsigma (?V6 ?V7)}
		{	
                        (	{(?X) fgcm:fs true}
				true
				{({(?X) fgcm:fs true}) prolog:assertz true}
			) prolog:if_then_else true.
			(	{(?Y) fgcm:fs true}
				true
        			{({(?Y) fgcm:fs true}) prolog:assertz true}
			) prolog:if_then_else true.
		}
	) prolog:forall true.
}.

{(?UnknownVar) fgcm:handleUnknownVariables true} <= {
        (
                {(?UnknownVar true) prolog:unify true.}
                {
                        () fgcm:assignUnknownVariables true.
                }
                {true.}
        ) prolog:if_then_else true.
}.

#If variable value unknown, initialize value as [0,1]
{() fgcm:assignUnknownVariables true} <=
{

        (?Observable) fgcm:fm true.
        (       {(?Node) fgcm:fs true.}
                {
                        (       {
                                        ((fgcm:gpi ?Observable ?Node ?MembershipLower ?MembershipUpper 0)^prolog:univ) prolog:call true.
                                }
                                {
                                        true
                                }
                                {
                                        ((fgcm:gpi ?Observable ?Node 0.0 1.0 0)^prolog:univ) prolog:assertz true.       
                                }
                        ) prolog:if_then_else true.
                }
        ) prolog:forall true. 
}.

#Calculates the sum of a list of tuples. eg [(1,1),(2,3)] = (3,4)
{(?List ?Acc) fgcm:tupleSum ?Acc} <= 
{
        ?List log:equalTo ().
}.


{([ rdf:first (?H1 ?H2); rdf:rest ?Tail] (?A1 ?A2)) fgcm:tupleSum ?C} <= 
{       
        (?H1 ?A1) prolog:plus_function ?NewA1.
        (?H2 ?A2) prolog:plus_function ?NewA2.
        (?Tail (?NewA1 ?NewA2)) fgcm:tupleSum ?C.
}.

#Keeps on iterating until the difference between subsequent iterations is small enough. Maximum of 1000 iterations allowed
{(?A ?NbIterations) fgcm:iterate ?IterationsNeeded} <=
{
        (       {(?NbIterations 1000) prolog:arithmetic_less_than_or_equal true}
                {
                        (?NbIterations 1) prolog:minus ?PrevNbIterations.
                        (?A ?PrevNbIterations) fgcm:getNodes ?NodesPreviousValue.
                        (
                                {
                                        (?PrevNbIterations ?NbIterations) fgcm:calculateNodes true. 
                                        #always fails once all nodes have been calculated                                       
                                } 
                                {
                                        (?PrevNbIterations ?NbIterations) fgcm:refresh true.
                                        (?A ?NbIterations) fgcm:getNodes ?NodesCurrentValue.
                                        (?Epsilon 0.001) prolog:unify true.
                                        
                                        (
                                                {(?NodesPreviousValue ?NodesCurrentValue ?Epsilon) fgcm:differenceBelowEpsilon true}
                                                {
                                                        (?IterationsNeeded ?NbIterations) prolog:unify true.
                                                }
                                                {
                                                        (?NbIterations 1) prolog:plus_function ?NewNbIterations.   
              
                                                        (?A ?NewNbIterations) fgcm:iterate ?IterationsNeeded. 
                                                }
                                       ) prolog:if_then_else true.           
                                }
                        ) prolog:disjunction true.
                }
                {
                        ('Maximum number of iterations (1000) reached') prolog:throw true.
                }
        ) prolog:if_then_else true.
}.

#Calculates nodes for one iteration. Once all nodes have been processed, method fails.
{(?PrevNbIterations ?NbIterations) fgcm:calculateNodes true} <=
{
        (?Observable) fgcm:fm true.
	(?Node) fgcm:fs true.
	(	(?MinProduct ?MaxProduct)
		{	
                        (?IncomingNode ?Node) fl:gsigma (?EdgeLower ?EdgeUpper).
			((fgcm:gpi ?Observable ?IncomingNode ?MembershipLower ?MembershipUpper ?PrevNbIterations)^prolog:univ) prolog:call true.

                        #Some networks use edge weights [0,1] instead of [-1,1], conversion necessary
                        ((2 ?EdgeLower)!prolog:product 1) prolog:minus ?TempEdgeLower.
                        ((2 ?EdgeUpper)!prolog:product 1) prolog:minus ?TempEdgeUpper.

                        #FCM UTI network required nodes to be in [-1,1] interval as well
                        ((2 ?MembershipLower)!prolog:product 1) prolog:minus ?TempMembershipLower.
                        ((2 ?MembershipUpper)!prolog:product 1) prolog:minus ?TempMembershipUpper.

                        (?TempEdgeLower ?TempMembershipLower) prolog:product ?ProductLL.
                        (?TempEdgeLower ?TempMembershipUpper) prolog:product ?ProductLU.
                        (?TempEdgeUpper ?TempMembershipLower) prolog:product ?ProductUL.
                        (?TempEdgeUpper ?TempMembershipUpper) prolog:product ?ProductUU.

                        ((?ProductLL ?ProductLU ?ProductUL ?ProductUU) ?MinProduct) prolog:min_list true.
                        ((?ProductLL ?ProductLU ?ProductUL ?ProductUU) ?MaxProduct) prolog:max_list true.            
        	}                                                    
		?L
	) prolog:findall true.

	(	{?L log:equalTo ()}
		{
                        true
                }       
        	{	
                        (?L (0 0)) fgcm:tupleSum (?S1 ?S2).
			(1 (1 ((?S1)!prolog:minus)!prolog:exp)!prolog:plus) prolog:quotient ?Z1.
			(1 (1 ((?S2)!prolog:minus)!prolog:exp)!prolog:plus) prolog:quotient ?Z2.
			((fgcm:gpi ?Observable ?Node ?Z1 ?Z2 ?NbIterations)^prolog:univ) prolog:assertz true.
		}
	) prolog:if_then_else true.
	() prolog:fail true.
}.

#If node has been given a new value current iteration, nothing happens
#If node has not been given a new value current iteration, its value of previous iteration is taken and added to current iteration
{(?Prev ?Cur) fgcm:refresh true} <=
{
        (       {(?Node) fgcm:fs true.}
                {
                        (       {
                                        ((fgcm:gpi ?Observable ?Node ?MembershipLower ?MembershipUpper ?Cur)^prolog:univ) prolog:call true.
                                }
                                {
                                 	((fgcm:gpi ?Observable ?Node ?V1 ?V2 ?Prev)^prolog:univ) prolog:retractall true.
                                        true
                                }
                                {
                                        (                                        
                                                {((fgcm:gpi ?Observable ?Node ?MembershipLower ?MembershipUpper ?Prev)^prolog:univ) prolog:call true.}
                                                {
                                                        ((fgcm:gpi ?Observable ?Node ?MembershipLower ?MembershipUpper ?Cur)^prolog:univ) prolog:assertz true.
                                       			((fgcm:gpi ?Observable ?Node ?V1 ?V2 ?Prev)^prolog:univ) prolog:retractall true.
                                                }
                                                {true}
                                        ) prolog:if_then_else true.
                                }
                        ) prolog:if_then_else true.

                }
        ) prolog:forall true. 
}.

#Retrieves all nodes of given iteration
{(?A ?NbIterations) fgcm:getNodes ?Nodes} <=
{
        (
                (?MembershipLower ?MembershipUpper)
                {
                        ((fgcm:gpi ?A ?V1 ?MembershipLower ?MembershipUpper ?NbIterations)^prolog:univ) prolog:call true.           
                }
                ?Nodes #Sequence of nodes will be the same every iteration so no need to remember which node belongs to which lower & upper membership
        ) prolog:findall true.
}.

#Returns true if difference between two given lists is less than given epsilon
{([ rdf:first (?H1LowerMembership ?H1UpperMembership); rdf:rest ()] [ rdf:first (?H2LowerMembership ?H2UpperMembership); rdf:rest ()] ?Epsilon) fgcm:differenceBelowEpsilon true} <=
{
        (?H1LowerMembership ?H2LowerMembership) fgcm:absoluteSubtraction ?LowerSubtraction.
        (?H1UpperMembership ?H2UpperMembership) fgcm:absoluteSubtraction ?UpperSubtraction.
        (?LowerSubtraction ?Epsilon) prolog:arithmetic_less_than_or_equal true.      
        (?UpperSubtraction ?Epsilon) prolog:arithmetic_less_than_or_equal true.
}.

{([ rdf:first (?H1LowerMembership ?H1UpperMembership); rdf:rest ?T1] [ rdf:first (?H2LowerMembership ?H2UpperMembership); rdf:rest ?T2] ?Epsilon) fgcm:differenceBelowEpsilon ?Result} <=
{       
        (?T1) log:notEqualTo ().
        (?T2) log:notEqualTo ().
        (?H1LowerMembership ?H2LowerMembership) fgcm:absoluteSubtraction ?LowerSubtraction.
        (?H1UpperMembership ?H2UpperMembership) fgcm:absoluteSubtraction ?UpperSubtraction.
        (
                {({(?LowerSubtraction ?Epsilon) prolog:arithmetic_less_than_or_equal true} {(?UpperSubtraction ?Epsilon) prolog:arithmetic_less_than_or_equal true}) prolog:conjunction true}
                {(?T1 ?T2 ?Epsilon) fgcm:differenceBelowEpsilon ?Result.}
                {(?Result false) prolog:unify true.}
        ) prolog:if_then_else true.      

}.

#abs(A-B)
{(?A ?B) fgcm:absoluteSubtraction ?C} <=
{
        ((?A ?B)!prolog:minus) prolog:abs ?C.
}.

{(?Left ?Right) fgcm:whitenize ?Result} <=
{
        (?Right ?Left) prolog:minus ?Difference.
        (?Difference 2) math:quotient ?Half.
        (?Left ?Half) prolog:plus_function ?Result.      
}.