Skip to content

Python frontend behaves strangely in multiplication involving negative numbers. #2496

@zooiik

Description

@zooiik

Version: heir-py 0.0.2

When I use HEIR, sometimes I found the FHE output was not the same as the expected output. After reducing my program, I discovered the root cause was negative numbers:

If I write my program in this way:

from heir import compile
from heir.mlir import I32, Secret

@compile()
def func(x: Secret[I32]):
    res = -1 * x
    return res

if __name__ == "__main__":
    x = 1
    func.setup()
    enc_x = func.encrypt_x(x)
    result_enc = func.eval(enc_x)
    result = func.decrypt_result(result_enc)
    print(f"Expected result: {func.original(x)}")
    print(f"FHE result: {result}")

It will output:

Expected result: -1
FHE result: 255

If I write my program in this way(Only change the value of x and the scalar in func):

from heir import compile
from heir.mlir import I32, Secret

@compile()
def func(x: Secret[I32]):
    res = 1 * x
    return res

if __name__ == "__main__":
    x = -1
    func.setup()
    enc_x = func.encrypt_x(x)
    result_enc = func.eval(enc_x)
    result = func.decrypt_result(result_enc)
    print(f"Expected result: {func.original(x)}")
    print(f"FHE result: {result}")

It will output:

HEIR Error at xxxxxx.py (4): HEIR compilation for function func failed:
running `heir-opt --canonicalize --mlir-to-bgv --mlir-print-debuginfo` produced these errors:
<stdin>:0:0: error: 'builtin.module' op Noise validation failed.

<stdin>:0:0: note: see current operation: 
"builtin.module"() ({
  "func.func"() <{arg_attrs = [{mgmt.mgmt = #mgmt.mgmt<level = 0>, tensor_ext.original_type = #tensor_ext.original_type<originalType = i32, layout = <map = (d0) -> (d0 mod 1024), alignment = <in = [], out = [1024], insertedDims = [0]>>>}], function_type = (!secret.secret<tensor<1024xi32>>) -> !secret.secret<tensor<1024xi32>>, res_attrs = [{mgmt.mgmt = #mgmt.mgmt<level = 0>, tensor_ext.original_type = #tensor_ext.original_type<originalType = i32, layout = <map = (d0) -> (d0 mod 1024), alignment = <in = [], out = [1024], insertedDims = [0]>>>}], sym_name = "func"}> ({
  ^bb0(%arg2: !secret.secret<tensor<1024xi32>> loc("xxxxxx.py":4:0)):
    "func.return"(%arg2) : (!secret.secret<tensor<1024xi32>>) -> () loc("xxxxxx.py":4:0)
  }) : () -> () loc("xxxxxx.py":4:0)
  "func.func"() <{function_type = (i32) -> !secret.secret<tensor<1024xi32>>, res_attrs = [{mgmt.mgmt = #mgmt.mgmt<level = 0>}], sym_name = "func__encrypt__arg0"}> ({
  ^bb0(%arg1: i32 loc("<stdin>":0:0)):
    %3 = "tensor.splat"(%arg1) : (i32) -> tensor<1024xi32> loc("<stdin>":0:0)
    %4 = "secret.conceal"(%3) {mgmt.mgmt = #mgmt.mgmt<level = 0>} : (tensor<1024xi32>) -> !secret.secret<tensor<1024xi32>> loc("<stdin>":0:0)
    "func.return"(%4) : (!secret.secret<tensor<1024xi32>>) -> () loc("<stdin>":0:0)
  }) {client.enc_func = {func_name = "func", index = 0 : i64}} : () -> () loc("<stdin>":0:0)
  "func.func"() <{arg_attrs = [{mgmt.mgmt = #mgmt.mgmt<level = 0>}], function_type = (!secret.secret<tensor<1024xi32>>) -> i32, sym_name = "func__decrypt__result0"}> ({
  ^bb0(%arg0: !secret.secret<tensor<1024xi32>> loc("<stdin>":0:0)):
    %0 = "arith.constant"() <{value = 0 : index}> : () -> index loc(unknown)
    %1 = "secret.reveal"(%arg0) : (!secret.secret<tensor<1024xi32>>) -> tensor<1024xi32> loc("<stdin>":0:0)
    %2 = "tensor.extract"(%1, %0) : (tensor<1024xi32>, index) -> i32 loc("<stdin>":0:0)
    "func.return"(%2) : (i32) -> () loc("<stdin>":0:0)
  }) {client.dec_func = {func_name = "func", index = 0 : i64}} : () -> () loc("<stdin>":0:0)
}) {bgv.schemeParam = #bgv.scheme_param<logN = 11, Q = [1073153], P = [1093633], plaintextModulus = 65537>, scheme.bgv} : () -> () loc("<stdin>":0:0)

I wonder if these two are bugs?

Metadata

Metadata

Labels

python frontendIssues related to the python frontend

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions