@@ -298,6 +298,202 @@ theorem deadzone_odd (x dz : Rat) (hdz : 0 ≤ dz) :
298298 rw [deadzone_in_dz x dz hin, deadzone_in_dz (-x) dz (by rwa [Rat.abs_neg]),
299299 Rat.neg_zero]
300300
301+ /-! ## Monotonicity -/
302+
303+ -- Private helpers used only by `deadzone_mono_val`
304+ private theorem dz_le_abs (v : Rat) : v ≤ v.abs := by
305+ by_cases h : 0 ≤ v
306+ · rw [Rat.abs_of_nonneg h]; exact @Rat.le_refl v
307+ · exact Rat.le_trans (Rat.le_of_lt (Rat.not_le.mp h)) Rat.abs_nonneg
308+
309+ private theorem dz_neg_abs_le (v : Rat) : -v.abs ≤ v := by
310+ by_cases h : 0 ≤ v
311+ · rw [Rat.abs_of_nonneg h]; exact Rat.le_trans (Rat.neg_le_iff.mpr h) h
312+ · have hv : v < 0 := Rat.not_le.mp h
313+ rw [Rat.abs_of_nonpos (Rat.le_of_lt hv), Rat.neg_neg]; exact @Rat.le_refl v
314+
315+ private theorem dz_abs_le_left (v dz : Rat) (h : v.abs ≤ dz) : -dz ≤ v :=
316+ Rat.le_trans (Rat.neg_le_neg h) (dz_neg_abs_le v)
317+
318+ private theorem dz_one_sub_pos (dz : Rat) (h : dz < 1 ) : (0 : Rat) < 1 - dz := by
319+ have := Rat.add_lt_add_right (a := dz) (b := 1 ) (c := -dz) |>.mpr h
320+ rwa [Rat.add_neg_cancel, ← Rat.sub_eq_add_neg] at this
321+
322+ private theorem dz_div_le (a b c : Rat) (h : a ≤ b) (hc : 0 < c) : a / c ≤ b / c := by
323+ rw [Rat.div_def, Rat.div_def]
324+ exact Rat.mul_le_mul_of_nonneg_right h (Rat.le_of_lt (Rat.inv_pos.mpr hc))
325+
326+ private theorem dz_sub_le (a b k : Rat) (h : a ≤ b) : a - k ≤ b - k := by
327+ rw [Rat.sub_eq_add_neg, Rat.sub_eq_add_neg]; exact (Rat.add_le_add_right (c := -k)).mpr h
328+
329+ private theorem dz_abs_out_neg (x dz : Rat) (hxneg : x < 0 ) (hxabs : x.abs > dz) : x < -dz := by
330+ rw [Rat.abs_of_nonpos (Rat.le_of_lt hxneg)] at hxabs
331+ have h := Rat.neg_lt_neg hxabs; rwa [Rat.neg_neg] at h
332+
333+ /-- **Monotonicity in the input value** : `deadzone` is non-decreasing in `v`.
334+
335+ If `v₁ ≤ v₂` then `deadzone v₁ dz ≤ deadzone v₂ dz`.
336+
337+ This is a key correctness property for RC stick processing: a larger stick
338+ deflection always produces a larger (or equal) output, preserving the
339+ direction of input changes across the deadzone boundary. -/
340+ theorem deadzone_mono_val (v1 v2 dz : Rat) (hv : v1 ≤ v2) (hdz0 : 0 ≤ dz) (hdz1 : dz < 1 ) :
341+ deadzone v1 dz ≤ deadzone v2 dz := by
342+ have h1mdz : (0 : Rat) < 1 - dz := dz_one_sub_pos dz hdz1
343+ by_cases h1 : v1.abs > dz
344+ · -- v1 outside deadzone
345+ by_cases h2 : v2.abs > dz
346+ · -- Both outside: monotone on each branch, or neg→pos gives neg<0<pos
347+ by_cases hv1pos : (0 : Rat) ≤ v1
348+ · -- Both positive (v1 ≤ v2, v1 ≥ 0 → v2 ≥ 0)
349+ have hv2pos : (0 : Rat) ≤ v2 := Rat.le_trans hv1pos hv
350+ rw [deadzone_pos_eq v1 dz (by rwa [Rat.abs_of_nonneg hv1pos] at h1) hdz0,
351+ deadzone_pos_eq v2 dz (by rwa [Rat.abs_of_nonneg hv2pos] at h2) hdz0]
352+ exact dz_div_le _ _ _ (dz_sub_le v1 v2 dz hv) h1mdz
353+ · -- v1 negative
354+ have hv1neg : v1 < 0 := Rat.not_le.mp hv1pos
355+ have hv1dz : v1 < -dz := dz_abs_out_neg v1 dz hv1neg h1
356+ by_cases hv2pos : (0 : Rat) ≤ v2
357+ · -- v1 negative, v2 non-negative
358+ have hv2dz : dz < v2 := by rwa [Rat.abs_of_nonneg hv2pos] at h2
359+ exact Rat.le_of_lt (Std.lt_trans (deadzone_neg v1 dz hv1dz hdz0 hdz1)
360+ (deadzone_pos v2 dz hv2dz hdz0 hdz1))
361+ · -- Both negative
362+ have hv2neg : v2 < 0 := Rat.not_le.mp hv2pos
363+ have hv2dz : v2 < -dz := dz_abs_out_neg v2 dz hv2neg h2
364+ rw [deadzone_neg_eq v1 dz hv1dz hdz0, deadzone_neg_eq v2 dz hv2dz hdz0]
365+ exact dz_div_le _ _ _ ((Rat.add_le_add_right (c := dz)).mpr hv) h1mdz
366+ · -- v1 outside, v2 inside: v1 must be negative (since v1 ≤ v2 ≤ dz < |v1|)
367+ have hin2 : v2.abs ≤ dz := Rat.not_lt.mp h2
368+ have hv1neg : v1 < 0 := by
369+ apply Classical.byContradiction; intro hv1pos
370+ have hv1nn : 0 ≤ v1 := Rat.not_lt.mp hv1pos
371+ rw [Rat.abs_of_nonneg hv1nn] at h1
372+ exact absurd (Rat.le_trans hv (Rat.le_trans (dz_le_abs v2) hin2)) (Rat.not_le.mpr h1)
373+ have hv1dz : v1 < -dz := dz_abs_out_neg v1 dz hv1neg h1
374+ rw [deadzone_in_dz v2 dz hin2]
375+ exact Rat.le_of_lt (deadzone_neg v1 dz hv1dz hdz0 hdz1)
376+ · -- v1 inside deadzone
377+ have hin1 : v1.abs ≤ dz := Rat.not_lt.mp h1
378+ rw [deadzone_in_dz v1 dz hin1]
379+ by_cases h2 : v2.abs > dz
380+ · -- v2 outside: v2 ≥ v1 ≥ -dz, so v2 must be positive (cannot be < -dz)
381+ have hv2dz : dz < v2 := by
382+ apply Classical.byContradiction; intro hv2neg_dz
383+ have hv2nn : ¬(0 ≤ v2) := fun hv2nn =>
384+ absurd (by rwa [Rat.abs_of_nonneg hv2nn] at h2) hv2neg_dz
385+ exact absurd (Rat.le_trans (dz_abs_le_left v1 dz hin1) hv)
386+ (Rat.not_le.mpr (dz_abs_out_neg v2 dz (Rat.not_le.mp hv2nn) h2))
387+ exact Rat.le_of_lt (deadzone_pos v2 dz hv2dz hdz0 hdz1)
388+ · -- Both inside: 0 ≤ 0
389+ rw [deadzone_in_dz v2 dz (Rat.not_lt.mp h2)]; exact Rat.le_refl
390+
391+ /-! ## No-deadzone identity (general) -/
392+
393+ /-- The negative case of the no-deadzone identity: when `dz = 0`, `deadzone x 0 = x` for `x < 0`. -/
394+ theorem deadzone_no_dz_neg (x : Rat) (hx : x < 0 ) : deadzone x 0 = x := by
395+ rw [deadzone_neg_eq x 0 (by rw [Rat.neg_zero]; exact hx) (Rat.le_refl)]
396+ rw [Rat.add_zero, Rat.sub_eq_add_neg, Rat.neg_zero, Rat.add_zero,
397+ Rat.div_def, Rat.inv_eq_of_mul_eq_one (Rat.mul_one 1 ), Rat.mul_one]
398+
399+ /-- **Identity with no deadzone** (all cases): when `dz = 0`, `deadzone x 0 = x` for all `x`.
400+
401+ Combining `deadzone_no_dz_pos`, `deadzone_no_dz_neg`, and the zero case (which is in the
402+ deadzone since `|0| = 0 = dz`), we get the full identity. -/
403+ theorem deadzone_no_dz (x : Rat) : deadzone x 0 = x := by
404+ by_cases h1 : 0 < x
405+ · exact deadzone_no_dz_pos x h1
406+ · by_cases h2 : x = 0
407+ · subst h2
408+ simp [deadzone]
409+ · exact deadzone_no_dz_neg x (Rat.lt_of_le_of_ne (Rat.not_lt.mp h1) h2)
410+
411+ /-! ## Monotonicity in the deadzone width -/
412+
413+ -- Private helpers for `deadzone_mono_dz_pos`
414+
415+ /-- Inverse anti-monotonicity: larger positive denominators give smaller inverses. -/
416+ private theorem dz_inv_anti (a b : Rat) (hba : b ≤ a) (ha : 0 < a) (hb : 0 < b) : a⁻¹ ≤ b⁻¹ := by
417+ have ha_ne : a ≠ 0 := Rat.ne_of_gt ha
418+ have hb_ne : b ≠ 0 := Rat.ne_of_gt hb
419+ rw [Rat.le_iff_sub_nonneg]
420+ have key : b⁻¹ - a⁻¹ = (a - b) * (a⁻¹ * b⁻¹) := by
421+ simp only [Rat.sub_eq_add_neg, Rat.add_mul, Rat.neg_mul]
422+ rw [show a * (a⁻¹ * b⁻¹) = b⁻¹ by
423+ rw [← Rat.mul_assoc a a⁻¹ b⁻¹, Rat.mul_inv_cancel a ha_ne, Rat.one_mul]]
424+ rw [show b * (a⁻¹ * b⁻¹) = a⁻¹ by
425+ rw [Rat.mul_comm a⁻¹ b⁻¹, ← Rat.mul_assoc b b⁻¹ a⁻¹,
426+ Rat.mul_inv_cancel b hb_ne, Rat.one_mul]]
427+ rw [key]
428+ exact Rat.mul_nonneg ((Rat.le_iff_sub_nonneg b a).mp hba)
429+ (Rat.mul_nonneg (Rat.le_of_lt (Rat.inv_pos.mpr ha)) (Rat.le_of_lt (Rat.inv_pos.mpr hb)))
430+
431+ /-- Rewrite `(x - dz) / (1 - dz)` as `1 - (1 - x) / (1 - dz)`. -/
432+ private theorem dz_div_rewrite (x dz : Rat) (h1mdz : 0 < 1 - dz) :
433+ (x - dz) / (1 - dz) = 1 - (1 - x) / (1 - dz) := by
434+ rw [Rat.div_def, Rat.div_def]
435+ have h1mdz_ne : (1 - dz) ≠ 0 := Rat.ne_of_gt h1mdz
436+ have hn : (1 - dz) - (1 - x) = x - dz := by
437+ simp only [Rat.sub_eq_add_neg, Rat.neg_add, Rat.neg_neg]
438+ rw [Rat.add_assoc, ← Rat.add_assoc (-dz) (-1 ) x, Rat.add_comm (-dz) (-1 ),
439+ Rat.add_assoc (-1 ) (-dz) x, ← Rat.add_assoc 1 (-1 ) (-dz + x),
440+ show (1 :Rat) + -1 = 0 from Rat.add_neg_cancel 1 , Rat.zero_add, Rat.add_comm (-dz) x]
441+ rw [show (x - dz) * (1 - dz)⁻¹ = ((1 - dz) - (1 - x)) * (1 - dz)⁻¹ from by rw [hn]]
442+ rw [show ((1 - dz) - (1 - x)) * (1 - dz)⁻¹ = (1 - dz) * (1 - dz)⁻¹ - (1 - x) * (1 - dz)⁻¹
443+ from by simp only [Rat.sub_eq_add_neg, Rat.add_mul, Rat.neg_mul]]
444+ rw [Rat.mul_inv_cancel _ h1mdz_ne]
445+
446+ /-- `c - b ≤ c - a` when `a ≤ b`. -/
447+ private theorem dz_sub_le_sub_l (a b c : Rat) (h : a ≤ b) : c - b ≤ c - a := by
448+ rw [Rat.le_iff_sub_nonneg (c - b) (c - a)]
449+ have key : (c - a) - (c - b) = b - a := by
450+ simp only [Rat.sub_eq_add_neg, Rat.neg_add, Rat.neg_neg]
451+ rw [Rat.add_assoc c (-a) (-c + b), ← Rat.add_assoc (-a) (-c) b, Rat.add_comm (-a) (-c),
452+ Rat.add_assoc (-c) (-a) b, ← Rat.add_assoc c (-c) (-a + b),
453+ show c + -c = (0 :Rat) from Rat.add_neg_cancel c, Rat.zero_add, Rat.add_comm (-a) b]
454+ rw [key]; exact (Rat.le_iff_sub_nonneg a b).mp h
455+
456+ /-- **Monotonicity in `dz`** (positive branch): for a fixed input `x`,
457+ increasing the deadzone width `dz` weakly decreases the output.
458+
459+ Statement: `dz₁ ≤ dz₂ < x ≤ 1`, `0 ≤ dz₁`, `dz₂ < 1` →
460+ `deadzone x dz₂ ≤ deadzone x dz₁`.
461+
462+ Proof: rewrite `(x - dz) / (1 - dz) = 1 - (1 - x) / (1 - dz)`. Since `dz₁ ≤ dz₂`,
463+ we have `1 - dz₂ ≤ 1 - dz₁`, so `(1 - dz₁)⁻¹ ≤ (1 - dz₂)⁻¹` by inverse anti-monotonicity.
464+ Multiplying by `1 - x ≥ 0` gives `(1 - x)/(1 - dz₁) ≤ (1 - x)/(1 - dz₂)`, hence the
465+ subtraction from 1 reverses the inequality. -/
466+ theorem deadzone_mono_dz_pos (x dz1 dz2 : Rat)
467+ (hx : 0 < x) (hx1 : x ≤ 1 )
468+ (h12 : dz1 ≤ dz2) (hdz0 : 0 ≤ dz1) (hdz1 : dz2 < 1 ) (hout : dz2 < x) :
469+ deadzone x dz2 ≤ deadzone x dz1 := by
470+ have hout1 : dz1 < x := Std.lt_of_le_of_lt h12 hout
471+ rw [deadzone_pos_eq x dz1 hout1 hdz0,
472+ deadzone_pos_eq x dz2 hout (Rat.le_trans hdz0 h12)]
473+ have hd1 : (0 :Rat) < 1 - dz1 := dz_one_sub_pos dz1 (Std.lt_of_le_of_lt h12 hdz1)
474+ have hd2 : (0 :Rat) < 1 - dz2 := dz_one_sub_pos dz2 hdz1
475+ -- Rewrite both sides: (x - dz) / (1 - dz) = 1 - (1 - x) / (1 - dz)
476+ rw [dz_div_rewrite x dz1 hd1, dz_div_rewrite x dz2 hd2]
477+ -- Goal: 1 - (1 - x) / (1 - dz2) ≤ 1 - (1 - x) / (1 - dz1)
478+ apply dz_sub_le_sub_l
479+ -- Goal: (1 - x) / (1 - dz1) ≤ (1 - x) / (1 - dz2)
480+ rw [Rat.div_def, Rat.div_def]
481+ have h1mx : (0 :Rat) ≤ 1 - x := (Rat.le_iff_sub_nonneg x 1 ).mp hx1
482+ -- 1 - dz2 ≤ 1 - dz1 (because dz1 ≤ dz2)
483+ have hdz12 : (1 :Rat) - dz2 ≤ 1 - dz1 := by
484+ rw [Rat.le_iff_sub_nonneg]
485+ have key : (1 - dz1) - (1 - dz2) = dz2 - dz1 := by
486+ simp only [Rat.sub_eq_add_neg, Rat.neg_add, Rat.neg_neg]
487+ rw [Rat.add_assoc 1 (-dz1) (-1 + dz2), ← Rat.add_assoc (-dz1) (-1 ) dz2,
488+ Rat.add_comm (-dz1) (-1 ), Rat.add_assoc (-1 ) (-dz1) dz2,
489+ ← Rat.add_assoc 1 (-1 ) (-dz1 + dz2),
490+ show (1 :Rat) + -1 = 0 from Rat.add_neg_cancel 1 ,
491+ Rat.zero_add, Rat.add_comm (-dz1) dz2]
492+ rw [key]; exact (Rat.le_iff_sub_nonneg dz1 dz2).mp h12
493+ -- (1 - dz1)⁻¹ ≤ (1 - dz2)⁻¹ by inverse anti-monotonicity
494+ have hinv : (1 - dz1)⁻¹ ≤ (1 - dz2)⁻¹ := dz_inv_anti (1 - dz1) (1 - dz2) hdz12 hd1 hd2
495+ exact Rat.mul_le_mul_of_nonneg_left hinv h1mx
496+
301497/-! ## Summary of proved properties
302498
303499 | Theorem | Statement | Status |
@@ -310,11 +506,15 @@ theorem deadzone_odd (x dz : Rat) (hdz : 0 ≤ dz) :
310506 | `deadzone_pos` | `x > dz ≥ 0, dz < 1 → deadzone x dz > 0` | ✅ Proved |
311507 | `deadzone_neg` | `x < -dz ≤ 0, dz < 1 → deadzone x dz < 0` | ✅ Proved |
312508 | `deadzone_no_dz_pos` | `deadzone x 0 = x` (identity, positive case) | ✅ Proved |
509+ | `deadzone_no_dz_neg` | `deadzone x 0 = x` (identity, negative case) | ✅ Proved |
510+ | `deadzone_no_dz` | `deadzone x 0 = x` (identity, all inputs) | ✅ Proved |
313511 | `deadzone_at_max` | `deadzone 1 dz = 1` (for `dz < 1`) | ✅ Proved |
314512 | `deadzone_at_min` | `deadzone (-1) dz = -1` (for `dz < 1`) | ✅ Proved |
315513 | `deadzone_le_one` | `x ≤ 1, 0 ≤ dz < 1 → deadzone x dz ≤ 1` | ✅ Proved |
316514 | `deadzone_ge_neg_one` | `-1 ≤ x, 0 ≤ dz < 1 → -1 ≤ deadzone x dz` | ✅ Proved |
317515 | `deadzone_odd` | `dz ≥ 0 → deadzone (-x) dz = -(deadzone x dz)` | ✅ Proved |
516+ | `deadzone_mono_val` | `v₁ ≤ v₂ → deadzone v₁ dz ≤ deadzone v₂ dz` | ✅ Proved |
517+ | `deadzone_mono_dz_pos` | `dz₁ ≤ dz₂ < x ≤ 1 → deadzone x dz₂ ≤ deadzone x dz₁` | ✅ Proved |
318518-/
319519
320520end PX4.Deadzone
0 commit comments