Information

Difficulty: moderate — 5 definitions, 2 abbreviations, 2 lemmas, 8 theorems.

definition abbreviation lemma theorem
legend

Some situations are easy to describe because they follow a pattern: “every day for a month” takes only a few words, even though it stands for a thirty or so different days. Other situations are not easy to describe: thirty completely unrelated dates, with no pattern connecting them, can only be pinned down by writing out every single one. There is no shortcut. The first kind of situation is what we call orderly (in order, in formation), the second is what we would call chaotic (out of order). A lot of data (the Latin plural) not always gives a lot of information.

Here we model information quantitatively: how much you have to write down, in the worst case, to pin one particular situation down out of everything it could have been. The smallest you can pin down discretely this way is a single yes-or-no question: did something happen or not? Heads or tails? On or off? Dead or alive? One bit: the unit of information.

A sequence of bits then allows you to describe more situations, by asking several yes-or-no questions one after another: the more bits there are, the more different situations you can discriminate, as the number of possible discriminated situations doubles with every extra bit in length (exponential growth).

However, information is a real measure, ‖x‖ : ℝ, the same kind of quantity as a length or a distance (the notation is borrowed on purpose: it’s the same ‖·‖ used elsewhere for “the size of something”). Say you need to tell something apart from three equally likely possibilities instead of two. Say: “which of these three doors is someone standing behind?”. One bit only tells two doors apart, not enough; two bits tell four doors apart, more than enough, with room left over. Three doors need something in between one and two bits — and the exact amount needed is not a whole number, and not a fraction either: it’s an irrational number, one that never settles into a neat ratio no matter how far you write it out. Real information doesn’t come in whole units at all.

Let’s prove that mathematically.

No positive whole-number power of 2 ever equals a whole-number power of 3: a power of 2 (beyond 2 ^ 0 = 1) is always even, while a power of 3 is always odd.

theorem no_pow_two_eq_pow_three (p q : Nat) (hq : 0 < q) : 2 ^ p  3 ^ q := by

Dependencies: (none)

Mathlib dependencies: one_pow, pow_succ, pow_zero

The exact number of bits needed to tell three equally likely possibilities apart — Real.logb 2 3, the real number x with 2 ^ x = 3 — is irrational: it can’t be written as any ratio of whole numbers.

theorem irrational_logb_two_three : Irrational (Real.logb 2 3) := by

Dependencies: (none)

Proof dependencies: no_pow_two_eq_pow_three

Mathlib dependencies: Int.cast_natCast, Int.rawCast, Irrational, Mathlib.Meta.NormNum.IsInt.of_raw, Mathlib.Meta.NormNum.IsInt.to_isNat, Mathlib.Meta.NormNum.IsInt.to_raw_eq, Mathlib.Meta.NormNum.IsNat.of_raw, Mathlib.Meta.NormNum.IsNat.to_isInt, Mathlib.Meta.NormNum.IsNat.to_raw_eq, Mathlib.Meta.NormNum.instAddMonoidWithOne, Mathlib.Meta.NormNum.instAtLeastTwo, Mathlib.Meta.NormNum.isInt_add, Mathlib.Meta.NormNum.isInt_neg, Mathlib.Meta.NormNum.isNat_eq_false, Mathlib.Meta.NormNum.isNat_le_true, Mathlib.Meta.NormNum.isNat_lt_true, Mathlib.Meta.NormNum.isNat_mul, Mathlib.Meta.NormNum.isNat_ofNat, Mathlib.Tactic.Linarith.eq_of_not_lt_of_not_gt, Mathlib.Tactic.Linarith.lt_irrefl, Mathlib.Tactic.Linarith.lt_of_eq_of_lt, Mathlib.Tactic.Linarith.sub_neg_of_lt, Mathlib.Tactic.Ring.Common.add_congr, Mathlib.Tactic.Ring.Common.add_mul, Mathlib.Tactic.Ring.Common.add_overlap_pf_zero, Mathlib.Tactic.Ring.Common.add_pf_add_gt, Mathlib.Tactic.Ring.Common.add_pf_add_lt, Mathlib.Tactic.Ring.Common.add_pf_add_overlap_zero, Mathlib.Tactic.Ring.Common.add_pf_add_zero, Mathlib.Tactic.Ring.Common.add_pf_zero_add, Mathlib.Tactic.Ring.Common.atom_pf, Mathlib.Tactic.Ring.Common.mul_add, Mathlib.Tactic.Ring.Common.mul_congr, Mathlib.Tactic.Ring.Common.mul_pf_left, Mathlib.Tactic.Ring.Common.mul_pf_right, Mathlib.Tactic.Ring.Common.mul_zero, Mathlib.Tactic.Ring.Common.neg_add, Mathlib.Tactic.Ring.Common.neg_congr, Mathlib.Tactic.Ring.Common.neg_mul, Mathlib.Tactic.Ring.Common.neg_zero, Mathlib.Tactic.Ring.Common.sub_congr, Mathlib.Tactic.Ring.Common.sub_pf, Mathlib.Tactic.Ring.Common.zero_mul, Mathlib.Tactic.Ring.cast_zero, Mathlib.Tactic.Ring.of_eq, Nat.cast_one, Nat.cast_zero, Nat.rawCast, Rat.cast_intCast, Rat.cast_natCast, Rat.num_div_den, Rat.num_pos, Rat.pos, Real, Real.logb, Real.rpow_logb, Real.rpow_lt_rpow_left_iff, Real.rpow_mul, Real.rpow_natCast, Real.rpow_zero, Ring, Set, Set.range, div_eq_iff, neg_eq_zero, sub_eq_zero_of_eq

