Here is one idea. Convert the set expression into an equivalent boolean expression, use BooleanMinimize
to simplify the boolean expression, and then convert back to a set expression.
Set expression
Rather than using Union
and Intersection
, I will use the built-in symbols SquareUnion
and SquareIntersection
so that I don't have to modify Union
and Intersection
to work with atomic symbols. So, here are the symbols I will be allowing for set expressions:
I added the symbols EmptySet
and UniversalSet
, similar to how Reals
, Integers
, etc. are handled. I also need to add formatting for these symbols:
MakeBoxes[EmptySet, StandardForm] ^= TemplateBox[
{},
"EmptySet",
DisplayFunction -> ("\[EmptySet]"&),
InterpretationFunction -> ("EmptySet"&)
];
MakeBoxes[UniversalSet, StandardForm] ^= TemplateBox[
{},
"UniversalSet",
DisplayFunction -> (StyleBox["\[DoubleStruckCapitalU]", FontFamily->"Times"]&),
InterpretationFunction -> ("UniversalSet"&)
];
It is convenient to add some input aliases:
CurrentValue[EvaluationNotebook[], InputAliases] = {
"su" -> "⊔",
"si" -> "⊓",
"sb" -> "⊏",
"es" -> TemplateBox[
{},
"EmptySet",
DisplayFunction -> ("\[EmptySet]"&),
InterpretationFunction -> ("EmptySet"&)
],
"us" -> TemplateBox[
{},
"UniversalSet",
DisplayFunction -> (StyleBox["\[DoubleStruckCapitalU]", FontFamily->"Times"]&),
InterpretationFunction -> ("UniversalSet"&)
]
};
Conversion to Boolean expression
This part is pretty simple. The Boolean equivalents are:
- union: $A \sqcup B \Longleftrightarrow $
Or[A, B]
- intersection: $A \sqcap B \Longleftrightarrow$
And[A, B]
- subset: $A \sqsubset B \Longleftrightarrow $
Implies[A, B]
- set difference: $A\backslash B \Longleftrightarrow $
And[A, Not[B]]
- set complement: $\bar{A} \Longleftrightarrow $
Not[A]
- set equivalence: $A = B \Longleftrightarrow $
Equivalent[A, B]
- empty set: $\emptyset \Longleftrightarrow $
False
- universal set: $\mathbb{U} \Longleftrightarrow $
True
So, the following function will convert our set expressions into equivalent Boolean expressions:
toBoolean[expr_] := ReplaceAll[
expr,
{
SquareUnion -> Or,
SquareIntersection -> And,
SquareSubset -> Implies,
Backslash -> (And[#1, !#2]&),
OverBar -> Not,
Equal -> Equivalent,
EmptySet -> False,
UniversalSet -> True
}
]
Boolean minimization
The function I will use to "simplify" boolean expressions is BooleanMinimize
. For a nontrivial example, consider the first set expression in the OP:
set = A ⊓ B ⊔ B;
The equivalent Boolean expression is:
toBoolean[set]
(A && B) || B
Using BooleanMinimize
on this expression:
BooleanMinimize[toBoolean[set]]
B
as expected.
BooleanMinimize
accepts a 2-arg version where the second argument is a condition. We can use this for the second set expression in the OP:
set = A ⊓ B;
cond = A ⊏ B;
Using BooleanMinimize
:
BooleanMinimize[toBoolean[set], toBoolean[cond]]
A
as expected.
Conversion back to set expression
Conversions back to a set expression is basically just the reverse of the conversions from a set expression, except that in some cases, the set expression actually represents a predicate. This means that sometimes False and True should be converted to EmptySet
and UniversalSet
, and sometimes (when the set expression represents a predicate) it should be left alone. Also, it would be convenient to convert Boolean expressions representing subset and set difference expressions to the standard set expressions for them.
First, I will define a setQ
expression that determines whether a set expression represents a set or a predicate:
setQ[EmptySet] = True;
setQ[UniversalSet] = True;
setQ[_Symbol] = True;
setQ[(Backslash | SquareUnion | SquareIntersection | OverBar)[a__]] := AllTrue[{a}, setQ]
setQ[_] = False;
Examples:
setQ[A ⊔ B]
setQ[A ⊏ B]
setQ[A == B]
True
False
False
Next, I will define a fromBoolean
function:
fromBoolean[expr_] := ReplaceAll[
expr,
{
Or -> SquareUnion,
And -> SquareIntersection,
Not -> OverBar,
Equivalent -> Equal
}
]
fromBoolean[a_ && !b_Symbol] := Backslash[a, b]
fromBoolean[!a_Symbol && b_] := Backslash[b, a]
fromBoolean[a_ || !b_Symbol] := SquareSubset[b, a]
fromBoolean[!a_Symbol || b_] := SquareSubset[a, b]
SetSimplify
Now, we are ready to create a SetSimplify
function for symbolic sets:
Options[SetSimplify] = {Method -> Automatic};
SetSimplify[set_, cond_:True, OptionsPattern[]] := Module[{res},
res = fromBoolean @ BooleanMinimize[
toBoolean[set],
toBoolean[cond],
Method->OptionValue[Method]
];
If[setQ[set],
res /. {False -> EmptySet, True -> UniversalSet},
res
]
]
The OP examples:
SetSimplify[(A ⊓ B) ⊔ B]
SetSimplify[A ⊓ B == A, A ⊏ B]
SetSimplify[A ⊔ B == B, A ⊏ B]
B
True
True
A few other examples taken from Wikipedia:
An identity law
set = A ⊓ UniversalSet;
set //TeXForm
$A\sqcap \mathbb{U}$
SetSimplify[set]
A
A complement law
set = A ⊔ OverBar[A];
set //TeXForm
$A\sqcup \bar{A}$
SetSimplify[set] //TeXForm
$\mathbb{U}$
Idempotent laws
SetSimplify[A ⊔ A]
SetSimplify[A ⊓ A]
A
A
Absorption laws
SetSimplify[A ⊔ (A ⊓ B)]
SetSimplify[A ⊓ (A ⊔ B)]
A
A
*One of De Morgan's laws:
law = OverBar[A ⊔ B] == OverBar[A] ⊓ OverBar[B];
law //TeXForm
$\overline{A\sqcup B}=\bar{A}\sqcap \bar{B}$
SetSimplify[law]
True
A complement law
set = OverBar[EmptySet];
set //TeXForm
$\overline{\emptyset }$
SetSimplify[set] //TeXForm
$\mathbb{U}$
Reflexivity
SetSimplify[A ⊏ A]
True
Antisymmetry
SetSimplify[A == B, A ⊏ B && B ⊏ A]
True
Transitivity
SetSimplify[A ⊏ C, A ⊏ B && B ⊏ C]
True
Joins
SetSimplify[A ⊏ A ⊔ B]
SetSimplify[A ⊔ B ⊏ C, A ⊏ C && B ⊏ C]
True
True
Meets
SetSimplify[A ⊓ B ⊏ A]
SetSimplify[C ⊏ A ⊓ B, C ⊏ A && C ⊏ B]
True
True
And a few others:
SetSimplify[A ⊓ B == A, A ⊔ B == B]
True
SetSimplify[A ∖ B, A ⊏ B] //TeXForm
$\emptyset$
SetSimplify[A ∖ A] //TeXForm
$\emptyset$
set = UniversalSet ∖ A;
set //TeXForm
$\mathbb{U}\backslash A$
SetSimplify[set] //TeXForm
$\bar{A}$
Finally, here is everything in one code block:
MakeBoxes[EmptySet, form_] ^= TemplateBox[
{},
"EmptySet",
DisplayFunction -> ("\[EmptySet]"&),
InterpretationFunction -> ("EmptySet"&)
];
MakeBoxes[UniversalSet, form_] ^= TemplateBox[
{},
"UniversalSet",
DisplayFunction -> (StyleBox["\[DoubleStruckCapitalU]", FontFamily->"Times"]&),
InterpretationFunction -> ("UniversalSet"&)
];
CurrentValue[EvaluationNotebook[], InputAliases] = {
"su" -> "⊔",
"si" -> "⊓",
"sb" -> "⊏",
"es" -> TemplateBox[
{},
"EmptySet",
DisplayFunction -> ("\[EmptySet]"&),
InterpretationFunction -> ("EmptySet"&)
],
"us" -> TemplateBox[
{},
"UniversalSet",
DisplayFunction -> (StyleBox["\[DoubleStruckCapitalU]", FontFamily->"Times"]&),
InterpretationFunction -> ("UniversalSet"&)
]
};
toBoolean[expr_] := ReplaceAll[
expr,
{
SquareUnion -> Or,
SquareIntersection -> And,
SquareSubset -> Implies,
OverBar -> Not,
Backslash -> (And[#1, !#2]&),
Equal -> Equivalent,
EmptySet -> False,
UniversalSet -> True
}
]
setQ[EmptySet] = True;
setQ[UniversalSet] = True;
setQ[_Symbol] = True;
setQ[(Backslash | SquareUnion | SquareIntersection | OverBar)[a__]] := AllTrue[{a}, setQ]
setQ[_] = False;
fromBoolean[expr_] := ReplaceAll[
expr,
{
Or -> SquareUnion,
And -> SquareIntersection,
Not -> OverBar,
Equivalent -> Equal
}
]
fromBoolean[a_ && !b_Symbol] := Backslash[a, b]
fromBoolean[!a_Symbol && b_] := Backslash[b, a]
fromBoolean[a_ || !b_Symbol] := SquareSubset[b, a]
fromBoolean[!a_Symbol || b_] := SquareSubset[a, b]
Options[SetSimplify] = {Method -> Automatic};
SetSimplify[set_, cond_:True, OptionsPattern[]] := Module[{res},
res = fromBoolean @ BooleanMinimize[
toBoolean[set],
toBoolean[cond],
Method->OptionValue[Method]
];
If[setQ[set],
res /. {False -> EmptySet, True -> UniversalSet},
res
]
]