I am trying to define some binary operators. A binary operator operates on a set of variables V and is defined by a subset S of pairs of those variables {Vi,Vj}. For example, "=",">",">=" are all binary operators that are already implemented in Mathematica. I would like to use Tilde[ ] as the "set" operator, analogous to "=" and TildeTilde[ ] as the test operator, analogous to "==". I will use a~~b here to represent TildeTilde[a,b]. I would like to be able to specify the operator as having any combination of three properties: Reflexive (x~~x is True for all x), symmetric (if x~~y then y~~x for all x,y) and transitive (if x~~y and y~~z then x~~z for all x,y,z). Perhaps I would specify other properties, and test various theorems on binary operators. The following code implements this in a very klugy way, but it works. I wonder if there is an elegant way of doing this? I looked at Can I define an axiomatic (Boolean algebra) system and prove theorems using Mathematica? and maybe I am asking too much.
The following code does what I want, but I am interested in a more elegant solution.
Tilde[a_, b_] := Module[{Change, SS},
If[Length[S] == 0, S = {{a, b}}, S = Append[S, {a, b}]];
If[Length[V] == 0, V = {a, b}, V = Union[Flatten[{V, a, b}]]];
SS = S;
Change = True;
While[Change, {
Change = False;
If[reflexive, SS = MakeReflexive[S]];
If[S != SS, Change = True];
S = SS;
If[symmetric, SS = MakeSymmetric[S]];
If[S != SS, Change = True];
S = SS;
If[transitive, SS = MakeTransitive[S]];
If[S != SS, Change = True];
S = SS;
}];
]
TildeTilde[a_, b_] := MemberQ[S, {a, b}]
MakeReflexive[S_] := Module[{n, nV, i, SS}, (* {a,a} True *)
SS = S;
n = Length[SS];
If[n > 0, {
nV = Length[V];
For[i = 1, i <= nV, i++, SS = Append[SS, {V[[i]], V[[i]]}]];
SS = Union[SS]; (* Remove duplicates *)
}];
SS
]
MakeSymmetric[S_] := Module[{n, i, SS}, (* {a,b} in S => {b,a} in S *)
SS = S;
n = Length[SS];
If[n > 0, {
For[i = 1, i <= n, i++, SS = Append[SS, {S[[i, 2]], S[[i, 1]]}]];
SS = Union[SS]; (* Remove duplicates *)
}];
SS
]
MakeTransitive[S_] := Module[{SS, n, i, j}, (* {a,c} in S and {c,b} in S => {a,b} in S *)
SS = S;
n = Length[SS];
If[n > 0, {
For[i = 1, i <= n, i++, {For[j = 1, j <= n, j++,
If[S[[i, 2]] == S[[j, 1]], SS = Append[SS, {S[[i, 1]], S[[j, 2]]}]];
]}];
SS = Union[SS]; (* Remove duplicates *)
}];
SS
]
STable[S_] := Module[{n, i, j, table}, (* Generate truth table for binary relation *)
n = Length[V];
table = Array["?" &, {n, n}];
For[i = 1, i <= n, i++, {For[j = 1, j <= n, j++, {
If[V[[i]] \[TildeTilde] V[[j]], table[[i, j]] = 1, table[[i, j]] = 0, table[[i,j]] = "?"];
}]}];
MatrixForm[table]
]
Now I can observe an equivalence truth table with:
ClearAll[a, b, c, d, e, f, x, y, S, V, reflexive, symmetric, transitive]
reflexive = True;
symmetric = True;
transitive = True;
V = {x, y}; (* include variables that might not be related to any other variable *)
a \[Tilde] b;
a \[Tilde] c;
d \[Tilde] e;
f \[Tilde] f;
Print["V=", V];
Print["S=", S];
STable[S]
make["transitive"]
code but I wanted to get some ink on the page. I imagine it can be substantially optimized. $\endgroup$make["transitive"]
would need to be performed last; sorry. The mechanism is pick a row then for every position at that row in which a1
occurs select that row fromm
. Total all selected rows (meaning add values down each column) and add this vector to the original row. Do this for every row inm
, then repeat the processi
times to make sure any changes are propagated all the way acrossm
. That last step is surely inefficient but I just wanted to get it done. $\endgroup$