Consider a few examples of the real thing: how much information is genuinely present in a couple of everyday situations.

Example.

The three colors a traffic light shows, one at a time.

inductive TrafficLightColor
  | green
  | amber
  | red
deriving DecidableEq, Fintype

Outer dependencies: (none)

Lean core dependencies: Eq, Nat, Nat.ble, PULift, cond, noConfusionEnum, noConfusionTypeEnum

Example.

A traffic light has exactly three colors to tell apart — the same three possibilities as the doors above, so it takes exactly the same irrational amount to tell them apart: Real.logb 2 3 bits, not a whole number no matter how you round it.

theorem traffic_light_information :
    Fintype.card TrafficLightColor = 3  Irrational (Real.logb 2 3) :=

Dependencies: TrafficLightColor

Proof dependencies: irrational_logb_two_three

Mathlib dependencies: Fintype.card, Irrational, Real, Real.logb

Lean core dependencies: And, Bool, Decidable.decide, Eq, Nat, id, of_decide_eq_true

Used by: (none)

Example.

An international phone number, in the longest form actually used: fifteen digits, the most the ITU’s E.164 standard ever allows.

abbrev PhoneNumber := Fin 15  Fin 10

Outer dependencies: (none)

Lean core dependencies: Fin, Nat

Example.

A phone number is a very different kind of situation from a traffic light: there are 10^15 of them, vastly more than three — but, unlike three doors or three colors, this many possibilities happens to fit comfortably between two whole numbers of bits: fifty bits are enough to tell every possible phone number apart, and forty-nine are not.

theorem phone_number_information :
    Fintype.card PhoneNumber = 10 ^ 15  ¬ (10 ^ 15  2 ^ 49)  10 ^ 15  2 ^ 50 := by

Dependencies: PhoneNumber

Lean core dependencies: And, Bool, Eq, Eq.mpr, Eq.trans, Fin, Nat, Nat.pow, Not, True, congr, congrArg, congrFun', eq_self, id, of_eq_true

Used by: (none)

Discreteness

Most of what this course actually builds only ever needs whole numbers of bits, though — a genuinely useful simplifying assumption, not a law of nature. Discrete below names exactly that assumption: a type is discrete when its measure always happens to land on a whole number, for every value. It’s a claim of existence, not a recipe: knowing a type is discrete doesn’t, by itself, hand you a way to compute the whole number back out (only that one exists) — this matters for something like a circuit built from real-valued parts (a transistor’s voltage, continuous) that composes into a component whose overall behaviour is discrete (a logic gate’s 0/1), without that discreteness being something you could read off any single part.

A type’s information is discrete when its real-valued measure always happens to land on a whole number. This is an assumption worth naming, not something every type gets automatically: a continuous quantity (a voltage, a physical decay) generally isn’t. Deliberately a bare existence claim — it doesn’t hand back which whole number, since nothing here says that has to be computable (see Discrete.size).

def Discrete (α : Type) [Norm α] : Prop :=  x : α,  n : Nat, x = n

Outer dependencies: (none)

Mathlib dependencies: Norm, Real

Lean core dependencies: Eq, Exists, Nat, Nat.cast

The whole number a discrete value’s measure happens to equal. Noncomputable: Discrete only promises this number exists, not a way to produce it.

noncomputable def Discrete.size {α : Type} [Norm α] (h : Discrete α) (x : α) : Nat :=
  (h x).choose

Outer dependencies: Discrete

Mathlib dependencies: Norm, Real

Lean core dependencies: Eq, Exists.choose, Nat, Nat.cast

The defining property of Discrete.size: it really does equal the real measure.

theorem Discrete.size_eq {α : Type} [Norm α] (h : Discrete α) (x : α) :
    (h.size x : ) = x :=

Dependencies: Discrete, Discrete.size

Mathlib dependencies: Norm, Real

