Zulip Chat Archive

Stream: new members

Topic: Rearranging minus signs


Jake Levinson (Apr 26 2023 at 20:43):

Is there a good way to rearrange minus signs? For example, is this already in mathlib:

example (n k : ) : (-1 : )^n = (-1 : )^k  (-1 : )^(n+k) = 1 := sorry

Or equivalent over an arbitrary comm_ring, or similar things like

Here's a hacky proof over the integers:

example (n k : ) : (-1 : )^n = (-1 : )^k  (-1 : )^(n+k) = 1 :=
begin
  split; intro h,
  rw [pow_add, h,  pow_add],
  exact even.neg_one_pow _, rfl⟩,
  rw [pow_add, int.mul_eq_one_iff_eq_one_or_neg_one] at h,
  cases h; rw [h.1, h.2],
end

Jake Levinson (Apr 26 2023 at 20:45):

Here's a hacky proof over an arbitrary comm_ring:

example (n k : ) (R : Type) [comm_ring R] :
  (-1 : R)^n = (-1 : R)^k  (-1 : R)^(n+k) = 1 :=
begin
  split; intro h,
  rw [pow_add, h,  pow_add],
  exact even.neg_one_pow _, rfl⟩,
  rw pow_add at h,
  rw  mul_one ((-1 : R)^n),
  nth_rewrite 1  even.neg_one_pow k, rfl⟩,
  rw [pow_add,  mul_assoc],
  convert one_mul _,
  convert h,
  refl,
end

There's a strange goal at the end: ⊢ sub_neg_monoid.to_has_neg R = has_involutive_neg.to_has_neg R. Really the last three lines should just be rw h.

Jake Levinson (Apr 26 2023 at 20:47):

Anyway, it would be nice to add some of this functionality to mathlib.

Eric Wieser (Apr 26 2023 at 20:52):

This was perhaps a bit easier:

import algebra.ring.units
import algebra.group_power.ring
import algebra.field.power
import data.int.basic

example (n k : ) (R : Type) [comm_ring R] :
  (-1 : R)^n = (-1 : R)^k  (-1 : R)^(n+k) = 1 :=
