Division in Concrete

Hi, I’m trying to do a simple division between two inputted values in Zama Concrete - I’ve tried a variety of things, but have been getting the following error:

RuntimeError: A subgraph within the function you are trying to compile cannot be fused because it has multiple input nodes

Is there a way to do this?

Hi @ac2421,

Division between two encrypted values are not officially supported.

Also, subgraphs (e.g., generated from float operations) cannot use multiple encrypted inputs.

What you can do is either fhe.multivariate(lambda x, y: x // y)(x, y) which would work when x.bit_width + y.bit_width <= 16.

Or, you can approximate encrypted division using other primitive operations:

u = 1000 / y
v = (x * u) / 1000
return v

We’re working on an extension to make it easier in the future! For now, can you try with one of these workarounds?

2 Likes

Hi, replying to this thread because I had the same issue. I tried both of your workarounds and both of them unfortunately yielded an error.

Using fhe.multivariate yielded this:
“AssertionError: Do not support this type of operation between encrypted tensors”

Using the primitive operations got me this:
“ValueError: The following ONNX operators are required to convert the torch model to numpy but are not currently implemented: Reciprocal.”

Is it a mistake on my end or is division between two encrypted values currently not implemented?

Hey, could you share your code, please?

Thank you so much! I tried both as part of a more complex function, but it yields a “NoParametersFound” error for both - I’m not sure if this is related to the division, or something else, although my function seems to work fine apart from the division part - I’d appreciate any help.

Here the best would be to share your code. Could you send us something which is complete, such that we can try to reproduce on our side and help?

Hi, thanks for the quick reply. Here is some minimal code to reproduce the error I got:

import torch
import torch.nn as nn
from concrete.ml.torch.compile import compile_brevitas_qat_model

class SimpleDivide(nn.Module):

    def __init__(self, bit_width):
        super().__init__()
        self.id1 = qnn.QuantIdentity(bit_width=bit_width)
        self.id2 = qnn.QuantIdentity(bit_width=bit_width)

    def forward(self, x, y):
        """Forward pass of the model."""
        x = self.id1(x)
        y = self.id2(y)
        u = 1000 / y
        v = (x * u) / 1000
        return v

model = SimpleDivide(bit_width=8)
tensor_x = torch.randn(1, 100, 200)
tensor_y = torch.ones(1, 100, 200) * 10
encrypted = compile_brevitas_qat_model(
    model, (tensor_x, tensor_y), verbose=True, n_bits=8
)

This yields

ValueError: The following ONNX operators are required to convert the torch model to numpy but are not currently implemented: Reciprocal.

And this code:

from concrete import fhe


class SimpleDivideFHE(nn.Module):

    def __init__(self, bit_width):
        super().__init__()
        self.id1 = qnn.QuantIdentity(bit_width=bit_width)
        self.id2 = qnn.QuantIdentity(bit_width=bit_width)

    def forward(self, x, y):
        """Forward pass of the model."""
        x = self.id1(x)
        y = self.id2(y)
        v = fhe.multivariate(lambda x, y: x // y)(x, y)
        return v

model = SimpleDivideFHE(bit_width=4)
tensor_x = torch.randn(1, 100, 200)
tensor_y = torch.ones(1, 100, 200) * 10
encrypted = compile_brevitas_qat_model(
    model, (tensor_x, tensor_y), verbose=True, n_bits=4
)

yields:

AssertionError: Do not support this type of operation between encrypted tensors

I am using Concrete-ML 1.5.0.

Thank you for taking the time to look into this! Let me know if you need more details.

Hello again @Tu-Duyen_Nguyen,
Both of the errors you get are coming from Concrete ML layers, not Concrete ! They are directly linked to the fact that we currently do not support enc * enc or enc / enc operations in Concrete ML yet, as we explained it on discord. However, it is an on-going work so we’ll update you as soon as it’s available !

Okay, thank you for your quick answer! I had confused the errors from Concrete ML and from Concrete. Looking forward to the next update!

@Tu-Duyen_Nguyen : if what you want now is dividing in FHE, it already works fine in Concrete:

from concrete import fhe
import numpy


def div(x, y):
    return fhe.multivariate(lambda x, y: x // y)(x, y)


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

w = 4
inputset = [(numpy.random.randint(1, 2**w), numpy.random.randint(1, 2**w)) for _ in range(100)]

print(f"Compilation...")
circuit = compiler.compile(inputset)

print(f"Key generation...")
circuit.keygen()

print(f"Homomorphic evaluation...")
for _ in range(20):
    sample = (numpy.random.randint(1, 2**w), numpy.random.randint(1, 2**w))
    encrypted_x, encrypted_y = circuit.encrypt(*sample)
    encrypted_result = circuit.run(encrypted_x, encrypted_y)
    result = circuit.decrypt(encrypted_result)

    print(f"Division {sample[0]} // {sample[1]} returns {result} in FHE")
    assert result == div(*sample)

Cheers