Zulip Chat Archive

Stream: new members

Topic: Prove Mathemetical Function is Bijective


Chase Wilson (Dec 13 2021 at 18:03):

Hey y'all, I'm trying to prove that this mathematical function is bijective, but I'm really new to theorum proving so I'm honestly just flailing trying to figure things out. So far I have this code, which is incomplete and errors out

import data.int.basic
import tactic.suggest

-- fn next(i: NonZeroU64) -> NonZeroU64 {
--     let i = i.get();
--     let n = (i << 1) ^ (i >> 63 ^ i >> 62 ^ i >> 60 ^ i >> 59) & 1;
--
--     // Safety: I hope I got the math right
--     unsafe { NonZeroU64::new_unchecked(n) }
-- }
namespace prove_prng

open int function

infixr ` << ` : 50 := int.shiftl
infixr ` >> ` := int.shiftr
infixr `  ` := int.lxor
infixr ` & ` : 60 := int.land

def prng (i : ) :  :=
  (i << 1)  ((i >> 63)  (i >> 62)  (i >> 60)  (i >> 59)) & 1

#eval prng 100
#eval prng 0

theorem injective_prng : injective @prng :=
begin
  assume x₁ x₂,
  assume H : prng x₁ = prng x₂,
  repeat { rw [prng] at H },
  induction x₁ using int.induction_on,
  induction x₂ using int.induction_on,
  { trivial },
  { apply int.zero_shiftr -- Error occurs here
  , apply int.zero_shiftl }
  -- There's more cases since I applied induction twice
end
  -- Also tried this:
  -- assume x₁ x₂,
  -- assume H : prng x₁ = prng x₂,
  -- show x₁ = x₂,
  --   from H

theorem surjective_prng : surjective @prng :=
  sorry

theorem bijective_prng : bijective @prng :=
  and.intro injective_prng surjective_prng

end prove_prng

The error it produces is this:

invalid apply tactic, failed to unify
  0 = x₂_i + 1
with
  0 >> ?m_1 = 0
state:
case hz, hp
x₂_i : ,
x₂_ᾰ :
  (0 << 1  (0 >> 63  0 >> 62  0 >> 60  0 >> 59) & 1) =
    (x₂_i << 1  (x₂_i >> 63  x₂_i >> 62  x₂_i >> 60  x₂_i >> 59) & 1) 
  0 = x₂_i,
H :
  (0 << 1  (0 >> 63  0 >> 62  0 >> 60  0 >> 59) & 1) =
    (x₂_i + 1 << 1 
       (x₂_i + 1 >> 63  x₂_i + 1 >> 62  x₂_i + 1 >> 60  x₂_i + 1 >> 59) & 1)
 0 = x₂_i + 1

But honestly, I don't even know if I'm heading in the right direction, any advice?

Eric Rodriguez (Dec 13 2021 at 18:07):

what do you want your tactic state to be? apply basically means "if my goal is B, and I have A → B, then A suffices".

Chase Wilson (Dec 13 2021 at 18:10):

For lack of a better term, I want to "execute the function on the state", I want to remove all the instances of zero being bitshifted in favor of a literal zero

Patrick Johnson (Dec 13 2021 at 18:11):

You probably want simp_rw [int.zero_shiftr, int.zero_shiftl] at * instead of apply int.zero_shiftr, apply int.zero_shiftl

Chase Wilson (Dec 13 2021 at 18:13):

Ahh, thanks! And another thing, I'd like to do this with a representation of 64bit integers but I think int is an unbounded integer, fin looks like what I want (bounded integers) but I can't find bitwise operations over it

Johan Commelin (Dec 13 2021 at 18:13):

Yeah, you'll probably have to implement them yourself

Chase Wilson (Dec 13 2021 at 18:14):

Drat, thank you!

Chase Wilson (Dec 13 2021 at 18:17):

How would I remove duplicate terms from each side of the equation, turning x + 1 = y + 1 into x = y?

Johan Commelin (Dec 13 2021 at 18:18):

congr

Yakov Pechersky (Dec 13 2021 at 18:18):

You can also use the fact that addition is injective. Do you have x + 1 = y + 1 as a goal or as hypothesis?

Patrick Johnson (Dec 13 2021 at 18:18):

or use docs#add_left_inj

Chase Wilson (Dec 13 2021 at 18:19):

Currently it's a hypothesis

Yakov Pechersky (Dec 13 2021 at 18:20):

Right, so congr won't work on a hypothesis. Because that would mean that 1 / 0 = 2 / 0 would imply 1 = 2.

Martin Dvořák (Dec 13 2021 at 18:31):

Fun fact: the term 1 / 0 = 2 / 0 reduces to true.

Patrick Johnson (Dec 13 2021 at 18:36):

Implementing unsigned 64-bit integers and bitwise operations on them is not that hard:

import data.list
import tactic
open list

structure uint64_t : Type :=
(bits : list bool)
(h : bits.length = 64)

def zero : uint64_t :=
{ bits := repeat ff 64,
  h := by rw length_repeat }

def not' (a : uint64_t) : uint64_t :=
{ bits := a.1.map bnot,
  h := by rw [a.2, length_map] }

def and' (a b : uint64_t) : uint64_t :=
{ bits := zip_with band a.1 b.1,
  h := by rw [(_ : min a.1.length b.1.length = 64),
  length_zip_with]; rw [a.2, b.2, min_self] }

def or' (a b : uint64_t) : uint64_t :=
not' (and' (not' a) (not' b))

Yaël Dillies (Dec 13 2021 at 18:43):

Just use vector bool 64


Last updated: Dec 20 2023 at 11:08 UTC