Documentation

SSA.Projects.FullyHomomorphicEncryption.Basic

noncomputable def f (q : ) (n : ) :
Equations
  • f q n = Polynomial.X ^ 2 ^ n + 1
Instances For
    def ZMod.toInt (q : ) (x : ZMod q) :
    Equations
    Instances For
      def ZMod.toFin (q : ) [Fact (q > 1)] (x : ZMod q) :
      Fin q
      Equations
      Instances For
        @[simp]
        theorem ZMod.toInt_inj (q : ) [Fact (q > 1)] {x : ZMod q} {y : ZMod q} :
        def ZMod.toInt_zero_iff_zero (q : ) [Fact (q > 1)] (x : ZMod q) :
        x = 0 ZMod.toInt q x = 0
        Equations
        • =
        Instances For
          instance instNontrivialZMod_sSA (q : ) [Fact (q > 1)] :
          Equations
          • =
          theorem f_deg_eq (q : ) [Fact (q > 1)] (n : ) :
          (f q n).degree = 2 ^ n

          Charaterizing f: f has degree 2^n

          theorem f_monic (q : ) [Fact (q > 1)] (n : ) :
          (f q n).Monic

          Charaterizing f: f is monic

          @[reducible, inline]
          abbrev R (q : ) (n : ) :

          The basic ring of interest in this dialect is R q n which corresponds to the ring ℤ/qℤ[X]/(X^(2^n) + 1).

          Equations
          Instances For
            @[reducible, inline]
            abbrev R.fromPoly {q : } {n : } :

            Canonical epimorphism / quotient map: ZMod q[X] ->*+ R q n

            Equations
            Instances For
              theorem R.surjective_fromPoly (q : ) (n : ) :
              Function.Surjective R.fromPoly

              fromPoly, the canonical epi from ZMod q[X] →*+ R q n is surjective

              theorem R.fromPoly_representatitive' (q : ) (n : ) (a : R q n) :
              R.fromPoly (R.representative' q n a) = a

              A concrete version that shows that mapping into the ideal back from the representative produces the representative' NOTE: Lean times out if I use the abbreviation R.fromPoly for unclear reasons!

              theorem R.fromPoly_representatitive'_toFun (q : ) (n : ) (a : R q n) :
              (↑R.fromPoly).toFun (R.representative' q n a) = a
              noncomputable def R.representative (q : ) (n : ) :
              R q nPolynomial (ZMod q)

              The representative of a : R q n is the (unique) polynomial p : ZMod q[X] of degree < 2^n such that R.fromPoly p = a.

              Equations
              Instances For
                @[simp]
                theorem R.fromPoly_kernel_eq_zero (q : ) (n : ) (x : Polynomial (ZMod q)) :
                R.fromPoly (f q n * x) = 0
                @[simp]
                theorem R.fromPoly_representative (q : ) (n : ) [Fact (q > 1)] (a : R q n) :
                R.fromPoly (R.representative q n a) = a

                R.representative is in fact a representative of the equivalence class.

                theorem R.fromPoly_rep'_eq_ideal (q : ) (n : ) (a : Polynomial (ZMod q)) :
                iIdeal.span {f q n}, R.representative' q n (R.fromPoly a) = a + i

                Characterization theorem for any potential representative (in terms of ideals). For an a : (ZMod q)[X], the representative of its equivalence class is just a + i for some i ∈ (Ideal.span {f q n}).

                theorem R.exists_representative_fromPoly_eq_mul_add (q : ) (n : ) (a : Polynomial (ZMod q)) :
                ∃ (k : Polynomial (ZMod q)), R.representative' q n (R.fromPoly a) = k * f q n + a

                Characterization theorem for any potential representative (in terms of elements). For an a : (ZMod q)[X], the representative of its equivalence class is a concrete element of the form a + k * (f q n) for some k ∈ (ZMod q)[X].

                theorem R.representatitive'_toFun_fromPoly_eq_element (q : ) (n : ) (a : Polynomial (ZMod q)) :
                ∃ (k : Polynomial (ZMod q)), R.representative' q n ((↑R.fromPoly).toFun a) = a + k * f q n

                A theorem similar to R.fromPoly_rep'_eq_element but uses fromPoly.toFun to be more deterministic, as the toFun sometimes sneaks in due to coercions.

                The representative of 0 wil live in the ideal of {f q n}. To show that such an element is a multiple of {f q n}, use Ideal.mem_span_singleton'

                theorem R.representative'_zero_elem (q : ) (n : ) :
                ∃ (k : Polynomial (ZMod q)), R.representative' q n 0 = k * f q n

                The representatiatve of 0 is a multiple of f q n.

                theorem neg_modByMonic (q : ) (p : Polynomial (ZMod q)) (mod : Polynomial (ZMod q)) :
                -p %ₘ mod = -(p %ₘ mod)

                pushing and pulling negation through mod

                @[simp]
                theorem sub_modByMonic (q : ) (a : Polynomial (ZMod q)) (b : Polynomial (ZMod q)) (mod : Polynomial (ZMod q)) :
                (a - b) %ₘ mod = a %ₘ mod - b %ₘ mod

                %ₘ is a subtraction homomorphism (obviously)

                theorem R.representative_zero (q : ) (n : ) [Fact (q > 1)] :

                Representative of (0 : R) is (0 : Z/qZ[X])

                theorem R.representative_fromPoly_toFun (q : ) (n : ) [Fact (q > 1)] (a : Polynomial (ZMod q)) :
                R.representative q n ((↑R.fromPoly).toFun a) = a %ₘ f q n

                Characterization theorem for the representative. Taking the representative of the equivalence class of a polynomial a : (ZMod q)[X] is the same as taking the remainder of a modulo f q n.

                theorem R.representative_fromPoly (q : ) (n : ) [Fact (q > 1)] (a : Polynomial (ZMod q)) :
                R.representative q n (R.fromPoly a) = a %ₘ f q n
                @[simp]
                theorem R.representative_add (q : ) (n : ) [Fact (q > 1)] (a : R q n) (b : R q n) :

                Representative is an additive homomorphism

                @[simp]
                theorem R.representative_mul (q : ) (n : ) [Fact (q > 1)] (a : R q n) (b : R q n) :

                Representative is an multiplicative homomorphism upto modulo

                @[simp]
                theorem R.representative_fromPoly_eq (q : ) (n : ) [Fact (q > 1)] (x : Polynomial (ZMod q)) (DEGREE : x.degree < (f q n).degree) :
                R.representative q n (R.fromPoly x) = x

                Another characterization of the representative: if the degree of x is less than that of (f q n), then we recover the same polynomial.

                theorem R.rep_degree_lt_n (q : ) (n : ) [Fact (q > 1)] (a : R q n) :
                (R.representative q n a).degree < 2 ^ n

                The representative of a : R q n is the (unique) reperesntative with degree < 2^n.

                theorem R.representative_degree_lt_f_degree (q : ) (n : ) [Fact (q > 1)] (a : R q n) :
                (R.representative q n a).degree < (f q n).degree

                The representative a : R q n is the (unique) representative with degree less than degree of f.

                noncomputable def R.repLength {q : } {n : } (a : R q n) :
                Equations
                Instances For
                  theorem R.repLength_leq_representative_degree_plus_1 {q : } {n : } (a : R q n) :
                  a.repLength (R.representative q n a).natDegree + 1
                  theorem R.repLength_lt_n_plus_1 {q : } {n : } [Fact (q > 1)] (a : R q n) :
                  a.repLength < 2 ^ n + 1
                  noncomputable def R.coeff {q : } {n : } (a : R q n) (i : ) :

                  This function gets the ith coefficient of the polynomial representative (with degree < 2^n) of an element a : R q n. Note that this is not invariant under the choice of representative.

                  Equations
                  Instances For
                    noncomputable def R.monomial {q : } {n : } (c : ZMod q) (i : ) :
                    R q n

                    R.monomial i c is the equivalence class of the monomial c * X^i in R q n.

                    Equations
                    Instances For
                      noncomputable def R.slice {q : } {n : } (a : R q n) (startIdx : ) (endIdx : ) :
                      R q n

                      Given an equivalence class of polynomials a : R q n with representative p = p_0 + p_1 X + ... + p_{2^n - 1} X^{2^n - 1}, R.slice a startIdx endIdx yields the equivalence class of the polynomial `p_{startIdx}*X^{startIdx} + p_{startIdx + 1} X^{startIdx + 1} + ... + p_{endIdx

                      • 1} X^{endIdx - 1}` Note that this is not invariant under the choice of representative.
                      Equations
                      • One or more equations did not get rendered due to their size.
                      Instances For
                        noncomputable def R.leadingTerm {q : } {n : } (a : R q n) :
                        R q n
                        Equations
                        Instances For
                          noncomputable def R.fromTensor {q : } {n : } (coeffs : List ) :
                          R q n
                          Equations
                          Instances For
                            theorem R.fromTensor_snoc (q : ) (n : ) (c : ) (cs : List ) :
                            R.fromTensor (cs ++ [c]) = R.fromTensor cs + R.monomial (↑c) cs.length
                            noncomputable def R.fromTensor' {q : } (coeffs : List ) :

                            A definition of fromTensor that operates on Z/qZ[X], to provide a relationship between R and Z/qZ[X] as the polynomial in R is built.

                            Equations
                            Instances For
                              theorem R.fromTensor_eq_fromTensor'_fromPoly_aux {q : } {n : } {k : } (coeffs : List ) (rp : R q n) (p : Polynomial (ZMod q)) (H : R.fromPoly p = rp) :
                              List.foldl (fun (res : R q n) (x : × ) => match x with | (i, c) => res + R.monomial (↑c) i) rp (List.enumFrom k coeffs) = R.fromPoly (List.foldl (fun (res : Polynomial (ZMod q)) (x : × ) => match x with | (i, c) => res + (Polynomial.monomial i) c) p (List.enumFrom k coeffs))
                              theorem R.fromTensor_eq_fromTensor'_fromPoly {q : } {n : } [Fact (q > 1)] {coeffs : List } :
                              R.fromTensor coeffs = R.fromPoly (R.fromTensor' coeffs)

                              fromTensor = R.fromPoly ∘ fromTensor'. This permits reasoning about fromTensor directly on the polynomial ring.

                              noncomputable def R.fromTensorFinsupp (q : ) (coeffs : List ) :

                              an equivalent implementation of fromTensor that uses Finsupp to enable reasoning about values using mathlib's notions of support, coefficients, etc.

                              Equations
                              Instances For
                                theorem Polynomial.degree_toFinsupp {M : Type u_1} [Semiring M] [DecidableEq M] (xs : List M) :
                                { toFinsupp := xs.toFinsupp }.degree xs.length
                                theorem R.fromTensorFinsupp_degree (q : ) (coeffs : List ) :
                                (R.fromTensorFinsupp q coeffs).degree coeffs.length

                                degree of fromTensorFinsupp is at most the length of the coefficient list.

                                theorem R.fromTensorFinsupp_coeffs {q : } {i : } (coeffs : List ) :
                                (R.fromTensorFinsupp q coeffs).coeff i = (coeffs.getD i 0)

                                the ith coefficient of fromTensorFinsupp is a coercion of the 'coeffs' into the right list.

                                theorem R.fromTensorFinsupp_concat_finsupp {q : } (c : ) (cs : List ) :
                                R.fromTensorFinsupp q (cs ++ [c]) = R.fromTensorFinsupp q cs + { toFinsupp := Finsupp.single cs.length c }

                                concatenating into a fromTensorFinsupp is the same as adding a ⟨Finsupp.single⟩.

                                concatenating into a fromTensorFinsupp is the same as adding a monomial.

                                theorem R.fromTensor_eq_fromTensorFinsupp_fromPoly {q : } {n : } {coeffs : List } :
                                R.fromTensor coeffs = R.fromPoly (R.fromTensorFinsupp q coeffs)

                                show that fromTensor is the same as fromPolyfromTensorFinsupp.

                                theorem coeff_modByMonic_degree_lt_f {q : } {n : } [Fact (q > 1)] {i : } (p : Polynomial (ZMod q)) (DEGREE : p.degree < (f q n).degree) :
                                (p %ₘ f q n).coeff i = p.coeff i

                                coeff (p % f) = coeff p if the degree of p is less than the degree of f.

                                @[simp]
                                theorem R.coeff_fromPoly {q : } {n : } [Fact (q > 1)] (p : Polynomial (ZMod q)) :
                                (R.fromPoly p).coeff = (p %ₘ f q n).coeff

                                The coefficient of fromPoly p is the coefficient of p modulo f q n.

                                theorem R.coeff_fromTensor {q : } {n : } [Fact (q > 1)] {i : } (tensor : List ) (htensorlen : tensor.length < 2 ^ n) :
                                (R.fromTensor tensor).coeff i = (tensor.getD i 0)

                                The coefficient of fromTensor is the same as the values available in the tensor input.

                                noncomputable def R.toTensor {q : } {n : } (a : R q n) :

                                Converts an element of R into a tensor (modeled as a List Int) with the representatives of the coefficients of the representative. The length of the list is the degree of the representative + 1.

                                Equations
                                Instances For
                                  theorem R.toTensor_length {q : } {n : } (a : R q n) :
                                  a.toTensor.length = a.repLength

                                  The length of the tensor R.toTensor a equals a.repLength

                                  noncomputable def R.toTensor' {q : } {n : } (a : R q n) :

                                  Converts an element of R into a tensor (modeled as a List Int) with the representatives of the coefficients of the representative. The length of the list is the degree of the generator polynomial f + 1.

                                  Equations
                                  Instances For
                                    def trimTensor (tensor : List ) :
                                    Equations
                                    Instances For
                                      inductive Ty (q : ) (n : ) :

                                      We define the base type of the representation, which encodes both natural numbers and elements in the ring R q n (which in FHE are sometimes called 'polynomials' in allusion to R.representative).

                                      In this context, `Tensor is a 1-D tensor, which we model here as a list of integers.

                                      • index: {q n : } → Ty q n
                                      • integer: {q n : } → Ty q n
                                      • tensor: {q n : } → Ty q n
                                      • polynomialLike: {q n : } → Ty q n
                                      Instances For
                                        instance instDecidableEqTy :
                                        {q n : } → DecidableEq (Ty q n)
                                        Equations
                                        • instDecidableEqTy = decEqTy✝
                                        instance instReprTy :
                                        {q n : } → Repr (Ty q n)
                                        Equations
                                        • instReprTy = { reprPrec := reprTy✝ }
                                        instance instInhabitedTy {q : } {n : } :
                                        Equations
                                        • instInhabitedTy = { default := Ty.index }
                                        instance instTyDenoteTy {q : } {n : } :
                                        TyDenote (Ty q n)
                                        Equations
                                        • instTyDenoteTy = { toType := fun (x : Ty q n) => match x with | Ty.index => | Ty.integer => | Ty.tensor => List | Ty.polynomialLike => R q n }
                                        inductive Op (q : ) (n : ) :

                                        The operation type of the Poly dialect. Operations are parametrized by the two parameters p and n that characterize the ring R q n. We parametrize the entire type by these since it makes no sense to mix operations in different rings.

                                        • add: {q n : } → Op q n
                                        • sub: {q n : } → Op q n
                                        • mul: {q n : } → Op q n
                                        • mul_constant: {q n : } → Op q n
                                        • leading_term: {q n : } → Op q n
                                        • monomial: {q n : } → Op q n
                                        • monomial_mul: {q n : } → Op q n
                                        • from_tensor: {q n : } → Op q n
                                        • to_tensor: {q n : } → Op q n
                                        • const: {q n : } → R q nOp q n
                                        • const_int: {q n : } → Op q n
                                        • const_idx: {q n : } → Op q n
                                        Instances For
                                          @[reducible, inline]
                                          abbrev FHE (q : ) (n : ) [Fact (q > 1)] :

                                          FHE is the dialect for fully homomorphic encryption

                                          Equations
                                          Instances For
                                            @[reducible]
                                            def Op.sig {q : } {n : } :
                                            Op q nList (Ty q n)
                                            Equations
                                            • Op.add.sig = [Ty.polynomialLike, Ty.polynomialLike]
                                            • Op.sub.sig = [Ty.polynomialLike, Ty.polynomialLike]
                                            • Op.mul.sig = [Ty.polynomialLike, Ty.polynomialLike]
                                            • Op.mul_constant.sig = [Ty.polynomialLike, Ty.integer]
                                            • Op.leading_term.sig = [Ty.polynomialLike]
                                            • Op.monomial.sig = [Ty.integer, Ty.index]
                                            • Op.monomial_mul.sig = [Ty.polynomialLike, Ty.index]
                                            • Op.from_tensor.sig = [Ty.tensor]
                                            • Op.to_tensor.sig = [Ty.polynomialLike]
                                            • (Op.const c).sig = []
                                            • (Op.const_int c).sig = []
                                            • (Op.const_idx i).sig = []
                                            Instances For
                                              @[reducible]
                                              def Op.outTy {q : } {n : } :
                                              Op q nTy q n
                                              Equations
                                              • Op.add.outTy = Ty.polynomialLike
                                              • Op.sub.outTy = Ty.polynomialLike
                                              • Op.mul.outTy = Ty.polynomialLike
                                              • Op.mul_constant.outTy = Ty.polynomialLike
                                              • Op.leading_term.outTy = Ty.polynomialLike
                                              • Op.monomial.outTy = Ty.polynomialLike
                                              • Op.monomial_mul.outTy = Ty.polynomialLike
                                              • Op.from_tensor.outTy = Ty.polynomialLike
                                              • (Op.const c).outTy = Ty.polynomialLike
                                              • Op.to_tensor.outTy = Ty.tensor
                                              • (Op.const_int c).outTy = Ty.integer
                                              • (Op.const_idx i).outTy = Ty.index
                                              Instances For
                                                @[reducible]
                                                def Op.signature {q : } {n : } :
                                                Op q nSignature (Ty q n)
                                                Equations
                                                • o.signature = { sig := o.sig, regSig := [], outTy := o.outTy, effectKind := EffectKind.pure }
                                                Instances For
                                                  instance instDialectSignatureFHE {q : } [Fact (q > 1)] {n : } :
                                                  Equations
                                                  • instDialectSignatureFHE = { signature := Op.signature }
                                                  noncomputable instance instDialectDenoteFHE {q : } [Fact (q > 1)] {n : } :
                                                  Equations
                                                  • One or more equations did not get rendered due to their size.