Lean core dependencies: Eq, Eq.symm, Exists.choose, Exists.choose_spec, Nat, Nat.cast

Technical — you can skip this.

Real information is never negative — a genuine, motivated assumption about how this measure behaves, not something that follows automatically from Norm alone (which places no constraint on its values at all). Every measure this course actually builds satisfies it. Typeclass plumbing (not something to stop and study) — the interesting content is in the theorems it enables further down the course, not in the class itself.

class NormNonneg (α : Type) [Norm α] : Prop where
  nonneg :  x : α, 0  x

Outer dependencies: (none)

Mathlib dependencies: Norm, Real

A payload bit: the same two values as Bool, but standing for the record of an actual observation — something measured at some place, at some time, rather than reasoned about in the abstract. Bool, by contrast, carries no information of its own: as a value, true/false is a pure concept you can state and reason about a priori — a control signal, a choice, not something you had to go and observe. That’s why Bit genuinely carries one unit of information and a bare Bool carries none.

def Bit := Bool
  deriving DecidableEq, Fintype, Nonempty

Outer dependencies: (none)

Lean core dependencies: Bool

Every bit carries exactly one unit of information.

instance : Norm Bit where
  norm _ := 1

Outer dependencies: Bit

Mathlib dependencies: Norm, Real

Technical — you can skip this.

instance : NormNonneg Bit := fun _ => by change (0:)  1; norm_num

Outer dependencies: Bit, NormNonneg, instNormBit

Lean core dependencies: Bool, id

Used by: (none)

Bit’s measure is always exactly 1, a whole number.

theorem discrete_Bit : Discrete Bit := fun _ => 1, by change (1:) = ((1:Nat):); norm_num

Dependencies: Bit, Discrete, instNormBit

Lean core dependencies: Eq, Nat, Nat.cast, id

Used by: (none)

Flip a bit.

def Bit.flip (b : Bit) : Bit := !(b : Bool)

Outer dependencies: Bit

Lean core dependencies: Bool.not

Used by: (none)

The number of possible n-bit sequences doubles with every extra bit: 2 choices for the first bit, 2 more for the next, and so on — exponential growth in the length of the sequence.

theorem card_BitSequence (n : Nat) : Fintype.card (BitSequence n) = 2 ^ n := by

Dependencies: Bit, BitSequence

Lean core dependencies: Eq, Eq.mpr, Eq.trans, Fin, Nat, True, congrArg, congrFun', eq_self, id, of_eq_true

Example.

Telling three possibilities apart genuinely needs two bits, not one: one bit only ever distinguishes two things (2 < 3), while two bits comfortably cover three, with a spare combination left over (3 ≤ 4). There’s no whole number of bits that fits three possibilities exactly — see the module’s own introduction for what the exact amount actually is.

theorem three_doors_information :
    ¬ (3  Fintype.card (BitSequence 1))  3  Fintype.card (BitSequence 2) := by

Dependencies: Bit, BitSequence

Proof dependencies: card_BitSequence

Mathlib dependencies: Fintype.card

Lean core dependencies: And, Bool, Decidable.decide, Eq, Eq.mpr, Fin, Nat, Not, congrArg, id, of_decide_eq_true

Used by: (none)

Technical — you can skip this.

A vector of n values (one per wire) has measure equal to the sum of the measures of its components: fanning a value out over more wires does not, by itself, create or destroy any information. This instance is typeclass plumbing (needed wherever a bundle of n wires is itself used as a State/Input/Output), not a new concept.

instance [Norm α] {n : Nat} : Norm (Fin n  α) where
  norm f :=  i, f i

Outer dependencies: (none)

Mathlib dependencies: Finset.sum, Finset.univ, Norm, Real

Lean core dependencies: Fin, Nat

Technical — you can skip this.

instance [Norm α] [NormNonneg α] {n : Nat} : NormNonneg (Fin n  α) :=

Mathlib dependencies: Finset, Finset.sum_nonneg, Finset.univ, Norm, Real

Lean core dependencies: Fin, Nat

Used by: (none)

A vector of discrete values is itself discrete: a whole number of wires, each carrying a whole number of bits, always adds up to a whole number overall. This is the general shape of “many real-valued parts composing into a discrete whole” — see the module’s own introduction.

theorem Discrete.pi {α : Type} [Norm α] (h : Discrete α) {n : Nat} : Discrete (Fin n  α) :=

Proof dependencies: Discrete.size, Discrete.size_eq

Lean core dependencies: Eq, Eq.mpr, Eq.symm, Fin, Nat, Nat.cast, congrArg, id, rfl

Used by: (none)

Dependency diagram

Drag to pan, Ctrl+scroll (or Cmd+scroll) to zoom, click a node to jump to it.

definitionabbreviationlemmatheoremexampledeclared elsewheredependencyproof dependency
legend