Is there any example about how to use EvalSumRows/EvalSumCols?

If not, does anyone can give an example here?

Here is a sample code (and its output) of how to use EvalSum, EvalSumRows, and EvalSumCols.
The code assumes you are using CKKS. Note that this code is for illustrative purposes only and does not use standard security parameters.

int main() {

    uint32_t multDepth = 1;
    uint32_t scaleModSize = 50;
    uint32_t N = 16;
    uint32_t batchSize = N/2;
    CCParams<CryptoContextCKKSRNS> parameters;
    parameters.SetMultiplicativeDepth(multDepth);
    parameters.SetScalingModSize(scaleModSize);
    parameters.SetBatchSize(batchSize);
    parameters.SetSecurityLevel(HEStd_NotSet);
    parameters.SetRingDim(N);
    
    CryptoContext<DCRTPoly> cc = GenCryptoContext(parameters);

    // Enable the features that you wish to use
    cc->Enable(PKE);
    cc->Enable(KEYSWITCH);
    cc->Enable(LEVELEDSHE);
    cc->Enable(ADVANCEDSHE);

    auto keys = cc->KeyGen();

    // Encoding and encryption of inputs

    // Inputs
    std::vector<double> x1 = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.5};
    std::vector<double> mat1 = {1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0};
    std::vector<double> mat2 = {1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0};                            
    uint32_t rowSize = 4;

    // Encoding as plaintexts
    Plaintext ptxt1 = cc->MakeCKKSPackedPlaintext(x1);
    Plaintext ptxtMat1 = cc->MakeCKKSPackedPlaintext(mat1);
    Plaintext ptxtMat2 = cc->MakeCKKSPackedPlaintext(mat2);

    std::cout << "Input x1: " << ptxt1 << std::endl;
    std::cout << "Input mat1: " << ptxtMat1 << std::endl;
    std::cout << "Input mat2: " << ptxtMat2 << std::endl;

    // Encrypt the encoded vectors
    std::cout << "Encryption\n";
    auto c1 = cc->Encrypt(keys.publicKey, ptxt1);
    auto ctMat1 = cc->Encrypt(keys.publicKey, ptxtMat1);
    auto ctMat2 = cc->Encrypt(keys.publicKey, ptxtMat2);

    std::cout << "Generating EvalSum* keys\n";
    cc->EvalSumKeyGen(keys.secretKey, keys.publicKey);
    auto evalSumRowKeys = cc->EvalSumRowsKeyGen(keys.secretKey, nullptr, rowSize);
    auto evalSumColKeys = cc->EvalSumColsKeyGen(keys.secretKey);

    // Evaluation
    std::cout << "EvalSum\n";    
    auto ctSum = cc->EvalSum(c1, batchSize);

    std::cout << "EvalSumRows\n";
    auto ctRowsSum = cc->EvalSumRows(ctMat1, rowSize, *evalSumRowKeys);

    std::cout << "EvalSumCols\n";
    auto ctColsSum = cc->EvalSumCols(ctMat2, rowSize, *evalSumColKeys);

    // Decryption and output
    std::cout << "Decryption\n";
    
    Plaintext result;
    std::cout.precision(8);
    std::cout << std::endl << "Results of homomorphic evaluations: " << std::endl;

    // Decrypt
    cc->Decrypt(keys.secretKey, c1, &result);
    result->SetLength(batchSize);
    std::cout << "x1 = " << result;
    std::cout << "Estimated precision in bits: " << result->GetLogPrecision() << std::endl;

    cc->Decrypt(keys.secretKey, ctSum, &result);
    result->SetLength(batchSize);
    std::cout << "sum = " << result;

    cc->Decrypt(keys.secretKey, ctRowsSum, &result);
    result->SetLength(batchSize);
    std::cout << "sum Rows: " << result;

    cc->Decrypt(keys.secretKey, ctColsSum, &result);
    result->SetLength(batchSize);
    std::cout << "sum Cols: " << result;

    return 0;
}

Output

Input x1: (1, 1, 1, 1, 1, 1, 1, 1.5,  ... ); Estimated precision: 50 bits

Input mat1: (1, 1, 1, 1, 2, 2, 2, 2,  ... ); Estimated precision: 50 bits

Input mat2: (1, 1, 1, 1, 2, 2, 2, 2,  ... ); Estimated precision: 50 bits

Encryption
Generating EvalSum* keys
EvalSum
EvalSumRows
EvalSumCols
Decryption

Results of homomorphic evaluations: 
x1 = (1, 1, 1, 1, 1, 1, 1, 1.5,  ... ); Estimated precision: 44 bits
Estimated precision in bits: 44
sum = (8.5, 8.5, 8.5, 8.5, 8.5, 8.5, 8.5, 8.5,  ... ); Estimated precision: 43 bits
sum Rows: (3, 3, 3, 3, 3, 3, 3, 3,  ... ); Estimated precision: 44 bits
sum Cols: (4, 4, 4, 4, 8, 8, 8, 8,  ... ); Estimated precision: 44 bits

Thank you very much. :smiley:

Does the batch size of EvalSum has some limitations?

In above case, when I change N=64, and give x1 18 elements as follows:
std::vector x1 = {1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.5, 1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.5, 1};

Then change the batch size parameter of EvalSum to 9, the result is not correct.
auto ctSum = cc->EvalSum(c1, 9);

Result:
sum = (16.5, 17, 17, 16, 15, 14, 13, 12, 10.5, 9.5, 8.5, 7.5, 6.5, 5.5, 4.5, 3.5, 2.5, 2, 2, 3, 4, 5, 6, 7, 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, … ); Estimated precision: 46 bits

Could you tell me why? Thanks.

Besides, when I use “EvalSumColsKeyGen” with “rowSize” as 256(batch size is 2048), I get “The number of slots cannot be smaller than value vector size” error messages.
I have no idea about this since I’m sure the slots of the cipher text is equal to the batch size. Could you help with this? thanks.

The batch size should be a power of two (the data should be padded to a power of two).

In CKKS, the maximum number of slots is ring dimension / 2. The ring dimension in the example is 16.

When performing the EvalSumRow or EvalSumColumn operations, is there any level consumption on the ciphertext? I am curious about the internal operations that occur when calling the EvalSumRow (or Column) function, such as rotations and masking.

  • EvalSumRows does not consume any levels (it includes only additions and rotations)
  • EvalSumCols consumes one level (multiplies by a binary mask internally)

Thank you very much!