Standard Prolog is based on crisp logic, in which a proposition is either absolutely true or is not known to be true and is therefore assumed to be absolutely false. PrologJ offers the option of performing computations using the rules of fuzzy logic in which a proposition can be partially true, not just absolutely true or false.
A fuzzy proposition that is regarded as partially true has associated with it a "truth value", which is a real number in the range 0.0 < truth-value < 1.0. Sometimes it is useful to think of a proposition that is regarded as being absolutely true as having a truth value of 1.0.
Fuzzy logic can be used to model inexact concepts that are inherently fuzzy - e.g. "Joe is a tall person." - a statement which would probably be regarded as absolutely true if Joe is a 6'8" basketball player, or absolutely false if Joe is a 3'6" Hobbit. But what if Joe is exactly six feet tall? Is Joe a tall person? Crisp logic has no clean way of modelling propositions that are "sort of" true. Fuzzy logic can also be used to model reasoning under uncertainty - e.g. "The witness is 90% sure that the robber was male".
PrologJ provides optional support for using fuzzy logic. This support is implemented in such a way as to have no effect on computations unless fuzzy computation is explicitly specified by the user. Moreover, the fuzzy logic extension can be either enabled or disabled at system startup by making the various built-in predicates that support it be either available or unavailable. (Support for fuzzy logic is enabled by default.)
When the use of fuzzy logic is enabled, a "top-level" PrologJ computation can be explicitly specified to be fuzzy. A computation that is not specified to be fuzzy is crisp by default. A fuzzy computation can either fail, or it can succeed with a truth value lying in (0 .. 1.0], with 1.0 corresponding to absolute truth. Of course, a crisp computation can only fail or succeed absolutely.
Likewise, a PrologJ clause (appearing in a source file or asserted by
asserta/1 or assertz/1) can be explicitly specified
to be fuzzy. A clause that is not specified to be fuzzy is crisp by default.
When a clause is crisp, its body is evaluated as a crisp computation; when it
is fuzzy, its body is evaluated as a fuzzy computation.
Clauses that are stored in the PrologJ database are either crisp or fuzzy. A clause that is asserted in the ordinary way (either as a fact, or a rule, or a grammar rule) is crisp. To assert a fuzzy clause, its head must be immediately followed by a colon and a number in the range (0.0 .. 1.0] - i.e. the number must be strictly greater than 0.0, and less than or equal to 1.0. This number is called the "inherent truth value" of the clause - it represents the truth value of a fact or the truth value that would be assigned to a computation using a rule if the body succeeds absolutely. The following examples illustrate the declaration of fuzzy facts, fuzzy rules, and fuzzy grammar rules.
secure(linux) : 0.95. % Linux is a very secure operating system
secure(X) : 0.3 :- % Any form of windows is moderately secure
atom_concat(windows, _, X).
verb : 0.7 --> % "Flies" is generally a verb
[ flies ].
When a fuzzy fact is used to attempt proving a goal, the truth value of the computatation is the inherent truth value of the fact. When a fuzzy clause is used to attempt proving a goal, its body is evaluated according to the rules of fuzzy logic, and if the body (partially) succeeds, then the truth value for the attempt to satisfy the goal is computed as (inherent truth value of clause) * (truth value of body). Example: Suppose we had the following rule in the database:
desirable(OS) : 0.8 % A secure operating system is generally desirable :- secure(OS).
If we now pose the following as a fuzzy query:
desirable('windows xp') : C.
We would get a truth value of (0.8) * (0.3) = 0.24.
Note that it is possible to specify a fuzzy clause with an inherent truth value of 1.0, which connotes absolute truth; this differs from a crisp clause in that an attempt to prove a goal using that clause will be done using the rules of fuzzy logic, and so the truth value of the computation will be the truth value of the body, which could be less than 1.0.
Any PrologJ computation is classified as either crisp or fuzzy. The result of a crisp computation is always either absolute success or failure. The result of a fuzzy computation may be absolute success, partial success, or failure. Which type of computation is performed may depend on the context in which the goal occurs.
?- prompt of the interpreter,
or that appears as the goal parameter of the call method of
the Java API, is said to be called in an initial context. The initial
context is considered to be crisp unless it is explicitly specified to be
fuzzy by following the goal with a return truth value variable and/or
a threshold specifier, as discussed later.call_fuzzy/1fuzzy_bagof/3fuzzy_best/1fuzzy_filter_set/4fuzzy_filter_set/5fuzzy_findall/3fuzzy_findset/3fuzzy_setof/3call/1catch/3','/2';'/2foreach/2->/2-> ;/3not/1\+/1once/1phrase/2bagof/3filter_set/4findall/3findset/3setof/3call/1catch/3','/2';'/2foreach/2->/2-> ;/3not/1\+/1once/1phrase/2Some goals are inherently crisp - e.g. most built-ins, all SQL predicates, or a predicate written in Prolog if a crisp clause is used to satisfy it. If an inherently crisp goal is called in a fuzzy context, then the goal can only succeed absolutely - which is taken as being success with a truth value of 1.0 - or fail. Partial success is not possible.
Likewise, some goals are inherently fuzzy - e.g. the built-ins
fuzzy_best/1 and fuzzy_truth/1, or a predicate
written in Prolog if a fuzzy clause is used to satisfy it. It is illegal to
call an inherently fuzzy goal in a crisp context; doing so will result in
the error permission_error(fuzzy_operation, Goal).
It is legal for a given predicate to be defined by a mixture of fuzzy and crisp clauses. Such a predicate should only be called in a fuzzy context, since an error would result if a fuzzy clause were used when the predicate is called in a crisp context.
The built-in predicate call_fuzzy/1 is added as part of the
fuzzy extension to allow an inherently fuzzy goal to be called in a crisp
context, with partial success treated as absolute success by a parent crisp
computation. See the discussion of this built-in predicate below.
Note: the following built-ins call their subgoal in a fuzzy context, but are themselves inherently crisp - that is, in the end, the built-in succeeds absolutely or fails depending on whether or not its "list" argument unifies with the computed list.
fuzzy_bagof/3fuzzy_filter_set/4fuzzy_filter_set/5fuzzy_findall/3fuzzy_findset/3fuzzy_setof/3
Any goal that appears in an initial context or a fuzzy context can be followed
by a return truth value variable, which takes the form of a colon (:)
followed by an uninstantiated variable. A fuzzy computation is performed, and
if it succeeds (partially or absolutely) the variable is instantiated to the
truth value resulting from the computation (1.0 meaning absolute success).
(Note: it is an error for the variable to be instantiated before the computation,
or to be instantiated by the computation itself.)
Example:
?- tall(joe) : V. % Succeeds if the database contains a crisp or fuzzy
% fact that joe is tall (or a rule that leads to this
% conclusion); if it succeeds, V is instantiated
% to the truth value of the fact or rule if it is crisp,
% or 1.0 if it is crisp.
The concept of a threshold is an important one in the PrologJ implementation of fuzzy logic. In a fuzzy computation, the threshold for a goal is the minimum truth value that will be considered to represent success for that goal. It is also possible to specify a threshold of 0.0 - which is taken to mean that a goal that succeeds with any degree of truth will be considered successful.
The default value for the threshold for a goal appearing in an initial context is 0.0. The default value for the threshold for a goal appearing in a fuzzy context is determined as follows:
call_fuzzy/1fuzzy_bagof/3fuzzy_best/1fuzzy_filter_set/4fuzzy_filter_set/5fuzzy_findall/3fuzzy_findset/3fuzzy_setof/3call/1catch/3','/2';'/2foreach/2->/2once/1phrase/2-> ;/3not/1\+/1
A higher threshold for any fuzzy goal can be specified by following the goal
with a double colon (::) and then a real number in the range
[0.0 .. 1.0). (If it is desired to specify a variable to be unified with the
truth value of the computation, then the variable comes first.) The effective
threshold is the maximum of the default threshold, determined as
discussed above, and the value specified. This mechanism can therefore only
be used to specify a threshold at least as high as the default; it cannot
be used to specify a lower value. It is also often used to specify the
threshold for an initial goal (since the default is 0.0 in this case).
Examples:
?- tall(Person) : V. % Find all Persons such that tall(Person)
% succeeds with any value - however small - in
% each case unify the actual truth value with V.
?- tall(Person) :: 0.5. % Find all the Persons such that tall(Person)
% succeeds with truth value at least 0.5.
?- tall(Person) : V :: 0.5. % Find all the Persons such that tall(Person)
% succeeds with truth value at least 0.5; in
% each case unify the actual truth value with V.
PrologJ uses the threshold to avoid the potential for combinatorial explosion when dealing with alternate ways of satisfying a goal. In fuzzy logic, the truth value of "A or B" is defined as maximum(truth value of A, truth value of B). This implies that - unless A succeeds absolutely (with the maximum possible truth value - 1.0) both subgoals need to be tried.
In Prolog, the various clauses for a predicate are considered to be connected
by "or". Thus, strict adherence to fuzzy logic would require that,
when attempting a goal for which there are multiple clauses, each clause
has to be tried in order to find the one with the maximum degree of
success (unless one succeeds totally) - leading quickly to the possibility
of combinatorial explosion. Moreover, Prolog programs typically assume
that only the first clause that succeeds in satisfying a goal is tried -
at least until the goal is redone during backtracking, and cut (!)
can be used to prevent trying alternatives if a goal succeeds.
To deal with these problem, PrologJ handles the evaluation of a predicate
for which there are multiple fuzzy clauses as follows: each clause is tried,
in turn, until one succeeds with truth value at least equal to the
threshold. If the clause contains redoable subgoals, each way of proving
the clause may be tried until one such solution is found. Cut (!)
can be used to cut off alternatives if the truth value at the point the cut is
executed is at least equal to the threshold, even though a later term may push
the truth value below the threshold and force failure (in the same way that
!, ..., fail can be used to force a crisp computation to fail.)
As soon as any clause succeeds with truth value at least equal to the
threshold, the goal is declared to be satisfied at once. Subsequent
clauses may be tried during backtracking - but again, only until a clause
that succeeds with truth value at least equal to the threshold is found.
When one of the control structure predicates listed earlier is called in a fuzzy context, its truth value is based on the truth value of its subgoal(s) according to the following rules.
call(A) - the truth value is the truth value of
Acatch(A, B, C) - the truth value is the truth
value of whichever of A or C is called.A, B - the truth value is the minimum
of the truth values for A and B.A; BA succeeds (or re-succeeds on redo) with truth value at
least equal to the threshold of the parent goal, the parent goal succeeds
with truth value equal to that for A. Only if A
does not (re)succeed with value at least equal to the threshold of the
parent goal is B tried; and if it succeeds, the parent goal
succeeds with truth value equal to that for B.
fuzzy_best/1 described below.foreach(A, B) - The truth value is the
minimum of the truth values of the various calls to B -
1.0 if B is never called because A never succeeds.
A -> B - The truth value is the minimum
of the truth values for A and B.A -> B; CA and B succeed, the truth value is the
minimum of the two truth values.A succeeds partially (but with truth value less than the
threshold for the parent goal), or it fails, and C succeeds,
then the truth value is the minimum of (1.0 - truth value for
A [ 1.0 - 0.0 = 1.0 if A failed ] ), truth value for
C). \+ A or not AA succeeds partially (but with truth value less than the
threshold for the parent goal), or it fails, the truth value is
(1.0 - truth value for A [ 1.0 - 0.0 = 1.0 if A
failed ] ).
once(A) - the truth value is the truth value of
A
phrase(A, B) - the truth value is the truth
value of the grammar rule corresponding to A
Fuzzy clauses can be accessed or deleted in the same way as crisp clauses,
using the built-in predicates clause/2,
retract/1 and retractall/1. When these predicates
are used in the standard way, the inherent truth value of the clause is ignored,
as is any threshold if they are called in the context of a fuzzy
computation. (That is, a fuzzy clause is handled in exactly the same way as a
crisp clause by default.)
However, it is possible to take into account the inherent truth value of a fuzzy clause when one of these built-in predicate is used, by following the head of the clause with one of the following. (This only affects clause selection; it has no affect on the outcome of the overall goal, which is always crisp).
:) followed by an uninstantiated variable. In this
case, the variable is instantiated to the inherent truth value of the
selected clause. (Note: this is not allowed with retractall/1,
since multiple clauses are typically selected, and no unification with
variables in the clause is visible.)::) followed by a real number that serves as a
threshold for selecting clauses, in the range (0.0 .. 1.0]. In this case,
a fuzzy clause is selected only if it has an inherent truth value at least
equal to the threshold. (Crisp clauses are always selected.)retractall/1.)
Note that, when entering one of these goals to the interpreter, the truth
value and/or threshold specifier(s) appear inside the goal, not
outside it. Example: to access clauses for a tall fact in such
a way as to find out the inherent truth value of each, and to only consider
clauses with inherent truth value at least 0.5, one would write:
clause(tall(Person) : V :: 0.5, B).
not
clause(tall(Person), B) : V :: 0.5.
(The latter would find all clauses, and would report 1.0 as the value of
V in every case, since it is considering the truth of the
proposition "there exists a clause ...")
When listing/1 or listing/2 lists a fuzzy clause,
its inherent truth value is printed as part of the listing in the same way it
would be specified when inputting the clause.
Several built-in predicates are added to facilitate fuzzy computations. In the discussions that follow, the following sample database will be used:
tall(joe) : 0.5. tall(ralph) : 0.3. tall(john) : 0.8. tall(george) : 0.4.
call_fuzzy(Goal)Goal must be a callable goal, and (as with any goal
called in a fuzzy context) can be followed by a return truth value
variable and/or a threshold specifier. (If no threshold is specified,
the default value 0.0 is used.)Goal succeeds either partially or absolutely, the
overall goal succeeds absolutely.call/1.
call/1.
Person being
instantiated to joe the first time and john the second. Each success
will be absolute. It will not succeed for tall(ralph) or
george, because the truth value of the relevant facts is less
than the threshold specified.
call_fuzzy(tall(Person) :: 0.5).
fuzzy_best(Goal)Goal must be a callable goal, and (as with any goal
called in a fuzzy context) can be followed by a return truth value
variable and/or a threshold specifier. (If no threshold is specified,
the default value 0.0 is used.)Goal are explored.
If at least one solution is found with truth value greater than or equal
to the threshold, then the overall goal succeeds with variables bound in
accordance with the best solution found, and with truth value equal to
the truth value for that solution. (If, in the course of exploring
alternatives, a solution is found that succeeds absolutely (with truth
value 1.0), the overall goal succeeds absolutely at once, without
exploring further alternatives, since no other outcome could possibly
have a higher truth value.) If no solution can be found with truth
value at least equal to the threshold, the goal fails.
call/1.
call/1
Person
instantiated to john and V instantiated to 0.8:
fuzzy_best(tall(Person)) : V.
fuzzy_findall(Term, Goal, Bag),fuzzy_findset(Term, Goal, Set),fuzzy_bagof(Template, BGoal, Bag),fuzzy_setof(Template, BGoal, Set)Goal parameter for fuzzy_findall
or fuzzy_findset, or the subgoal portion of it for
fuzzy_bagof or fuzzy_setof) must
be a callable goal, and (as with any goal
called in a fuzzy context) can be followed by a return truth value
variable and/or a threshold specifier. (If no threshold is specified,
the default value 0.0 is used. In this case, specifying a variable
to receive the return truth value generally serves no purpose, but
is legal.)template : truth value where
truth value is the truth value of the goal success that resulted
in template. (In the case of fuzzy_findset/3 or
fuzzy_setof/3, if the same template value occurs two or more
times, the truth value of the single copy that is reported will be the
maximum of the different truth values.)
L to the list shown.
fuzzy_findall(P, tall(P) :: 0.5, L). % L will be instantiated to [ ralph : 0.5, john : 0.8 ]
fuzzy_truth(TruthValue)TruthValue is a number in the range (0.0 .. 1.0]. If the
value is at least equal to the threshold for the goal, the predicate
succeeds once, with truth value equal to the value of its argument.
It cannot be redone. If TruthValue is less than the
threshold for the goal, then the goal fails.| Conditions | Error Term |
|---|---|
TruthValue is a variable |
instantiation_error |
TruthValue is not a variable or a number |
type_error(number, TruthValue) |
TruthValue is less than or equal to 0.0
or greater than 1.0 |
domain_error(fuzzy_truth, TruthValue) |
','/2
since the inherent truth value for tall(ralph) is 0.3).
tall(Person), fuzzy_truth(0.4).
In conjunction with the set processing extension,
PrologJ incorporates support for working with fuzzy sets. A fuzzy set is
represented by a list, whose elements are of the form :/2,
where the first argument is a set element and the second argument is a real
number in the range (0.0 .. 1.0] representing its degree of membership in
the set. (Note that 0.0 is not allowed - since such an element would be
definitely not a part of the set, and therefore would not be listed. However,
1.0 is allowed - representing an element that is absolutely part of the set.)
A fuzzy set may also include an element that is not of the form
:/2, which is taken to represent an element with degree of
membership 1.0 (and it is converted to a form that explicitly specifies
this in the resultant set).
The standard set operations (set_difference/3, etc) should not
be used with fuzzy sets, since they treat the truth value specifier as part
of the set element. Instead, the following should be used. All of these
allow the first and second arguments to be either a bag or a set; however, if
the first argument is a bag it is first converted to a set in which each
element's degree of membership is the largest degree of membership associated
with that element in the bag. The result is always a fuzzy set, with each
element having its degree of membership specified explicitly (even if 1.0).
Although these operations are intended for use with fuzzy sets, they can also be used with crisp sets. In this case, each member of a crisp set is considered to have degree of membership 1.0 in that set. The result is always a fuzzy set, with each element having its degree of membership specified explicitly (even if 1.0).
All of these predicates are considered crisp, in the sense that they succeed or fail solely on the basis of whether the set they compute unifies with the final argument. If they are called in a fuzzy context, the threshold of their context is ignored.
This documentation here describes the way these predicates differ from their crisp counterparts. This documentation should therefore be read in conjunction with the documentation for the built-in set-processing-extension predicates.
fuzzy_difference/3fuzzy_filter_set/4, fuzzy_filter_set/5
:/2, to
unify with both a set element and its degree of membership. (A degree
of membership 1.0 is supplied if one is not present in the element.)
If the template does not have this form, it is unified only with
the set element.fuzzy_filter_set/4, the degree of membership for
any element in the result set is the minimum of its
degree of membership in the original set and the truth value of
the test predicate. For fuzzy_filter_set/5, the
degree of membership is explicitly specified by the fourth
parameter.fuzzy_filter_set/4, the result list parameter is
the fourth parameter; for fuzzy_filter_set/5, the result
list parameter is the fifth parameter
S to include only elements with degree of
membership 0.8 or greater, placing the result in result set
R
fuzzy_filter_set(S, _ : V, V >= 0.8, R).
S so that the degree of membership of
each element is the square of its original degree of membership,
placing the result in result set R
fuzzy_filter_set(S, _ : V, Square is V * V, Square, R).
fuzzy_intersection/3fuzzy_union/3