begin
  suffices : (-1 : Rˣ)^(n : ) = (-1 : Rˣ)^(k : )  (-1 : Rˣ)^(n+k : ) = 1,
  { simp_rw [units.ext_iff, nat.cast_add, zpow_coe_nat] at this,
    push_cast at this,
    exact this },
  rw [mul_inv_eq_one, inv_zpow, zpow_add, inv_neg', inv_one],
end

Eric Wieser (Apr 26 2023 at 20:53):

The trick is to move to the docs#units of R, and integer powers, where you can just move everything around

Damiano Testa (Apr 26 2023 at 21:37):

There is also the good old case-bash:

import algebra.group_power.ring

example (n k : ) (R : Type) [comm_ring R] :
  (-1 : R)^n = (-1 : R)^k  (-1 : R)^(n+k) = 1 :=
begin
  cases neg_one_pow_eq_or R n;
  cases neg_one_pow_eq_or R k;
  simp [*, pow_add, eq_comm],
end

Jake Levinson (Apr 26 2023 at 22:25):

Nice proofs, @Eric Wieser and @Damiano Testa . Either version seems worth adding to mathlib. (I came upon this while doing a different computation, so it would be nice not to be reproving a fact like this, even if the optimized proof is only 3-5 lines).

Eric Wieser (Apr 26 2023 at 22:27):

Can you share the statement of the larger computation?

Eric Wieser (Apr 26 2023 at 22:28):

Sometimes changing the formulation is a better option than adding slightly contrived algebraic lemmas

Jake Levinson (Apr 26 2023 at 22:31):

I see, let's see. It wasn't just one, though, it was a few different equations. For instance:
(n k : ℕ) : even (n + k) → (-1 : ℤ)^n = (-1)^k
(or with R in place of ).

Jake Levinson (Apr 26 2023 at 22:32):

Or the same statement with odd and an extra sign. This was for the discussion in #18837 about even and odd polynomials, which actually leads to a goal of the form

example (n k : ) (a : ) : odd (n + k)  (-1 : )^n * a = (-1)^k * a  a = 0 := sorry

Eric Wieser (Apr 26 2023 at 22:45):

I would suggest writing (-1 : ℤ)^n * a = (-1)^k * a as (-1 : ℤˣ)^n • a = (-1 : ℤˣ)^k • a, and then you can prove it for any ring where 2 is invertible

Eric Wieser (Apr 26 2023 at 22:46):

Using (-1 : ℤˣ)^n • x as the canonical spelling for "attach an alternating sign to x" means you need fewer lemmas to work with

Jake Levinson (Apr 26 2023 at 22:46):

Oh interesting, OK

Jake Levinson (Apr 26 2023 at 22:47):

Hmm. But odd.neg_one_pow and even.neg_one_pow don't use this spelling, I don't think.

Eric Wieser (Apr 26 2023 at 22:47):

There was a PR at one point that added a dedicated neg_one_pow function to make this type of thing easier, but I argued we already had that function as (-1 : ℤˣ)^n

Eric Wieser (Apr 26 2023 at 22:48):

Jake Levinson said:

Hmm. But odd.neg_one_pow and even.neg_one_pow don't use this spelling, I don't think.

They should work fine, this is exactly why I created docs#has_distrib_neg

Damiano Testa (Apr 26 2023 at 22:51):

Eric Wieser said:

I would suggest writing (-1 : ℤ)^n * a = (-1)^k * a as (-1 : ℤˣ)^n • a = (-1 : ℤˣ)^k • a, and then you can prove it for any ring where 2 is invertible

It is in fact enough that 2 is not a zero-divisor, which holds for . :smile:

Eric Wieser (Apr 26 2023 at 22:52):

I'm slightly worried I have been using invertible 2 when I meant ne_zero 2

Eric Wieser (Apr 26 2023 at 22:52):

2 can only be a zero-divisor if it's zero, right?

Damiano Testa (Apr 26 2023 at 22:53):

ne_zero is not enough: 2 is a zero divisor in zmod 4.

Damiano Testa (Apr 26 2023 at 22:54):

Ultimately, you cook up 2 * x = 0 and you want to deduce that 2x is zero. You get this, but assuming that multiplication by 2 is injective.

Eric Wieser (Apr 26 2023 at 22:55):

And to think I only thought I had to worry about characteristic two...

Damiano Testa (Apr 26 2023 at 22:56):

You should not worry about characteristic 2, you should embrace it.

Eric Wieser (Apr 26 2023 at 22:57):

In the case I have in mind that's actually what I did; but now I can replace my "the usual approach fails in char 2" claim with a stronger "the usual approach fails in even characteristic" :smile:

Damiano Testa (Apr 26 2023 at 22:57):

Nevertheless,

import data.zmod.basic

example : (2 : zmod 4) = - 2 := rfl

Jake Levinson (Apr 26 2023 at 22:59):

Hmm. How would you prove the second lemma below?

import data.polynomial.algebra_map
open polynomial

lemma polynomial.coeff_comp_neg_X {R : Type} [comm_ring R] {p : polynomial R} (k : ) :
  (p.comp (-X)).coeff k = (-1 : ˣ)^k * (p.coeff k) := sorry

lemma coeff_zero_of_odd_add {p : polynomial } {n k : }
  (hp : p.comp (-X) = (-1 : ˣ)^n * p) (hnk : odd (n + k)) :  p.coeff k = 0 :=
begin
  rw polynomial.ext_iff at hp,
  specialize hp k,
  rw polynomial.coeff_comp_neg_X at hp,
  -- hp : ↑-1 ^ k * p.coeff k = (↑-1 ^ n * p).coeff k
  sorry
end

Damiano Testa (Apr 26 2023 at 23:00):

Eric, when you say "even characteristic" I imagine that you are excluding characteristic 0... :upside_down:

Damiano Testa (Apr 26 2023 at 23:01):

Anyway, for these kinds of arguments, I find that "characteristic" does not isolate the right property.

Damiano Testa (Apr 26 2023 at 23:02):

More generally, while "characteristic" is defined for very general (semi)rings, it is only actually useful in situations where you are already quite close to a field.

Eric Wieser (Apr 26 2023 at 23:02):

@Jake Levinson: I guess there's tension here between my claim that (-1 : ℤˣ)^n • is the best spelling for multiplying by -1, and your use case being working with itself

Eric Wieser (Apr 26 2023 at 23:03):

I would suggest stating (hp : p.comp (-X) = (-1)^n * p) as (hp : p.comp (-X) = (-1 : ℤˣ)^n • p) though

Jake Levinson (Apr 26 2023 at 23:04):

Oops, I meant to include that change. The goal I showed (where the coercion from constants to polynomials and the coercion to a unit are both getting in the way) is with the hypotheses adjusted in that way.

Heather Macbeth (Apr 26 2023 at 23:07):

Here's another version!

example (n k : ) (R : Type) [comm_ring R] :
  (-1 : R)^n = (-1 : R)^k  (-1 : R)^(n+k) = 1 :=
begin
  split ; intro h,
  { linear_combination (-1:R) ^ k * h + pow_mul (-1:R) 2 k,
    ring_exp, },
  { linear_combination -(-1:R) ^ n * h + (-1:R) ^ k * pow_mul (-1:R) 2 n,
    ring_exp }
end

Jake Levinson (Apr 26 2023 at 23:07):

Relatedly, (C (-1 : ℤˣ)^k) doesn't work (failed to synthesize type class instance for semiring ℤˣ). But it's preferable to express this as a constant to say that it affects each coefficient individually (polynomial.coeff_C_mul rather than polynomial.coeff_mul).

Jake Levinson (Apr 26 2023 at 23:08):

@Heather Macbeth Cool! ring_exp and linear_combination seem like good tactics to learn.

Eric Wieser (Apr 26 2023 at 23:12):

does docs#polynomial.coeff_smul exist? (edit: yes)

Jake Levinson (Apr 26 2023 at 23:12):

It exists, but it doesn't work on that hypothesis (not sure why)

Eric Wieser (Apr 26 2023 at 23:13):

Can you make a mwe? I don't see where C appears above

Jake Levinson (Apr 26 2023 at 23:17):

I'll show you the approach I had before making this thread - I rewrote -1 to C (-1).

import data.polynomial.algebra_map

open polynomial

lemma polynomial.coeff_comp_neg_X {R : Type} [comm_ring R] {p : polynomial R} (k : ) :
  (p.comp (-X)).coeff k = (-1)^k * (p.coeff k) := sorry

lemma coeff_zero_of_odd_add' {p : polynomial } {n k : }
  (hp : p.comp (-X) = (-1)^n * p) (hnk : odd (n + k)) :
  p.coeff k = 0 :=
begin
  rw polynomial.ext_iff at hp,
  specialize hp k,
  rw [polynomial.coeff_comp_neg_X, (by simp : (-1 : polynomial ) = C (-1)),
       C_pow, coeff_C_mul, mul_eq_mul_right_iff,
      (_ : (-1 : )^k = (-1)^n  (-1 : )^(n+k) = 1), hnk.neg_one_pow] at hp,
  norm_num at hp,
  exact hp,
  sorry -- ⊢ (-1) ^ k = (-1) ^ n ↔ (-1) ^ (n + k) = 1
  -- use one of the supplied proofs :)
