(* Depth lower bounds (Section 5): no threshold unit, whatever its real weights, reads a memory word or computes a one-hot disjunction of conjunctions (Lemma 5.7), and no ternary unit computes the branch predicate on three or more bits, which one unit with integer weights computes (Remark 5.5). *) From Stdlib Require Import Reals Lra ZArith Lia List Bool. Import ListNotations. (* ---- units with real weights ------------------------------------------------------------------------------ *) (* the pre-activation without bias of the unit with weights w on the inputs x_i, i in l *) Fixpoint rsum (w : nat -> R) (x : nat -> bool) (l : list nat) : R := match l with [] => 0%R | i :: t => ((if x i then w i else 0) + rsum w x t)%R end. Definition rfire (N : nat) (w : nat -> R) (b : R) (x : nat -> bool) : bool := if Rle_dec 0 (rsum w x (seq 0 N) + b) then true else false. (* two inputs whose ones, counted with multiplicity, are those of two others have the same summed sums *) Lemma rsum_swap : forall w x1 x2 x3 x4 l, (forall i, (Nat.b2n (x1 i) + Nat.b2n (x2 i) = Nat.b2n (x3 i) + Nat.b2n (x4 i))%nat) -> (rsum w x1 l + rsum w x2 l = rsum w x3 l + rsum w x4 l)%R. Proof. intros w x1 x2 x3 x4 l H. induction l as [|i t IH]; cbn [rsum]; [lra|]. specialize (H i). destruct (x1 i), (x2 i), (x3 i), (x4 i); cbn [Nat.b2n] in H; try lia; lra. Qed. Lemma rfire_swap : forall N w b x1 x2 x3 x4, (forall i, (Nat.b2n (x1 i) + Nat.b2n (x2 i) = Nat.b2n (x3 i) + Nat.b2n (x4 i))%nat) -> rfire N w b x1 = true -> rfire N w b x2 = true -> rfire N w b x3 = false -> rfire N w b x4 = false -> False. Proof. intros N w b x1 x2 x3 x4 H. unfold rfire. pose proof (rsum_swap w x1 x2 x3 x4 (seq 0 N) H). destruct (Rle_dec 0 (rsum w x1 (seq 0 N) + b)), (Rle_dec 0 (rsum w x2 (seq 0 N) + b)), (Rle_dec 0 (rsum w x3 (seq 0 N) + b)), (Rle_dec 0 (rsum w x4 (seq 0 N) + b)); intros; try discriminate; lra. Qed. (* ---- Lemma 5.7 --------------------------------------------------------------------------------------------- *) (* the address alpha in the inputs 0 .. A - 1, least significant first, the words mu in the inputs A .. *) Fixpoint bval (x : nat -> bool) (k : nat) : nat := match k with O => 0 | S k' => bval x k' + Nat.b2n (x k') * 2 ^ k' end. Definition select (A : nat) (x : nat -> bool) : bool := x (A + bval x A)%nat. Lemma bval_low : forall x k, (forall i, (1 <= i)%nat -> x i = false) -> bval x k = match k with O => O | S _ => Nat.b2n (x 0%nat) end. Proof. intros x k H. induction k as [|k IH]; [reflexivity|]. cbn [bval]. rewrite IH. destruct k as [|k]; [cbn; lia|]. rewrite (H (S k)) by lia. cbn [Nat.b2n]. lia. Qed. Theorem read_needs_two_levels : forall A w b, (1 <= A)%nat -> ~ (forall x, rfire (A + 2 ^ A) w b x = select A x). Proof. intros A w b HA H. set (pt := fun (a m0 m1 : bool) i => (a && Nat.eqb i 0) || (m0 && Nat.eqb i A) || (m1 && Nat.eqb i (S A))). assert (Hlow : forall a m0 m1 i, (1 <= i)%nat -> (i < A)%nat -> pt a m0 m1 i = false). { intros a m0 m1 i H1 H2. unfold pt. destruct (Nat.eqb_spec i 0), (Nat.eqb_spec i A), (Nat.eqb_spec i (S A)); try lia; rewrite !andb_false_r; reflexivity. } assert (Hsel : forall a m0 m1, select A (pt a m0 m1) = if a then m1 else m0). { intros a m0 m1. unfold select. assert (E : bval (pt a m0 m1) A = Nat.b2n a). { assert (G : forall k, (k <= A)%nat -> bval (pt a m0 m1) k = match k with O => O | S _ => Nat.b2n a end). { induction k as [|k IH]; intros Hk; [reflexivity|]. cbn [bval]. rewrite IH by lia. destruct k as [|k]. - unfold pt. rewrite Nat.eqb_refl. destruct (Nat.eqb_spec 0 A), (Nat.eqb_spec 0 (S A)); try lia. rewrite !andb_false_r, !orb_false_r, andb_true_r. destruct a; reflexivity. - rewrite (Hlow a m0 m1 (S k)) by lia. cbn [Nat.b2n]. lia. } rewrite (G A (le_n _)). destruct A; [lia | reflexivity]. } rewrite E. unfold pt. destruct a; cbn [Nat.b2n]. - replace (A + 1)%nat with (S A) by lia. rewrite Nat.eqb_refl. destruct (Nat.eqb_spec (S A) 0), (Nat.eqb_spec (S A) A); try lia. rewrite !andb_false_r, andb_true_r. reflexivity. - rewrite Nat.add_0_r, Nat.eqb_refl. destruct (Nat.eqb_spec A 0), (Nat.eqb_spec A (S A)); try lia. rewrite !andb_false_r, andb_true_r, orb_false_r. reflexivity. } apply (rfire_swap (A + 2 ^ A) w b (pt false true false) (pt true false true) (pt false false true) (pt true true false)). - intro i. unfold pt. destruct (Nat.eqb_spec i 0), (Nat.eqb_spec i A), (Nat.eqb_spec i (S A)); cbn [andb orb Nat.b2n]; lia. - rewrite H, Hsel. reflexivity. - rewrite H, Hsel. reflexivity. - rewrite H, Hsel. reflexivity. - rewrite H, Hsel. reflexivity. Qed. (* the one-hot form: the lines s in the inputs 0 .. n - 1, the bits mu in the inputs n .. 2n - 1 *) Definition onehot (n : nat) (x : nat -> bool) : Prop := exists j, (j < n)%nat /\ forall i, (i < n)%nat -> x i = Nat.eqb i j. Definition orsel (n : nat) (x : nat -> bool) : bool := existsb (fun j => x j && x (n + j)%nat) (seq 0 n). Theorem onehot_needs_two_levels : forall n w b, (2 <= n)%nat -> ~ (forall x, onehot n x -> rfire (n + n) w b x = orsel n x). Proof. intros n w b Hn H. set (pt := fun (s m : nat) i => Nat.eqb i s || Nat.eqb i (n + m)%nat). assert (Hoh : forall s m, (s < n)%nat -> onehot n (pt s m)). { intros s m Hs. exists s. split; [exact Hs|]. intros i Hi. unfold pt. destruct (Nat.eqb_spec i (n + m)); [lia|]. apply orb_false_r. } assert (Hval : forall s m, (s < 2)%nat -> (m < 2)%nat -> orsel n (pt s m) = Nat.eqb s m). { intros s m Hs Hm. unfold orsel, pt. destruct (Nat.eqb_spec s m) as [<-|E]. - apply existsb_exists. exists s. split; [apply in_seq; lia|]. rewrite !Nat.eqb_refl, orb_true_l, orb_true_r. reflexivity. - apply not_true_is_false. intro Hx. apply existsb_exists in Hx as (j & Hj & Hx). apply in_seq in Hj. apply andb_true_iff in Hx as [H1 H2]. apply orb_true_iff in H1, H2. destruct H1 as [H1|H1], H2 as [H2|H2]; apply Nat.eqb_eq in H1, H2; lia. } apply (rfire_swap (n + n) w b (pt 0 0) (pt 1 1) (pt 0 1) (pt 1 0))%nat. - intro i. unfold pt. destruct (Nat.eqb_spec i 0), (Nat.eqb_spec i 1), (Nat.eqb_spec i (n + 0)), (Nat.eqb_spec i (n + 1)); cbn; lia. - rewrite H by (apply Hoh; lia). rewrite Hval by lia. reflexivity. - rewrite H by (apply Hoh; lia). rewrite Hval by lia. reflexivity. - rewrite H by (apply Hoh; lia). rewrite Hval by lia. reflexivity. - rewrite H by (apply Hoh; lia). rewrite Hval by lia. reflexivity. Qed. (* ---- Remark 5.5 ------------------------------------------------------------------------------------------- *) Open Scope Z_scope. Fixpoint zsum (w : nat -> Z) (x : nat -> bool) (l : list nat) : Z := match l with [] => 0 | i :: t => (if x i then w i else 0) + zsum w x t end. Definition zfire (n : nat) (w : nat -> Z) (b : Z) (x : nat -> bool) : bool := Z.leb 0 (zsum w x (seq 0 n) + b). (* the value of r_0 .. r_(n-1) in two's complement, and the branch predicate [value <= 0] *) Fixpoint uval (r : nat -> bool) (k : nat) : Z := match k with O => 0 | S k' => uval r k' + Z.b2z (r k') * 2 ^ Z.of_nat k' end. Definition tval (n : nat) (r : nat -> bool) : Z := uval r (n - 1) - Z.b2z (r (n - 1)%nat) * 2 ^ Z.of_nat (n - 1). Definition branch (n : nat) (r : nat -> bool) : bool := Z.leb (tval n r) 0. (* with integer weights one unit computes it *) Lemma branch_one_unit : forall n r, branch n r = Z.leb 0 (Z.b2z (r (n - 1)%nat) * 2 ^ Z.of_nat (n - 1) - uval r (n - 1)). Proof. intros n r. unfold branch, tval. apply eq_true_iff_eq. rewrite !Z.leb_le. lia. Qed. Lemma zsum_ext : forall w x x' l, (forall i, In i l -> x i = x' i) -> zsum w x l = zsum w x' l. Proof. intros w x x' l H. induction l as [|i t IH]; cbn [zsum]; [reflexivity|]. rewrite (H i (or_introl eq_refl)), IH by (intros j Hj; apply H; right; exact Hj). reflexivity. Qed. Lemma zsum_zero : forall w l, zsum w (fun _ => false) l = 0. Proof. intros w l. induction l as [|i t IH]; cbn [zsum]; lia. Qed. Lemma zsum_one : forall w k l, NoDup l -> In k l -> zsum w (fun i => Nat.eqb i k) l = w k. Proof. intros w k l Hnd. induction Hnd as [|i t Hi Hnd IH]; intros Hk; [destruct Hk|]. cbn [zsum]. destruct (Nat.eqb_spec i k) as [->|E]. - rewrite (zsum_ext w _ (fun _ => false)), zsum_zero; [lia|]. intros j Hj. destruct (Nat.eqb_spec j k); [subst; contradiction | reflexivity]. - destruct Hk as [Hk|Hk]; [contradiction|]. rewrite IH by exact Hk. lia. Qed. Lemma zsum_all : forall w l, zsum w (fun _ => true) l = fold_right Z.add 0 (map w l). Proof. intros w l. induction l as [|i t IH]; cbn [zsum map fold_right]; lia. Qed. Lemma uval_ext : forall r r' k, (forall i, (i < k)%nat -> r i = r' i) -> uval r k = uval r' k. Proof. intros r r' k H. induction k as [|k IH]; cbn [uval]; [reflexivity|]. rewrite IH by (intros i Hi; apply H; lia). rewrite (H k) by lia. reflexivity. Qed. Lemma uval_one : forall j k, (j < k)%nat -> uval (fun i => Nat.eqb i j) k = 2 ^ Z.of_nat j. Proof. intros j k Hjk. induction k as [|k IH]; [lia|]. cbn [uval]. destruct (Nat.eqb_spec k j) as [->|E]. - rewrite (uval_ext _ (fun _ => false)). + assert (Z0 : forall m, uval (fun _ => false) m = 0) by (induction m as [|m IHm]; cbn [uval]; [reflexivity | rewrite IHm; reflexivity]). rewrite Z0. cbn [Z.b2z]. lia. + intros i Hi. destruct (Nat.eqb_spec i j); [lia | reflexivity]. - rewrite IH by lia. cbn [Z.b2z]. lia. Qed. Lemma uval_all : forall k, uval (fun _ => true) k = 2 ^ Z.of_nat k - 1. Proof. induction k as [|k IH]; cbn [uval]; [reflexivity|]. rewrite IH. cbn [Z.b2z]. rewrite Nat2Z.inj_succ, Z.pow_succ_r by lia. lia. Qed. Lemma sum_ternary_lower : forall (w : nat -> Z) (l : list nat), (forall i, In i l -> w i = -1) -> fold_right Z.add 0 (map w l) = - Z.of_nat (length l). Proof. intros w l H. induction l as [|i t IH]; cbn [map fold_right length]; [reflexivity|]. rewrite (H i (or_introl eq_refl)), IH by (intros j Hj; apply H; right; exact Hj). lia. Qed. Theorem branch_not_ternary : forall n w b, (3 <= n)%nat -> (forall i, -1 <= w i <= 1) -> ~ (forall r, zfire n w b r = branch n r). Proof. intros n w b Hn Hw H. (* r = 0 has value 0, so b >= 0 *) assert (Hb : 0 <= b). { specialize (H (fun _ => false)). unfold zfire, branch, tval in H. rewrite zsum_zero in H. assert (Z0 : forall m, uval (fun _ => false) m = 0) by (induction m as [|m IHm]; cbn [uval]; [reflexivity | rewrite IHm; reflexivity]). rewrite Z0 in H. cbn [Z.b2z] in H. replace (0 - 0 * 2 ^ Z.of_nat (n - 1)) with 0 in H by lia. cbn in H. apply Z.leb_le. rewrite H. reflexivity. } (* each unit vector e_i, i < n - 1, has value 2^i > 0, so w_i = -1 and b = 0 *) assert (He : forall i, (i < n - 1)%nat -> w i + b < 0). { intros i Hi. specialize (H (fun k => Nat.eqb k i)). unfold zfire, branch, tval in H. rewrite zsum_one in H by (apply seq_NoDup || (apply in_seq; lia)). rewrite uval_one in H by exact Hi. destruct (Nat.eqb_spec (n - 1) i) as [E|E]; [lia|]. cbn [Z.b2z] in H. assert (P : 0 < 2 ^ Z.of_nat i) by (apply Z.pow_pos_nonneg; lia). replace (2 ^ Z.of_nat i - 0 * 2 ^ Z.of_nat (n - 1) <=? 0) with false in H by (symmetry; apply Z.leb_gt; lia). apply Z.leb_gt. exact H. } assert (Hw1 : forall i, (i < n - 1)%nat -> w i = -1) by (intros i Hi; specialize (He i Hi); specialize (Hw i); lia). assert (Hb0 : b = 0) by (specialize (He 0%nat ltac:(lia)); rewrite Hw1 in He by lia; lia). (* the vector of ones has value -1, so w_(n-1) >= n - 1 >= 2 *) specialize (H (fun _ => true)). unfold zfire, branch, tval in H. rewrite zsum_all, uval_all in H. cbn [Z.b2z] in H. replace (seq 0 n) with (seq 0 (n - 1) ++ [(n - 1)%nat]) in H by (replace n with (S (n - 1)) at 3 by lia; rewrite seq_S; reflexivity). rewrite map_app, fold_right_app in H. cbn [map fold_right] in H. assert (Hs : forall (l : list Z) c, fold_right Z.add c l = fold_right Z.add 0 l + c). { intros l c. induction l as [|y t IH]; cbn [fold_right]; lia. } rewrite Hs, sum_ternary_lower, length_seq in H by (intros i Hi; apply in_seq in Hi; apply Hw1; lia). replace (2 ^ Z.of_nat (n - 1) - 1 - 1 * 2 ^ Z.of_nat (n - 1) <=? 0) with true in H by (symmetry; apply Z.leb_le; lia). apply Z.leb_le in H. specialize (Hw (n - 1)%nat). lia. Qed. (* ---- Lemma 5.4 --------------------------------------------------------------------------------------------- *) (* z(r) = H(-(r_0 + ... + r_(n-1))) *) Definition zunit (n : nat) (r : nat -> bool) : bool := zfire n (fun _ => -1) 0 r. Lemma zsum_neg_le : forall r l, zsum (fun _ => -1) r l <= 0. Proof. intros r l. induction l as [|i t IH]; cbn [zsum]; [lia|]. destruct (r i); lia. Qed. Lemma zsum_neg : forall r l, Z.leb 0 (zsum (fun _ => -1) r l) = forallb (fun i => negb (r i)) l. Proof. intros r l. induction l as [|i t IH]; cbn [zsum forallb]; [reflexivity|]. pose proof (zsum_neg_le r t). destruct (r i); cbn [negb andb]. - apply Z.leb_gt. lia. - rewrite Z.add_0_l. exact IH. Qed. Lemma uval_bound : forall r k, 0 <= uval r k < 2 ^ Z.of_nat k. Proof. intros r k. induction k as [|k IH]; cbn [uval]; [cbn; lia|]. rewrite Nat2Z.inj_succ, Z.pow_succ_r by lia. destruct (r k); cbn [Z.b2z]; lia. Qed. Lemma uval_zero : forall r k, Z.eqb (uval r k) 0 = forallb (fun i => negb (r i)) (seq 0 k). Proof. intros r k. induction k as [|k IH]; [reflexivity|]. cbn [uval]. rewrite seq_S, forallb_app, <- IH. cbn [forallb Nat.add]. pose proof (uval_bound r k) as B. assert (P : 0 < 2 ^ Z.of_nat k) by (apply Z.pow_pos_nonneg; lia). destruct (r k); cbn [Z.b2z negb andb]. - rewrite andb_false_r. apply Z.eqb_neq. lia. - rewrite andb_true_r, Z.mul_0_l, Z.add_0_r. reflexivity. Qed. (* z(r) is 1 exactly when r = 0, and [r <= 0] = H(r_(n-1) + z(r) - 1): the branch takes two ternary units *) Theorem branch_unit : forall n r, (1 <= n)%nat -> zunit n r = Z.eqb (tval n r) 0 /\ branch n r = Z.leb 0 (Z.b2z (r (n - 1)%nat) + Z.b2z (zunit n r) - 1). Proof. intros n r Hn. assert (Hz : zunit n r = Z.eqb (uval r (n - 1)) 0 && negb (r (n - 1)%nat)). { unfold zunit, zfire. rewrite Z.add_0_r, zsum_neg. replace (seq 0 n) with (seq 0 (n - 1) ++ [(n - 1)%nat]) by (replace n with (S (n - 1)) at 3 by lia; rewrite seq_S; reflexivity). rewrite forallb_app, <- uval_zero. cbn [forallb]. rewrite andb_true_r. reflexivity. } unfold branch, tval. rewrite Hz. pose proof (uval_bound r (n - 1)) as B. assert (P : 0 < 2 ^ Z.of_nat (n - 1)) by (apply Z.pow_pos_nonneg; lia). set (u := uval r (n - 1)) in *. destruct (r (n - 1)%nat); cbn [negb]; rewrite ?andb_false_r, ?andb_true_r; cbn [Z.b2z]. - split; [symmetry; apply Z.eqb_neq; lia|]. rewrite (proj2 (Z.leb_le (u - 1 * 2 ^ Z.of_nat (n - 1)) 0)) by lia. reflexivity. - replace (u - 0 * 2 ^ Z.of_nat (n - 1)) with u by lia. split; [reflexivity|]. destruct (Z.eqb_spec u 0) as [E|E]; cbn [Z.b2z]; [rewrite E; reflexivity|]. rewrite (proj2 (Z.leb_gt u 0)) by lia. reflexivity. Qed.