Does Concrete support operations between negative integers?

I try to modify the code snippet in the Quick Start:

from concrete import fhe

def add(x, y):
    return x + y

compiler = fhe.Compiler(add, {"x": "encrypted", "y": "clear"})

inputset = [(-2000, 2000), (0, 0), (1, 6), (7, 7), (7, 1)]
circuit = compiler.compile(inputset)

x = -1000
y = 1

clear_evaluation = add(x, y)
homomorphic_evaluation = circuit.encrypt_run_decrypt(x, y)

print(clear_evaluation)
print(homomorphic_evaluation)

but I got this:

-999
7193

I don’t know why. :smiling_face_with_tear:

Hey @Yiteng_Peng,

The reason it’s not working is that your function is being evaluated like this:

%0 = x                  # EncryptedScalar<int12>        ∈ [-2000, 7]
%1 = y                  # ClearScalar<uint11>           ∈ [0, 2000]
%2 = add(%0, %1)        # EncryptedScalar<uint4>        ∈ [0, 14]
return %2

Notice the output type of the result, it’s uint4 which means it can’t be negative.

This issue occurs because your inputset is insufficient. If you replace the inputset with:

inputset = [
    (np.random.randint(-2000, 2000), np.random.randint(-2000, 2000))
    for _ in range(100)
]

it’ll work as expected :slightly_smiling_face:

Let us know if it’s okay!

:smiley: Yes! It works well now! Thank you!

You’re welcome!

Let us know if you have more questions :wink:

Just a small doubt here ,see the bounds of the variables has been calculated ,but for the add node the program tracer should have calculated the bounds which is (-2000,2007) and accordingly calculate the bits required by encrypted integer , why were the bounds not generated for the add node?

Evaluation just runs the function on given inputs and record resulting values. If your inputset is:

inputset = [(-2000, 2000), (0, 0), (1, 6), (7, 7), (7, 1)]

Evaluations will be like so:

  • (-2000, 2000)
    • x: -2000
    • y: 2000
    • add: 0
  • (0, 0)
    • x: 0
    • y: 0
    • add: 0
  • (1, 6)
    • x: 1
    • y: 6
    • add: 7
  • (7, 7)
    • x: 7
    • y: 7
    • add: 14
  • (7, 1)
    • x: 7
    • y: 1
    • add: 8

So as you can see the observed values for add are:

  • 0
  • 0
  • 7
  • 14
  • 8

Which is in range:

  • [0, 14]

Hope this helps :slightly_smiling_face:

Oh I see that small catch ,I was rather thinking of the bounds of add node as (min{ add(xi,yj): i ,j in len(inputset), max{ add(xi,yj): i ,jin range len(inputset)})
Thanks for pointing out but had the tracer generated bounds in the way i mentioned above
we could get rid of the issue of calculating representative bounds of input set which might have outliers

It’s easy for addition, but it’s impossible to generalize for complex operations such as matrix multiplications, or convolutions. The issue is that the input can be arbitrarily large, and the search space grows extremely fast.

but every multivariate function can be broken down into primitive operations and table look ups
so for each such node( which is an operand) can be evaluated over each pair generated from all possible ordered pairs which can be formed from input set ,now the elements could be tensors or scalars ,yes this would require us to do many computations but I believe it can be done
Nevertheless I still might be missing something …but as you showed choosing a representative input set works