Definitions/Def_DualIsogenyAPI.lean
Abstract dual pairs and dual endomorphism data
For additive abelian groups A,B, the predicate AddMonoidHom.IsDualPair φ ψ n, with \varphi : A \to B, \psi : B \to A additive maps and n \in \mathbb{Z}, is a two-field proposition: \psi(\varphi a) = n \cdot a for all a \in A and \varphi(\psi b) = n \cdot b for all b \in B (integer scalar action). It is symmetric in the two maps (IsDualPair.symm, isDualPair_comm); for composable pairs the composites form a dual pair with the roles reversed and the integers multiplied (IsDualPair.comp); the multiplication-by-m endomorphism is self-paired with integer m^2, and the identity with 1. Further lemmas give uniqueness of the partner when \varphi is surjective or injective, the inclusions \ker\psi \subseteq B[n] and \ker\varphi \subseteq A[n], membership of n \cdot a in the range of \psi, surjectivity of \psi when A is n-divisible, and stability under negating both maps.
AddMonoid.End.DualEndData φ is a bundled structure on an endomorphism \varphi of A carrying a further endomorphism dual and integers trace, norm, together with the equations \varphi + \hat\varphi = t, \varphi\hat\varphi = n and \hat\varphi\varphi = n in \mathrm{End}(A) (integers acting through \mathbb{Z} \to \mathrm{End}(A)); the equations are fields, so the data is a choice, not a property. Consequences: the pair (\varphi,\hat\varphi) is a dual pair with integer n, the two commute, \hat\varphi = t - \varphi, the relation \varphi^2 - t\varphi + n = 0 holds in operator and pointwise form, and the roles of \varphi,\hat\varphi may be exchanged. intLinComb produces data for a + b\varphi with dual a + b\hat\varphi, trace 2a + tb and norm a^2 + tab + nb^2, whence the discriminant scales by b^2 and negativity of t^2 - 4n is preserved for b \neq 0. ofCharPoly builds data from a relation \varphi^2 - t\varphi + n = 0, and dualEndData_intCast treats \varphi = m.
Two named predicates on a subring S \subseteq \mathrm{End}(A) record what is not proved here: DualIsogenyExistence S asserts that every \varphi \in S admits dual data with dual in S and non-negative norm, and DualAdditivityResidual S that for \varphi,\psi \in S and arbitrary dual data for each there is dual data for \varphi + \psi whose dual is the sum of the two duals. The first holds for S = \bot, the subring of integer multiplications. Small computations in \mathrm{End}(\mathbb{Z}) check the norm, trace and discriminant formulae on numerical instances.
Relation to Mathlib
Mathlib has no abstract notion of a dual pair of additive maps or of dual data for an additive endomorphism; both are the project's own, stated for AddMonoidHom and for the endomorphism ring AddMonoid.End with integers acting via zsmul and Int.cast.
Where it is used
This is the purely group-theoretic substrate for degrees of isogenies: the dual-pair relations and the quadratic relation \varphi^2 - t\varphi + n = 0 with its norm form (a+b\varphi)(a+b\hat\varphi) = a^2 + tab + nb^2 are what later modules use for endomorphism rings of elliptic curves and for the Frobenius–Verschiebung relation on curves over finite fields, with no curves or schemes appearing at this level.
References
- J. H. Silverman, The Arithmetic of Elliptic Curves, 2nd ed., Graduate Texts in Mathematics 106, Springer, 2009, §III.6 and §III.9
References are suggested automatically and have not been individually verified.
English text generated automatically from the Lean source; the Lean statement is authoritative.
- 317 lines
- 56 declarations
- used in the statements of 7 theorems and imported by 35 proofs
- imports 0 definition modules
Source file: Definitions/Def_DualIsogenyAPI.lean
Imports
- only Mathlib
Imported by
Declarations
- structure
AddMonoidHom.IsDualPair - field
AddMonoidHom.IsDualPair.comp_left - field
AddMonoidHom.IsDualPair.comp_right - theorem
AddMonoidHom.IsDualPair.symm - theorem
AddMonoidHom.isDualPair_comm - theorem
AddMonoidHom.IsDualPair.comp - theorem
AddMonoidHom.isDualPair_intCast - theorem
AddMonoidHom.isDualPair_id_one - theorem
AddMonoidHom.IsDualPair.left_unique_of_surjective - theorem
AddMonoidHom.IsDualPair.right_unique_of_injective - theorem
AddMonoidHom.IsDualPair.ker_le_torsion_left - theorem
AddMonoidHom.IsDualPair.ker_le_torsion_right - theorem
AddMonoidHom.IsDualPair.nsmul_mem_range_left - theorem
AddMonoidHom.IsDualPair.surjective_of_divisible - theorem
AddMonoidHom.IsDualPair.neg - structure
AddMonoid.End.DualEndData - field
AddMonoid.End.DualEndData.dual - field
AddMonoid.End.DualEndData.trace - field
AddMonoid.End.DualEndData.norm - field
AddMonoid.End.DualEndData.add_dual - field
AddMonoid.End.DualEndData.mul_dual - field
AddMonoid.End.DualEndData.dual_mul - theorem
AddMonoid.End.DualEndData.isDualPair - theorem
AddMonoid.End.DualEndData.commute - theorem
AddMonoid.End.DualEndData.dual_eq - theorem
AddMonoid.End.DualEndData.charPoly - theorem
AddMonoid.End.DualEndData.add_dual_apply - theorem
AddMonoid.End.DualEndData.charPoly_apply - def
AddMonoid.End.DualEndData.symm - theorem
AddMonoid.End.DualEndData.symm_dual - theorem
AddMonoid.End.DualEndData.symm_trace - theorem
AddMonoid.End.DualEndData.symm_norm - theorem
AddMonoid.End.DualEndData.intLin_apply - theorem
AddMonoid.End.DualEndData.dual_charPoly - theorem
AddMonoid.End.DualEndData.intLinComb_mul_dual - def
AddMonoid.End.DualEndData.intLinComb - theorem
AddMonoid.End.DualEndData.intLinComb_norm - theorem
AddMonoid.End.DualEndData.intLinComb_trace - theorem
AddMonoid.End.DualEndData.intLinComb_dual - theorem
AddMonoid.End.DualEndData.intLinComb_disc - theorem
AddMonoid.End.DualEndData.intLinComb_disc_neg - def
AddMonoid.End.DualEndData.ofCharPoly - theorem
AddMonoid.End.DualEndData.ofCharPoly_trace - theorem
AddMonoid.End.DualEndData.ofCharPoly_norm - theorem
AddMonoid.End.DualEndData.ofCharPoly_dual - def
AddMonoid.End.dualEndData_intCast - theorem
AddMonoid.End.dualEndData_intCast_norm - theorem
AddMonoid.End.dualEndData_intCast_trace - theorem
AddMonoid.End.dualEndData_intCast_dual - def
DualIsogenyExistence - def
DualAdditivityResidual - theorem
dualIsogenyExistence_bot - theorem
one_charPoly_int - theorem
gate_intLinComb_norm_at_one - theorem
gate_dualEndData_intCast_three - theorem
gate_intLinComb_disc_scaling
Source
import Mathlib set_option autoImplicit false namespace AddMonoidHom variable {A B C : Type*} [AddCommGroup A] [AddCommGroup B] [AddCommGroup C] structure IsDualPair (φ : A →+ B) (ψ : B →+ A) (n : ℤ) : Prop where comp_left : ∀ a, ψ (φ a) = n • a comp_right : ∀ b, φ (ψ b) = n • b theorem IsDualPair.symm {φ : A →+ B} {ψ : B →+ A} {n : ℤ} (h : IsDualPair φ ψ n) : IsDualPair ψ φ n := ⟨h.comp_right, h.comp_left⟩ theorem isDualPair_comm {φ : A →+ B} {ψ : B →+ A} {n : ℤ} : IsDualPair φ ψ n ↔ IsDualPair ψ φ n := ⟨IsDualPair.symm, IsDualPair.symm⟩ theorem IsDualPair.comp {φ' : A →+ B} {φ : B →+ A} {m : ℤ} {ψ' : B →+ C} {ψ : C →+ B} {n : ℤ} (hφ : IsDualPair φ' φ m) (hψ : IsDualPair ψ' ψ n) : IsDualPair (ψ'.comp φ') (φ.comp ψ) (m * n) := by refine ⟨fun a => ?_, fun c => ?_⟩ · show φ (ψ (ψ' (φ' a))) = (m * n) • a rw [hψ.comp_left, map_zsmul, hφ.comp_left, mul_zsmul'] · show ψ' (φ' (φ (ψ c))) = (m * n) • c rw [hφ.comp_right, map_zsmul, hψ.comp_right, mul_zsmul] theorem isDualPair_intCast (m : ℤ) : IsDualPair (A := A) (B := A) (m : AddMonoid.End A) (m : AddMonoid.End A) (m ^ 2) := by have key : ∀ a : A, (m : AddMonoid.End A) ((m : AddMonoid.End A) a) = m ^ 2 • a := by intro a have h : (m : AddMonoid.End A) ((m : AddMonoid.End A) a) = ((m : AddMonoid.End A) * (m : AddMonoid.End A)) a := rfl rw [h, ← Int.cast_mul, AddMonoid.End.intCast_apply, sq] exact ⟨key, key⟩ theorem isDualPair_id_one : IsDualPair (id A) (id A) 1 := ⟨fun a => (one_zsmul a).symm, fun a => (one_zsmul a).symm⟩ theorem IsDualPair.left_unique_of_surjective {φ : A →+ B} {ψ : B →+ A} {n : ℤ} (h : IsDualPair φ ψ n) (hφ : Function.Surjective φ) {ψ' : B →+ A} (hψ' : ∀ a, ψ' (φ a) = n • a) : ψ' = ψ := by ext b obtain ⟨a, rfl⟩ := hφ b rw [hψ', h.comp_left] theorem IsDualPair.right_unique_of_injective {φ : A →+ B} {ψ : B →+ A} {n : ℤ} (h : IsDualPair φ ψ n) (hφ : Function.Injective φ) {ψ' : B →+ A} (hψ' : ∀ b, φ (ψ' b) = n • b) : ψ' = ψ := by ext b; exact hφ (by rw [hψ', h.comp_right]) theorem IsDualPair.ker_le_torsion_left {φ : A →+ B} {ψ : B →+ A} {n : ℤ} (h : IsDualPair φ ψ n) {b : B} (hb : ψ b = 0) : n • b = 0 := by rw [← h.comp_right b, hb, map_zero] theorem IsDualPair.ker_le_torsion_right {φ : A →+ B} {ψ : B →+ A} {n : ℤ} (h : IsDualPair φ ψ n) {a : A} (ha : φ a = 0) : n • a = 0 := h.symm.ker_le_torsion_left ha theorem IsDualPair.nsmul_mem_range_left {φ : A →+ B} {ψ : B →+ A} {n : ℤ} (h : IsDualPair φ ψ n) (a : A) : n • a ∈ Set.range ψ := ⟨φ a, h.comp_left a⟩ theorem IsDualPair.surjective_of_divisible {φ : A →+ B} {ψ : B →+ A} {n : ℤ} (h : IsDualPair φ ψ n) (hdiv : ∀ a : A, ∃ a', n • a' = a) : Function.Surjective ψ := by intro a; obtain ⟨a', ha'⟩ := hdiv a; exact ⟨φ a', by rw [h.comp_left, ha']⟩ theorem IsDualPair.neg {φ : A →+ B} {ψ : B →+ A} {n : ℤ} (h : IsDualPair φ ψ n) : IsDualPair (-φ) (-ψ) n := ⟨fun a => by simp [h.comp_left], fun b => by simp [h.comp_right]⟩ end AddMonoidHom namespace AddMonoid.End variable {A : Type*} [AddCommGroup A] structure DualEndData (φ : AddMonoid.End A) where dual : AddMonoid.End A trace : ℤ norm : ℤ add_dual : φ + dual = (trace : AddMonoid.End A) mul_dual : φ * dual = (norm : AddMonoid.End A) dual_mul : dual * φ = (norm : AddMonoid.End A) namespace DualEndData variable {φ : AddMonoid.End A} theorem isDualPair (D : DualEndData φ) : AddMonoidHom.IsDualPair (A := A) (B := A) φ D.dual D.norm := by refine ⟨fun a => ?_, fun a => ?_⟩ · have h := DFunLike.congr_fun D.dual_mul a rwa [AddMonoid.End.intCast_apply] at h · have h := DFunLike.congr_fun D.mul_dual a rwa [AddMonoid.End.intCast_apply] at h theorem commute (D : DualEndData φ) : Commute φ D.dual := by unfold Commute SemiconjBy; rw [D.mul_dual, D.dual_mul] theorem dual_eq (D : DualEndData φ) : D.dual = (D.trace : AddMonoid.End A) - φ := by rw [← D.add_dual, add_sub_cancel_left] theorem charPoly (D : DualEndData φ) : φ * φ - (D.trace : AddMonoid.End A) * φ + (D.norm : AddMonoid.End A) = 0 := by have h := D.dual_mul rw [D.dual_eq, sub_mul] at h rw [← h]; abel theorem add_dual_apply (D : DualEndData φ) (a : A) : φ a + D.dual a = D.trace • a := by have h : (φ + D.dual) a = (D.trace : AddMonoid.End A) a := by rw [D.add_dual] rwa [AddMonoid.End.intCast_apply] at h theorem charPoly_apply (D : DualEndData φ) (a : A) : φ (φ a) - D.trace • φ a + D.norm • a = 0 := by have h1 := D.isDualPair.comp_left a have h2 := D.add_dual_apply (φ a) rw [← h1, ← h2, sub_add_cancel_left]; exact neg_add_cancel _ def symm (D : DualEndData φ) : DualEndData D.dual where dual := φ trace := D.trace norm := D.norm add_dual := by rw [← D.add_dual, add_comm] mul_dual := D.dual_mul dual_mul := D.mul_dual @[simp] theorem symm_dual (D : DualEndData φ) : D.symm.dual = φ := rfl @[simp] theorem symm_trace (D : DualEndData φ) : D.symm.trace = D.trace := rfl @[simp] theorem symm_norm (D : DualEndData φ) : D.symm.norm = D.norm := rfl private theorem intLin_apply (a b : ℤ) (ψ : AddMonoid.End A) (x : A) : ((a : AddMonoid.End A) + (b : AddMonoid.End A) * ψ) x = a • x + b • ψ x := by show (a : AddMonoid.End A) x + (b : AddMonoid.End A) (ψ x) = _ rw [AddMonoid.End.intCast_apply, AddMonoid.End.intCast_apply] theorem dual_charPoly (D : DualEndData φ) : D.dual * D.dual - (D.trace : AddMonoid.End A) * D.dual + (D.norm : AddMonoid.End A) = 0 := D.symm.charPoly theorem intLinComb_mul_dual (D : DualEndData φ) (a b : ℤ) : ((a : AddMonoid.End A) + (b : AddMonoid.End A) * φ) * ((a : AddMonoid.End A) + (b : AddMonoid.End A) * D.dual) = ((a ^ 2 + D.trace * a * b + D.norm * b ^ 2 : ℤ) : AddMonoid.End A) := by ext x show ((a : AddMonoid.End A) + b * φ) (((a : AddMonoid.End A) + b * D.dual) x) = ((a ^ 2 + D.trace * a * b + D.norm * b ^ 2 : ℤ) : AddMonoid.End A) x rw [intLin_apply, intLin_apply, AddMonoid.End.intCast_apply, map_add, map_zsmul, map_zsmul] have hcr : φ (D.dual x) = D.norm • x := D.isDualPair.comp_right x have hsum := D.add_dual_apply x calc a • (a • x + b • D.dual x) + b • (a • φ x + b • φ (D.dual x)) = (a * a) • x + (a * b) • D.dual x + ((b * a) • φ x + (b * b) • φ (D.dual x)) := by simp only [zsmul_add, ← mul_zsmul] _ = (a * a) • x + (a * b) • (φ x + D.dual x) + (b * b) • φ (D.dual x) := by rw [zsmul_add, mul_comm b a]; abel _ = (a ^ 2 + D.trace * a * b + D.norm * b ^ 2) • x := by rw [hcr, hsum, ← mul_zsmul, ← mul_zsmul, ← add_zsmul, ← add_zsmul] congr 1; ring def intLinComb (D : DualEndData φ) (a b : ℤ) : DualEndData ((a : AddMonoid.End A) + (b : AddMonoid.End A) * φ) where dual := (a : AddMonoid.End A) + (b : AddMonoid.End A) * D.dual trace := 2 * a + D.trace * b norm := a ^ 2 + D.trace * a * b + D.norm * b ^ 2 add_dual := by have h : (a : AddMonoid.End A) + b * φ + ((a : AddMonoid.End A) + b * D.dual) = (a : AddMonoid.End A) + (a : AddMonoid.End A) + b * (φ + D.dual) := by rw [mul_add]; abel rw [h, D.add_dual] ext x show (a : AddMonoid.End A) x + (a : AddMonoid.End A) x + ((b : AddMonoid.End A) * (D.trace : AddMonoid.End A)) x = ((2 * a + D.trace * b : ℤ) : AddMonoid.End A) x rw [show ((b : AddMonoid.End A) * (D.trace : AddMonoid.End A)) x = (b : AddMonoid.End A) ((D.trace : AddMonoid.End A) x) from rfl, AddMonoid.End.intCast_apply, AddMonoid.End.intCast_apply, AddMonoid.End.intCast_apply, AddMonoid.End.intCast_apply, ← add_zsmul, ← mul_zsmul, ← add_zsmul] congr 1; ring mul_dual := D.intLinComb_mul_dual a b dual_mul := D.symm.intLinComb_mul_dual a b @[simp] theorem intLinComb_norm (D : DualEndData φ) (a b : ℤ) : (D.intLinComb a b).norm = a ^ 2 + D.trace * a * b + D.norm * b ^ 2 := rfl @[simp] theorem intLinComb_trace (D : DualEndData φ) (a b : ℤ) : (D.intLinComb a b).trace = 2 * a + D.trace * b := rfl @[simp] theorem intLinComb_dual (D : DualEndData φ) (a b : ℤ) : (D.intLinComb a b).dual = (a : AddMonoid.End A) + (b : AddMonoid.End A) * D.dual := rfl theorem intLinComb_disc (D : DualEndData φ) (a b : ℤ) : (D.intLinComb a b).trace ^ 2 - 4 * (D.intLinComb a b).norm = b ^ 2 * (D.trace ^ 2 - 4 * D.norm) := by simp only [intLinComb_trace, intLinComb_norm]; ring theorem intLinComb_disc_neg (D : DualEndData φ) {a b : ℤ} (hb : b ≠ 0) (hD : D.trace ^ 2 < 4 * D.norm) : (D.intLinComb a b).trace ^ 2 < 4 * (D.intLinComb a b).norm := by have hkey := D.intLinComb_disc a b have hbsq : (0 : ℤ) < b ^ 2 := by positivity nlinarith end DualEndData def DualEndData.ofCharPoly (φ : AddMonoid.End A) (t n : ℤ) (hchar : φ * φ - (t : AddMonoid.End A) * φ + (n : AddMonoid.End A) = 0) : DualEndData φ where dual := (t : AddMonoid.End A) - φ trace := t norm := n add_dual := by abel mul_dual := by have hc : φ * (t : AddMonoid.End A) = (t : AddMonoid.End A) * φ := (Int.cast_commute t φ).eq.symm have := hchar; rw [mul_sub, hc, ← sub_eq_zero, show (t : AddMonoid.End A) * φ - φ * φ - (n : AddMonoid.End A) = -(φ * φ - (t : AddMonoid.End A) * φ + (n : AddMonoid.End A)) by abel, this, neg_zero] dual_mul := by have := hchar; rw [sub_mul, ← sub_eq_zero, show (t : AddMonoid.End A) * φ - φ * φ - (n : AddMonoid.End A) = -(φ * φ - (t : AddMonoid.End A) * φ + (n : AddMonoid.End A)) by abel, this, neg_zero] @[simp] theorem DualEndData.ofCharPoly_trace (φ : AddMonoid.End A) (t n : ℤ) (h) : (DualEndData.ofCharPoly φ t n h).trace = t := rfl @[simp] theorem DualEndData.ofCharPoly_norm (φ : AddMonoid.End A) (t n : ℤ) (h) : (DualEndData.ofCharPoly φ t n h).norm = n := rfl @[simp] theorem DualEndData.ofCharPoly_dual (φ : AddMonoid.End A) (t n : ℤ) (h) : (DualEndData.ofCharPoly φ t n h).dual = (t : AddMonoid.End A) - φ := rfl def dualEndData_intCast (m : ℤ) : DualEndData (A := A) (m : AddMonoid.End A) where dual := (m : AddMonoid.End A) trace := 2 * m norm := m ^ 2 add_dual := by push_cast; noncomm_ring mul_dual := by rw [← Int.cast_mul, ← sq] dual_mul := by rw [← Int.cast_mul, ← sq] @[simp] theorem dualEndData_intCast_norm (m : ℤ) : (dualEndData_intCast (A := A) m).norm = m ^ 2 := rfl @[simp] theorem dualEndData_intCast_trace (m : ℤ) : (dualEndData_intCast (A := A) m).trace = 2 * m := rfl @[simp] theorem dualEndData_intCast_dual (m : ℤ) : (dualEndData_intCast (A := A) m).dual = (m : AddMonoid.End A) := rfl end AddMonoid.End section NamedResiduals variable {A : Type*} [AddCommGroup A] def DualIsogenyExistence (S : Subring (AddMonoid.End A)) : Prop := ∀ φ ∈ S, ∃ D : AddMonoid.End.DualEndData φ, D.dual ∈ S ∧ 0 ≤ D.norm def DualAdditivityResidual (S : Subring (AddMonoid.End A)) : Prop := ∀ φ ∈ S, ∀ ψ ∈ S, ∀ Dφ : AddMonoid.End.DualEndData φ, ∀ Dψ : AddMonoid.End.DualEndData ψ, ∃ D : AddMonoid.End.DualEndData (φ + ψ), D.dual = Dφ.dual + Dψ.dual theorem dualIsogenyExistence_bot : DualIsogenyExistence (⊥ : Subring (AddMonoid.End A)) := by intro φ hφ obtain ⟨m, rfl⟩ := Subring.mem_bot.mp hφ exact ⟨AddMonoid.End.dualEndData_intCast m, Subring.mem_bot.mpr ⟨m, rfl⟩, sq_nonneg m⟩ end NamedResiduals section SatGates private theorem one_charPoly_int : (1 : AddMonoid.End ℤ) * 1 - ((2 : ℤ) : AddMonoid.End ℤ) * 1 + ((1 : ℤ) : AddMonoid.End ℤ) = 0 := by norm_num theorem gate_intLinComb_norm_at_one : ((AddMonoid.End.DualEndData.ofCharPoly (A := ℤ) 1 2 1 one_charPoly_int).intLinComb 1 2).norm = 9 := by simp only [AddMonoid.End.DualEndData.intLinComb_norm, AddMonoid.End.DualEndData.ofCharPoly_trace, AddMonoid.End.DualEndData.ofCharPoly_norm] decide theorem gate_dualEndData_intCast_three : (AddMonoid.End.dualEndData_intCast (A := ℤ) 3).norm = 9 ∧ (AddMonoid.End.dualEndData_intCast (A := ℤ) 3).trace = 6 := ⟨rfl, rfl⟩ theorem gate_intLinComb_disc_scaling : ((AddMonoid.End.DualEndData.ofCharPoly (A := ℤ) 1 2 1 one_charPoly_int).intLinComb 0 2).trace ^ 2 - 4 * ((AddMonoid.End.DualEndData.ofCharPoly (A := ℤ) 1 2 1 one_charPoly_int).intLinComb 0 2).norm = 2 ^ 2 * (2 ^ 2 - 4 * 1) := by rw [AddMonoid.End.DualEndData.intLinComb_disc, AddMonoid.End.DualEndData.ofCharPoly_trace, AddMonoid.End.DualEndData.ofCharPoly_norm] end SatGates
Statements phrased using this module (7)
- Non-integral isogeny endomorphism forces an imaginary quadratic degree form
WeierstrassCurve.Affine.IsogenyEndDatum.exists_sq_lt_four_mul_and_forall_exists_finrankAlong_eq51 below · depth 12 - Existence of dual endomorphism data with norm the degree
WeierstrassCurve.Affine.IsogenyEndDatum.exists_dualEndData_dual_mem_and_norm_eq_finrankAlong44 below · depth 13 - Dual pairs of degree coprime to q transport q-torsion-freeness
AddMonoidHom.IsDualPair.forall_q_zsmul_eq_zero_of_isCoprime0 below · depth 16 - Rational dual isogeny with integral trace
WeierstrassCurve.exists_mem_rationalHomSet_isDualPair_and_add_eq_smul_id13 below · depth 16 - Existence of dual endomorphisms in the rational endomorphism subring
WeierstrassCurve.dualIsogenyExistence_rationalEndSubring11 below · depth 17 - Existence of a dual pair for rational homomorphisms
WeierstrassCurve.exists_isDualPair_of_mem_rationalHomSet15 below · depth 18 - Rational isogeny with rational dual killing a Frobenius-stable ℓ-subgroup
WeierstrassCurve.exists_mem_rationalHomSet_ker_eq_zmultiples_of_map_mem_zmultiples64 below · depth 21