end

Eric Wieser (Apr 26 2023 at 23:27):

This gets pretty close: works:

import data.polynomial.algebra_map

open polynomial

lemma polynomial.coeff_comp_neg_X {R : Type} [comm_ring R] {p : polynomial R} (k : ) :
  (p.comp (-X)).coeff k = (-1 : ˣ)^k  (p.coeff k) := sorry

lemma coeff_zero_of_odd_add' {p : polynomial } {n k : }
  (hp : p.comp (-X) = (-1 : ˣ)^n  p) (hnk : odd (n + k)) :
  p.coeff k = 0 :=
begin
  rw polynomial.ext_iff at hp,
  specialize hp k,
  rwa [polynomial.coeff_comp_neg_X, coeff_smul, smul_eq_iff_eq_inv_smul,smul_smul,
    inv_pow, inv_neg', inv_one, pow_add, add_comm, hnk.neg_pow, one_pow, units.neg_smul,
    one_smul, eq_neg_self_iff] at hp,
end

Jake Levinson (Apr 26 2023 at 23:27):

Hmm. Changing the muls to smuls throughout makes it better. But, I'm still ending up with needing to move / deal with minus signs:

import data.polynomial.algebra_map
open polynomial

lemma polynomial.coeff_comp_neg_X {R : Type} [comm_ring R] {p : polynomial R} (k : ) :
  (p.comp (-X)).coeff k = (-1 : ˣ)^k  (p.coeff k) := sorry

lemma coeff_zero_of_odd_add' {p : polynomial } {n k : }
  (hp : p.comp (-X) = (-1 : ˣ)^n  p) (hnk : odd (n + k)) :
  p.coeff k = 0 :=
begin
  rw polynomial.ext_iff at hp,
  specialize hp k,
  rw [polynomial.coeff_comp_neg_X, coeff_smul] at hp,
  -- hp: (-1) ^ k • p.coeff k = (-1) ^ n • p.coeff k
  sorry
end

Eric Wieser (Apr 26 2023 at 23:30):

Annoyingly docs#neg_one_smul doesn't work for the ℤˣ action

Eric Wieser (Apr 26 2023 at 23:34):

Edited above, now sorry-free

Yaël Dillies (Apr 26 2023 at 23:35):

wink wink has_smul_distrib_neg

Eric Wieser (Apr 26 2023 at 23:35):

docs#units.neg_smul is good enough for now

Jake Levinson (Apr 26 2023 at 23:37):

hooray!

Eric Wieser (Apr 26 2023 at 23:41):

I suspect my "(-1 : ℤˣ)^n • sign-application is best sign-application" stance is not one that is universally held, so you might want to see if anyone has a short proof leveraging a different spelling

Jake Levinson (Apr 26 2023 at 23:41):

Yes, that looks like exactly the way the proof should go. So a lesson here for me is to use smul and, for minus signs, the units.

Jake Levinson (Apr 26 2023 at 23:42):

Hm, fair. Still, smul_eq_iff_eq_inv_smul is literally the rearrangement lemma I was asking for. :)

Eric Wieser (Apr 26 2023 at 23:43):

eq_inv_smul_iff₀ would have worked without units, but you'd have to keep reminding lean that 0 \ne -1

Jake Levinson (Apr 26 2023 at 23:56):

To be fair, who among us doesn't forget 0 \ne -1 from time to time?

Kevin Buzzard (Apr 27 2023 at 07:26):

It's false in characteristic 1 (which is odd and still satisfies 2=0, although 2 is a unit anyway)


Last updated: Dec 20 2023 at 11:08 UTC