Zulip Chat Archive

Stream: mathlib4

Topic: Instances of DivisibleBy


Stepan Nesterov (Jan 06 2026 at 22:25):

I am confused as to how the instances of DivisibleBy are set up.

import Mathlib

#synth DivisibleBy ℚ ℤ -- works
#synth DivisibleBy ℚ ℕ -- fails

section divZ

variable (A : Type*) [AddCommGroup A] [DivisibleBy A ℤ] (B : AddSubgroup A)

#synth DivisibleBy (A ⧸ B) ℤ -- fails

end divZ

section divN

variable (A : Type*) [AddCommGroup A] [DivisibleBy A ℕ] (B : AddSubgroup A)

#synth DivisibleBy (A ⧸ B) ℕ -- works

end divN

#synth DivisibleBy (ℚ ⧸ (Int.castAddHom ℚ).range) ℕ -- fails to synthesize `DivisibleBy ℚ ℕ`
#synth DivisibleBy (ℚ ⧸ (Int.castAddHom ℚ).range) ℤ -- fails to synthesize `DivisibleBy (A ⧸ B) ℤ`

Because ℚ is only divisible by ℤ and the quotient group only inherits divisibility by ℕ, mathlib can not infer that Q/Z\mathbb{Q}/\mathbb{Z} is a divisible group. Is there a reason for why it works like that?

Ruben Van de Velde (Jan 06 2026 at 22:27):

docs#DivisibleBy

Kevin Buzzard (Jan 06 2026 at 23:32):

Youch, this is a constructive definition, so it doesn't surprise me that there are some missing instances: adding them might cause diamonds. Are you sure you want to be using it at all? This doesn't just say "there's a yy such that ny=xny=x", it says "we give a yy such that ny=xny=x and you'd better hope that nobody else gives another one even when there are many choices for yy".

Stepan Nesterov (Jan 06 2026 at 23:34):

Kevin Buzzard said:

Youch, this is a constructive definition, so it doesn't surprise me that there are some missing instances: adding them might cause diamonds. Are you sure you want to be using it at all? This doesn't just say "there's a yy such that ny=xny=x", it says "we give a yy such that ny=xny=x and you'd better hope that nobody else gives another one even when there are many choices for yy".

Is there a non-constructive counterpart that I could use?

Kevin Buzzard (Jan 06 2026 at 23:38):

You could make IsDivisibleBy, define it as Nonempty (DivisbleBy ...) and then you could beef up

/-- A group is `ℤ`-rootable if it is `ℕ`-rootable.
-/
@[to_additive /-- An additive group is `ℤ`-divisible if it is `ℕ`-divisible. -/]
def rootableByIntOfRootableByNat [RootableBy A ℕ] : RootableBy A ℤ

and

/-- A group is `ℕ`-rootable if it is `ℤ`-rootable
-/
@[to_additive /-- An additive group is `ℕ`-divisible if it `ℤ`-divisible. -/]
def rootableByNatOfRootableByInt [RootableBy A ℤ] : RootableBy A ℕ where

into instances (right now they're not instances because I guess they'll cause diamonds?)

Kevin Buzzard (Jan 06 2026 at 23:40):

This sounds like a fun exercise, let me try knocking something up. For all I know there's already a standard way of talking about divisible abelian groups already though (although I don't know one and couldn't immediately spot one).

Kevin Buzzard (Jan 07 2026 at 00:03):

import Mathlib

class IsDivisibleBy (A α : Type*) [AddMonoid A] [SMul α A] [Zero α] : Prop where
  nonemptyDivisibleBy : Nonempty (DivisibleBy A α)

section monoid

variable {A α : Type*} [AddMonoid A] [SMul α A] [Zero α]

theorem isDivisibleBy_def : IsDivisibleBy A α ↔ ∀ {n : α} (a : A), n ≠ 0 → ∃ y, n • y = a := by
  constructor
  · rintro ⟨div, div_zero, div_cancel⟩ n a hn
    exact ⟨div a n, div_cancel _ hn⟩
  · intro h
    classical
    let div : A → α → A := fun a n ↦ if hn : n = 0 then 0 else (h a hn).choose
    refine ⟨div, ?_, ?_⟩
    · intro a
      exact dif_pos rfl
    · intro n a hn
      convert (h a hn).choose_spec
      exact dif_neg hn

instance [h : DivisibleBy A α] : IsDivisibleBy A α := ⟨⟨h⟩⟩

end monoid

section group

variable {A α : Type*} [AddGroup A] [SMul α A] [Zero α]

instance [h : IsDivisibleBy A ℕ] : IsDivisibleBy A ℤ := by
  obtain ⟨⟨h⟩⟩ := h
  exact ⟨⟨AddGroup.divisibleByIntOfDivisibleByNat A⟩⟩

instance [h : IsDivisibleBy A ℤ] : IsDivisibleBy A ℕ := by
  obtain ⟨⟨h⟩⟩ := h
  exact ⟨⟨AddGroup.divisibleByNatOfDivisibleByInt A⟩⟩

#synth IsDivisibleBy ℚ ℤ -- works
#synth IsDivisibleBy ℚ ℕ -- works

end group

section commgroup

variable {A α : Type*} [AddCommGroup A] [SMul α A] [Zero α]

noncomputable instance AddQuotientGroup.isDivisibleBy
  [h : IsDivisibleBy A ℤ] (B : AddSubgroup A): IsDivisibleBy (A ⧸ B) ℤ := by
  obtain ⟨⟨h⟩⟩ := h
  exact ⟨⟨QuotientAddGroup.mk_surjective.divisibleBy _ fun _ _ => rfl⟩⟩

section divZ

variable (A : Type*) [AddCommGroup A] [IsDivisibleBy A ℤ] (B : AddSubgroup A)

#synth IsDivisibleBy (A ⧸ B) ℤ -- works

end divZ

section divN

variable (A : Type*) [AddCommGroup A] [IsDivisibleBy A ℕ] (B : AddSubgroup A)

#synth IsDivisibleBy (A ⧸ B) ℕ -- works

end divN

#synth IsDivisibleBy (ℚ ⧸ (Int.castAddHom ℚ).range) ℕ -- works
#synth IsDivisibleBy (ℚ ⧸ (Int.castAddHom ℚ).range) ℤ -- works

Don't know if mathlib will be interested. For sure, if it is, it will want IsRootableBy with all proofs multiplicativised.


Last updated: Feb 28 2026 at 14:05 UTC