IV.2 · Probability
How much mass belongs to either event?
Toggle membership in two events and follow union, intersection, and complement. Overlap is counted once, and the underlying outcome weights stay the same.
Events carry probability mass
Outlined outcomes belong to A ∪ B.
Sample space: 1, 2, 3, 4, 5, 6. Equal weights explicitly assume equiprobable outcomes. A = {2, 4, 6}; B = {4, 5, 6}. Union includes either event; intersection includes both; complement includes outcomes outside A. Every mass uses the same weights.
- Total probability
- 1
- Event A
- 0.5
- Event B
- 0.5
- Intersection
- 1/30.333333…
- Union
- 2/30.666667…
- Complement of A
- 0.5
= exact model quantity · ≈ numerical companion or observed estimate
from 0 to 100, whole numbers only
Start with total mass one
Equal weights explicitly assume equiprobable outcomes.
Start with the fair die’s and . Predict whether adding the two half-masses gives the union’s mass. Locate the two faces that would be counted twice.
Intuition: mass lives on outcomes
Events do not own separate copies of probability. They select from a single space whose weights sum to one. An outcome that belongs to both events still has one weight. This is why overlapping regions cannot simply be added when the question is “A or B.”
The geometry of overlap is set membership. Probability measures its mass. Two regions with the same number of labels can have different probabilities when their weights differ.
Definition
For events , the union contains outcomes in at least one event; the intersection contains outcomes in both. The complement contains outcomes outside but inside the chosen sample space. Events are disjoint when .
For every event, , using the same nonnegative, normalized weights. Therefore , , and .
Mechanics: follow membership and weight separately
The stage uses outcome membership to show the two events and their overlap. Toggling A or B changes a set, not its probability weight. Edit weight selects a nonnegative raw weight to change; all raw weights are divided by their positive total to obtain one normalized mass model. Read the labels along with the shading: colour alone should not be your evidence that a face belongs to both events.
Inspect the original event masses and the combined sets. Replay the trace after moving a face from A-only to both: its intersection contribution appears, but its union contribution was already present. The inspector, equation, and accessible interpretation describe the same memberships and weights.
Derivation: inclusion-exclusion
Separate the union into three disjoint pieces: , , and . Adding includes the middle piece twice. Subtract it once:
The sample space also splits into the disjoint pieces and , so
These identities require neither equal outcome weights nor independence. For disjoint events the intersection is empty, and the subtraction term is zero. For , the union is simply , not twice its mass.
Worked examples
On the fair die, . The intersection has mass . The union has mass , agreeing with . The complement has mass .
Worked example: Events carry probability mass
Outlined outcomes belong to A ∪ B.
Sample space: 1, 2, 3, 4, 5, 6. Equal weights explicitly assume equiprobable outcomes. A = {2, 4, 6}; B = {4, 5, 6}. Union includes either event; intersection includes both; complement includes outcomes outside A. Every mass uses the same weights.
- Total probability
- 1
- Event A
- 0.5
- Event B
- 0.5
- Intersection
- 1/30.333333…
- Union
- 2/30.666667…
- Complement of A
- 0.5
= exact model quantity · ≈ numerical companion or observed estimate
from 0 to 100, whole numbers only
Start with total mass one
Equal weights explicitly assume equiprobable outcomes.
For a weighted space with masses , choose and . The marginal masses are and , intersection mass is , and union mass is . Inclusion-exclusion still gives .
Executable reference code
The Python program validates the model, performs actual set operations, and sums exact fractions over each result. Assertions check the literal die answers and weighted identities. NumPy represents membership with boolean masks: OR for union, AND for intersection, and NOT for complement. Its printed decimal sums approximate the exact reference fractions.
"""Set operations select outcomes; one normalized mass map weights every set."""
from fractions import Fraction as F
def event_mass(weights, event):
if not weights or any(not isinstance(p, F) or p < 0 for p in weights.values()):
raise ValueError("Use a nonempty model with nonnegative Fraction masses")
if sum(weights.values(), F(0)) != 1:
raise ValueError("Masses must sum exactly to one")
if not set(event) <= weights.keys():
raise ValueError("Event contains an outcome outside the sample space")
return sum((weights[x] for x in set(event)), F(0))
def describe_events(weights, a, b):
a, b = set(a), set(b)
# Validate even events whose out-of-space labels disappear in an intersection.
pa, pb = event_mass(weights, a), event_mass(weights, b)
joint = event_mass(weights, a & b)
union = event_mass(weights, a | b)
complement = event_mass(weights, set(weights) - a)
assert union == pa + pb - joint
assert complement == 1 - pa
return {"P(A)": pa, "P(B)": pb, "P(A∩B)": joint,
"P(A∪B)": union, "P(Aᶜ)": complement, "disjoint": not (a & b)}
def main():
die = {face: F(1, 6) for face in range(1, 7)}
a, b = {2, 4, 6}, {4, 5, 6}
result = describe_events(die, a, b)
assert result == {"P(A)": F(1, 2), "P(B)": F(1, 2), "P(A∩B)": F(1, 3),
"P(A∪B)": F(2, 3), "P(Aᶜ)": F(1, 2), "disjoint": False}
assert describe_events(die, a, a)["P(A∪B)"] == F(1, 2)
assert describe_events(die, a, set(die) - a)["P(A∪B)"] == 1
assert describe_events(die, set(), b)["P(A∩B)"] == 0
weighted = {"x": F(1, 2), "y": F(1, 3), "z": F(1, 6)}
weighted_result = describe_events(weighted, {"x", "y"}, {"y", "z"})
assert weighted_result["P(A∩B)"] == F(1, 3)
assert weighted_result["P(A∪B)"] == 1
zero_weight = {"possible": F(1), "zero": F(0)}
overlap = describe_events(zero_weight, {"zero"}, {"zero"})
assert overlap["P(A∩B)"] == 0 and not overlap["disjoint"]
for weights, event in ((die, {7}), ({}, set()),
({1: F(-1), 2: F(2)}, {1}), ({1: F(1, 2)}, {1})):
try:
describe_events(weights, event, set())
except ValueError:
pass
else:
raise AssertionError("Invalid model or event was accepted")
print("Fair die, A = {2, 4, 6}, B = {4, 5, 6}:", result)
print("Weighted x, y, z example:", weighted_result)
print("Shared zero-weight label: joint mass 0, but sets are not disjoint")
if __name__ == "__main__":
main()"""OR, AND, and NOT implement set membership; weights supply event mass.
Membership is boolean and exact. Floating-point sums are compared to literal
Fraction references at absolute tolerance 1e-12, with zero relative tolerance.
"""
from fractions import Fraction as F
import numpy as np
ATOL = 1e-12
def event_summary(weights, a, b):
p = np.asarray(weights, dtype=float)
a, b = np.asarray(a), np.asarray(b)
if p.ndim != 1 or p.size == 0 or np.any(p < 0) or not np.all(np.isfinite(p)):
raise ValueError("Use a nonempty finite nonnegative mass vector")
if not np.isclose(p.sum(), 1.0, rtol=0, atol=ATOL):
raise ValueError("Masses must sum to one within floating-point tolerance")
if a.dtype != np.bool_ or b.dtype != np.bool_ or a.shape != p.shape or b.shape != p.shape:
raise ValueError("Use boolean event masks with one entry per outcome")
pa, pb = p[a].sum(), p[b].sum()
joint, union, complement = p[a & b].sum(), p[a | b].sum(), p[~a].sum()
np.testing.assert_allclose(union, pa + pb - joint, rtol=0, atol=ATOL)
np.testing.assert_allclose(complement, 1 - pa, rtol=0, atol=ATOL)
return np.array([pa, pb, joint, union, complement]), not np.any(a & b)
def main():
faces = np.arange(1, 7)
p = np.full(6, 1 / 6)
a, b = np.isin(faces, [2, 4, 6]), np.isin(faces, [4, 5, 6])
masses, disjoint = event_summary(p, a, b)
expected = [F(1, 2), F(1, 2), F(1, 3), F(2, 3), F(1, 2)]
np.testing.assert_allclose(masses, [float(x) for x in expected], rtol=0, atol=ATOL)
assert not disjoint
assert event_summary(p, a, ~a)[1]
np.testing.assert_allclose(event_summary(p, a, a)[0][3], float(F(1, 2)), rtol=0, atol=ATOL)
weighted, _ = event_summary([0.5, 1 / 3, 1 / 6], [True, True, False], [False, True, True])
np.testing.assert_allclose(weighted, [float(x) for x in (F(5, 6), F(1, 2), F(1, 3), F(1), F(1, 6))], rtol=0, atol=ATOL)
zero_overlap, zero_disjoint = event_summary([1.0, 0.0], [False, True], [False, True])
assert zero_overlap[2] == 0 and not zero_disjoint
for bad_p, bad_a, bad_b in (([-0.5, 1.5], [True, False], [False, True]),
([0.5, 0.4], [True, False], [False, True]),
([np.nan], [True], [True]),
([1.0], [1], [True]), ([1.0], [True, False], [True])):
try:
event_summary(bad_p, bad_a, bad_b)
except ValueError:
pass
else:
raise AssertionError("Invalid masses or masks were accepted")
print("Die masses ≈", masses, "in order A, B, intersection, union, complement")
print("Exact references:", expected)
print("Weighted example masses ≈", weighted)
print("Zero overlap mass does not guarantee disjoint sets")
if __name__ == "__main__":
main()Explore
Make B equal to A. Predict that intersection and union both become A. Then choose B to be the complement of A: the intersection becomes empty and the union becomes the whole space. Finally empty A while keeping B fixed; explain why the union remains B and the intersection has mass zero.
For every change, check inclusion-exclusion against direct union summation. A trace that adds weights over selected outcomes provides a second view of the same mathematical identity, rather than evidence from random trials.
Common mistakes and counterexamples
“Or” in union is inclusive: an outcome in both counts. Exclusive OR would omit the intersection and describe a different event. A complement is relative to the declared space; the complement of even die faces does not include the integer .
Equal probabilities do not imply equal sets. The even faces and the top three faces each have mass but differ. Disjointness also does not mean independence: odd and even are mutually exclusive and each has positive mass, so knowing one happened rules out the other.
Numerical considerations
Rational summation preserves exact union and complement identities for these finite examples. Floating-point code may accumulate tiny summation errors. The NumPy reference checks its small arrays against exact fractions with absolute tolerance and zero relative tolerance; it does not silently clip a negative weight or renormalize an invalid vector.
Boolean set membership itself is exact. Keep that separate from approximate mass arithmetic. A tiny rounded joint mass should not decide whether two sets are disjoint, and a displayed decimal such as is not the exact fraction .
Engineering and ML connections
Boolean masks select rows, category groups, and evaluation subsets. A union of error categories requires overlap correction or an OR mask; adding category counts can double-count the same examples. Weighted data requires summing weights over the mask. The same structure appears when grouping predicted categorical probabilities.
Exercises
Summary and checkpoints
Events select outcomes from one weighted space. Union counts each selected outcome once; intersection requires both memberships; complement fills the rest of the space. Check that you can derive inclusion-exclusion, apply it to unequal weights, and distinguish set disjointness from zero mass and independence.