unit LukasiwiczLogic;
{
Thaddy de Koning, (c) 2021-2025. Creative commons V3.}
}
{$mode objfpc}{$H+}
{$R-}{$Q-}// No range or overflow checks for performance
interface
uses
sysutils;
type
{ Łukasiewicz truth values in [0,1] }
TLukasiewiczValue = single;
{ Basic Łukasiewicz operations }
function LukNot(const A: TLukasiewiczValue): TLukasiewiczValue; inline;
function LukAnd(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
function LukOr(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
{Note: default conform theory, but two common alternatives as comments below }
function LukXor(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
function LukImplies(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
function LukEquiv(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
{ Operators AND/OR/NOT/XOR }
operator not(const A: TLukasiewiczValue): TLukasiewiczValue; inline;
operator and(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
operator or(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
{Note: default conform theory, but two common alternatives as comments below }
operator xor(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
{ Additional Łukasiewicz operations }
function LukStrongConjunction(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
function LukStrongDisjunction(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
function LukDelta(const A: TLukasiewiczValue): TLukasiewiczValue; inline; { Delta operator }
function LukNabla(const A: TLukasiewiczValue): TLukasiewiczValue; inline; { Nabla operator }
{ Łukasiewicz T-norm and T-conorm }
function LukTNorm(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
function LukTConorm(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
{ Threshold functions }
function LukIsTrue(const A: TLukasiewiczValue; const Threshold: TLukasiewiczValue = 1.0): Boolean; inline;
function LukIsFalse(const A: TLukasiewiczValue; const Threshold: TLukasiewiczValue = 0.0): Boolean; inline;
{ Aggregation operators }
function LukWeightedAverage(const Values: array of TLukasiewiczValue;
const Weights: array of TLukasiewiczValue): TLukasiewiczValue;
function LukGeneralizedAnd(const Values: array of TLukasiewiczValue): TLukasiewiczValue;
function LukGeneralizedOr(const Values: array of TLukasiewiczValue): TLukasiewiczValue;
implementation
uses
Math;
{ Łukasiewicz Negation: ¬A = 1 - A }
function LukNot(const A: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := 1.0 - A;
end;
{ Łukasiewicz Conjunction: A ⊗ B = max(0, A + B - 1) }
function LukAnd(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := Max(0.0, A + B - 1.0);
end;
{ Łukasiewicz Disjunction: A ⊕ B = min(1, A + B) }
function LukOr(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := Min(1.0, A + B);
end;
{ Łukasiewicz Implication: A → B = min(1, 1 - A + B) }
function LukImplies(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := Min(1.0, 1.0 - A + B);
end;
{ Łukasiewicz Equivalence: A ↔ B = 1 - |A - B| }
function LukEquiv(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := 1.0 - Abs(A - B);
end;
{ Łukasiewicz XOR - Three common definitions }
function LukXor(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
{ Definition 1: A ⊕ B = max(min(A, 1-B), min(1-A, B))
This is Łukasiewicz interpretation of exclusive or }
Result := Max(Min(A, 1.0 - B), Min(1.0 - A, B));
{ Alternative definitions (comment out the above and uncomment one below): }
{ Definition 2: A ⊕ B = |A - B| }
// Result := Abs(A - B);
{ Definition 3: A ⊕ B = min(max(A, B), max(1-A, 1-B)) - Standard fuzzy XOR }
// Result := Min(Max(A, B), Max(1.0 - A, 1.0 - B));
end;
{ Operators }
operator not(const A: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := 1.0 - A;
end;
operator and(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := Max(0.0, A + B - 1.0);
end;
operator or(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := Min(1.0, A + B);
end;
operator xor(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
{ Using Definition 1 for the operator }
Result := Max(Min(A, 1.0 - B), Min(1.0 - A, B));
end;
{ Strong Conjunction: A ⊙ B = max(0, A + B - 1) (same as ∧ in Łukasiewicz) }
function LukStrongConjunction(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := Max(0.0, A + B - 1.0);
end;
{ Strong Disjunction: A ⊞ B = min(1, A + B) (same as ∨ in Łukasiewicz) }
function LukStrongDisjunction(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := Min(1.0, A + B);
end;
{ Delta operator: ΔA = 1 if A = 1, 0 otherwise }
function LukDelta(const A: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
if A = 1.0 then
Result := 1.0
else
Result := 0.0;
end;
{ Nabla operator: ∇A = 1 if A > 0, 0 otherwise }
function LukNabla(const A: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
{ Use Sign function for faster check }
if Sign(A) > 0 then
Result := 1.0
else
Result := 0.0;
end;
{ T-norm: Łukasiewicz T-norm (same as conjunction) }
function LukTNorm(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := Max(0.0, A + B - 1.0);
end;
{ T-conorm: Łukasiewicz T-conorm (same as disjunction) }
function LukTConorm(const A, B: TLukasiewiczValue): TLukasiewiczValue; inline;
begin
Result := Min(1.0, A + B);
end;
{ Check if value is considered true (>= threshold) }
function LukIsTrue(const A: TLukasiewiczValue; const Threshold: TLukasiewiczValue = 1.0): Boolean; inline;
begin
Result := A >= Threshold;
end;
{ Check if value is considered false (<= threshold) }
function LukIsFalse(const A: TLukasiewiczValue; const Threshold: TLukasiewiczValue = 0.0): Boolean; inline;
begin
Result := A <= Threshold;
end;
{ Weighted average aggregation }
function LukWeightedAverage(const Values: array of TLukasiewiczValue;
const Weights: array of TLukasiewiczValue): TLukasiewiczValue;
var
i: Integer;
sumValues, sumWeights: TLukasiewiczValue;
begin
if Length(Values) = 0 then
Exit(0.5); { Default middle value }
if Length(Weights) <> Length(Values) then
raise Exception.Create('Number of weights must match number of values');
sumValues := 0.0;
sumWeights := 0.0;
for i := 0 to High(Values) do
begin
sumValues := sumValues + Values[i] * Weights[i];
sumWeights := sumWeights + Weights[i];
end;
if sumWeights = 0 then
Result := 0.5
else
Result := sumValues / sumWeights;
end;
{ Generalized conjunction (minimum for all values) }
function LukGeneralizedAnd(const Values: array of TLukasiewiczValue): TLukasiewiczValue;
var
i: Integer;
begin
if Length(Values) = 0 then
Exit(1.0); { Empty conjunction is true }
Result := Values[0];
for i := 1 to High(Values) do
Result := Max(0.0, Result + Values[i] - 1.0);
end;
{ Generalized disjunction (maximum for all values) }
function LukGeneralizedOr(const Values: array of TLukasiewiczValue): TLukasiewiczValue;
var
i: Integer;
begin
if Length(Values) = 0 then
Exit(0.0); { Empty disjunction is false }
Result := Values[0];
for i := 1 to High(Values) do
Result := Min(1.0, Result + Values[i]);
end;
end.