Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
hoang-gia.nguyen
dap-pattern-search-standalone
Commits
c6c9d2f5
Commit
c6c9d2f5
authored
May 04, 2023
by
Hoang Gia NGUYEN
Browse files
first commit
parent
fe2d6195
Pipeline
#60
failed with stages
in 0 seconds
Changes
275
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
5313 additions
and
0 deletions
+5313
-0
bigpiseal3.5.1/dotnet/tests/SecretKeyTests.cs
bigpiseal3.5.1/dotnet/tests/SecretKeyTests.cs
+102
-0
bigpiseal3.5.1/dotnet/tests/SerializationTests.cs
bigpiseal3.5.1/dotnet/tests/SerializationTests.cs
+112
-0
bigpiseal3.5.1/dotnet/tests/TestAssemblyCleanup.cs
bigpiseal3.5.1/dotnet/tests/TestAssemblyCleanup.cs
+20
-0
bigpiseal3.5.1/dotnet/tests/Utilities.cs
bigpiseal3.5.1/dotnet/tests/Utilities.cs
+90
-0
bigpiseal3.5.1/native/examples/1_bfv_basics.cpp
bigpiseal3.5.1/native/examples/1_bfv_basics.cpp
+428
-0
bigpiseal3.5.1/native/examples/2_encoders.cpp
bigpiseal3.5.1/native/examples/2_encoders.cpp
+475
-0
bigpiseal3.5.1/native/examples/3_levels.cpp
bigpiseal3.5.1/native/examples/3_levels.cpp
+338
-0
bigpiseal3.5.1/native/examples/4_ckks_basics.cpp
bigpiseal3.5.1/native/examples/4_ckks_basics.cpp
+312
-0
bigpiseal3.5.1/native/examples/5_rotation.cpp
bigpiseal3.5.1/native/examples/5_rotation.cpp
+200
-0
bigpiseal3.5.1/native/examples/6_serialization.cpp
bigpiseal3.5.1/native/examples/6_serialization.cpp
+415
-0
bigpiseal3.5.1/native/examples/7_performance.cpp
bigpiseal3.5.1/native/examples/7_performance.cpp
+728
-0
bigpiseal3.5.1/native/examples/ANN/v1/ANN_decrypt_result_v1.cpp
...eal3.5.1/native/examples/ANN/v1/ANN_decrypt_result_v1.cpp
+139
-0
bigpiseal3.5.1/native/examples/ANN/v1/ANN_decrypt_v1.cpp
bigpiseal3.5.1/native/examples/ANN/v1/ANN_decrypt_v1.cpp
+78
-0
bigpiseal3.5.1/native/examples/ANN/v1/ANN_encrypt_v1.cpp
bigpiseal3.5.1/native/examples/ANN/v1/ANN_encrypt_v1.cpp
+53
-0
bigpiseal3.5.1/native/examples/ANN/v1/ANN_evaluate_v1 copy.cpp
...seal3.5.1/native/examples/ANN/v1/ANN_evaluate_v1 copy.cpp
+515
-0
bigpiseal3.5.1/native/examples/ANN/v1/ANN_evaluate_v1.cpp
bigpiseal3.5.1/native/examples/ANN/v1/ANN_evaluate_v1.cpp
+568
-0
bigpiseal3.5.1/native/examples/ANN/v1/ANN_genkey_v1.cpp
bigpiseal3.5.1/native/examples/ANN/v1/ANN_genkey_v1.cpp
+34
-0
bigpiseal3.5.1/native/examples/ANN/v1/CMakeLists.txt
bigpiseal3.5.1/native/examples/ANN/v1/CMakeLists.txt
+171
-0
bigpiseal3.5.1/native/examples/ANN/v1/csv_api.cpp
bigpiseal3.5.1/native/examples/ANN/v1/csv_api.cpp
+497
-0
bigpiseal3.5.1/native/examples/ANN/v1/csv_api.h
bigpiseal3.5.1/native/examples/ANN/v1/csv_api.h
+38
-0
No files found.
Too many changes to show.
To preserve performance only
275 of 275+
files are displayed.
Plain diff
Email patch
bigpiseal3.5.1/dotnet/tests/SecretKeyTests.cs
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using
Microsoft.Research.SEAL
;
using
Microsoft.VisualStudio.TestTools.UnitTesting
;
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
namespace
SEALNetTest
{
[
TestClass
]
public
class
SecretKeyTests
{
[
TestMethod
]
public
void
CreateTest
()
{
EncryptionParameters
parms
=
new
EncryptionParameters
(
SchemeType
.
BFV
)
{
PolyModulusDegree
=
64
,
PlainModulus
=
new
Modulus
(
1
<<
6
),
CoeffModulus
=
CoeffModulus
.
Create
(
64
,
new
int
[]
{
40
})
};
SEALContext
context
=
new
SEALContext
(
parms
,
expandModChain
:
false
,
secLevel
:
SecLevelType
.
None
);
KeyGenerator
keygen
=
new
KeyGenerator
(
context
);
SecretKey
secret
=
keygen
.
SecretKey
;
SecretKey
copy
=
new
SecretKey
(
secret
);
Assert
.
AreEqual
(
64u
l
,
copy
.
Data
.
CoeffCount
);
Assert
.
IsTrue
(
copy
.
Data
.
IsNTTForm
);
SecretKey
copy2
=
new
SecretKey
();
copy2
.
Set
(
copy
);
Assert
.
AreEqual
(
64u
l
,
copy2
.
Data
.
CoeffCount
);
Assert
.
IsTrue
(
copy2
.
Data
.
IsNTTForm
);
}
[
TestMethod
]
public
void
SaveLoadTest
()
{
EncryptionParameters
parms
=
new
EncryptionParameters
(
SchemeType
.
BFV
)
{
PolyModulusDegree
=
64
,
PlainModulus
=
new
Modulus
(
1
<<
6
),
CoeffModulus
=
CoeffModulus
.
Create
(
64
,
new
int
[]
{
40
})
};
SEALContext
context
=
new
SEALContext
(
parms
,
expandModChain
:
false
,
secLevel
:
SecLevelType
.
None
);
KeyGenerator
keygen
=
new
KeyGenerator
(
context
);
SecretKey
secret
=
keygen
.
SecretKey
;
Assert
.
AreEqual
(
64u
l
,
secret
.
Data
.
CoeffCount
);
Assert
.
IsTrue
(
secret
.
Data
.
IsNTTForm
);
Assert
.
AreNotEqual
(
ParmsId
.
Zero
,
secret
.
ParmsId
);
SecretKey
secret2
=
new
SecretKey
();
Assert
.
IsNotNull
(
secret2
);
Assert
.
AreEqual
(
0u
l
,
secret2
.
Data
.
CoeffCount
);
Assert
.
IsFalse
(
secret2
.
Data
.
IsNTTForm
);
using
(
MemoryStream
stream
=
new
MemoryStream
())
{
secret
.
Save
(
stream
);
stream
.
Seek
(
offset
:
0
,
loc
:
SeekOrigin
.
Begin
);
secret2
.
Load
(
context
,
stream
);
}
Assert
.
AreNotSame
(
secret
,
secret2
);
Assert
.
AreEqual
(
64u
l
,
secret2
.
Data
.
CoeffCount
);
Assert
.
IsTrue
(
secret2
.
Data
.
IsNTTForm
);
Assert
.
AreNotEqual
(
ParmsId
.
Zero
,
secret2
.
ParmsId
);
Assert
.
AreEqual
(
secret
.
ParmsId
,
secret2
.
ParmsId
);
}
[
TestMethod
]
public
void
ExceptionsTest
()
{
SEALContext
context
=
GlobalContext
.
BFVContext
;
SecretKey
key
=
new
SecretKey
();
Utilities
.
AssertThrows
<
ArgumentNullException
>(()
=>
key
=
new
SecretKey
(
null
));
Utilities
.
AssertThrows
<
ArgumentNullException
>(()
=>
key
.
Set
(
null
));
Utilities
.
AssertThrows
<
ArgumentNullException
>(()
=>
ValCheck
.
IsValidFor
(
key
,
null
));
Utilities
.
AssertThrows
<
ArgumentNullException
>(()
=>
key
.
Save
(
null
));
Utilities
.
AssertThrows
<
ArgumentNullException
>(()
=>
key
.
UnsafeLoad
(
null
,
new
MemoryStream
()));
Utilities
.
AssertThrows
<
ArgumentNullException
>(()
=>
key
.
UnsafeLoad
(
context
,
null
));
Utilities
.
AssertThrows
<
ArgumentNullException
>(()
=>
key
.
Load
(
context
,
null
));
Utilities
.
AssertThrows
<
ArgumentNullException
>(()
=>
key
.
Load
(
null
,
new
MemoryStream
()));
Utilities
.
AssertThrows
<
EndOfStreamException
>(()
=>
key
.
Load
(
context
,
new
MemoryStream
()));
}
}
}
bigpiseal3.5.1/dotnet/tests/SerializationTests.cs
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using
Microsoft.VisualStudio.TestTools.UnitTesting
;
using
Microsoft.Research.SEAL
;
using
System
;
using
System.IO
;
using
System.Text
;
namespace
SEALNetTest
{
[
TestClass
]
public
class
SerializationTests
{
[
TestMethod
]
public
void
IsValidHeader
()
{
Assert
.
AreEqual
(
Serialization
.
SEALHeaderSize
,
0x10
);
Serialization
.
SEALHeader
header
=
new
Serialization
.
SEALHeader
();
Assert
.
IsTrue
(
Serialization
.
IsValidHeader
(
header
));
Serialization
.
SEALHeader
invalidHeader
=
new
Serialization
.
SEALHeader
();
invalidHeader
.
Magic
=
0x1212
;
Assert
.
IsFalse
(
Serialization
.
IsValidHeader
(
invalidHeader
));
invalidHeader
.
Magic
=
Serialization
.
SEALMagic
;
Assert
.
AreEqual
(
Serialization
.
SEALHeaderSize
,
invalidHeader
.
HeaderSize
);
invalidHeader
.
VersionMajor
=
0x02
;
Assert
.
IsFalse
(
Serialization
.
IsValidHeader
(
invalidHeader
));
invalidHeader
.
VersionMajor
=
SEALVersion
.
Major
;
invalidHeader
.
ComprMode
=
(
ComprModeType
)
0x02
;
Assert
.
IsFalse
(
Serialization
.
IsValidHeader
(
invalidHeader
));
}
[
TestMethod
]
public
void
SEALHeaderSaveLoad
()
{
Serialization
.
SEALHeader
header
=
new
Serialization
.
SEALHeader
();
Serialization
.
SEALHeader
loaded
=
new
Serialization
.
SEALHeader
();
using
(
MemoryStream
mem
=
new
MemoryStream
())
{
header
.
ComprMode
=
Serialization
.
ComprModeDefault
;
header
.
Size
=
256
;
Assert
.
IsTrue
(
Serialization
.
IsValidHeader
(
header
));
Serialization
.
SaveHeader
(
header
,
mem
);
mem
.
Seek
(
offset
:
0
,
loc
:
SeekOrigin
.
Begin
);
Serialization
.
LoadHeader
(
mem
,
loaded
);
Assert
.
AreEqual
(
loaded
.
Magic
,
header
.
Magic
);
Assert
.
AreEqual
(
loaded
.
HeaderSize
,
header
.
HeaderSize
);
Assert
.
AreEqual
(
loaded
.
VersionMajor
,
header
.
VersionMajor
);
Assert
.
AreEqual
(
loaded
.
VersionMinor
,
header
.
VersionMinor
);
Assert
.
AreEqual
(
loaded
.
ComprMode
,
header
.
ComprMode
);
Assert
.
AreEqual
(
loaded
.
Reserved
,
header
.
Reserved
);
Assert
.
AreEqual
(
loaded
.
Size
,
header
.
Size
);
}
}
[
TestMethod
]
public
void
SEALHeaderUpgrade
()
{
LegacyHeaders
.
SEALHeader_3_4
header_3_4
=
new
LegacyHeaders
.
SEALHeader_3_4
();
using
MemoryStream
mem
=
new
MemoryStream
();
using
BinaryWriter
writer
=
new
BinaryWriter
(
mem
,
Encoding
.
UTF8
,
true
);
writer
.
Write
(
header_3_4
.
Magic
);
writer
.
Write
(
header_3_4
.
ZeroByte
);
writer
.
Write
((
byte
)
header_3_4
.
ComprMode
);
writer
.
Write
(
header_3_4
.
Size
);
writer
.
Write
(
header_3_4
.
Reserved
);
mem
.
Seek
(
offset
:
0
,
loc
:
SeekOrigin
.
Begin
);
{
Serialization
.
SEALHeader
loaded
=
new
Serialization
.
SEALHeader
();
Serialization
.
LoadHeader
(
mem
,
loaded
);
Assert
.
IsTrue
(
Serialization
.
IsValidHeader
(
loaded
));
Assert
.
AreEqual
(
header_3_4
.
ComprMode
,
loaded
.
ComprMode
);
Assert
.
AreEqual
(
header_3_4
.
Size
,
loaded
.
Size
);
mem
.
Seek
(
offset
:
0
,
loc
:
SeekOrigin
.
Begin
);
}
{
Serialization
.
SEALHeader
loaded
=
new
Serialization
.
SEALHeader
();
Serialization
.
LoadHeader
(
mem
,
loaded
,
false
);
Assert
.
IsFalse
(
Serialization
.
IsValidHeader
(
loaded
));
mem
.
Seek
(
offset
:
0
,
loc
:
SeekOrigin
.
Begin
);
}
}
[
TestMethod
]
public
void
ExceptionsTest
()
{
SEALContext
context
=
GlobalContext
.
BFVContext
;
Ciphertext
cipher
=
new
Ciphertext
();
using
(
MemoryStream
mem
=
new
MemoryStream
())
{
KeyGenerator
keygen
=
new
KeyGenerator
(
context
);
Encryptor
encryptor
=
new
Encryptor
(
context
,
keygen
.
PublicKey
);
Plaintext
plain
=
new
Plaintext
(
"2x^3 + 4x^2 + 5x^1 + 6"
);
encryptor
.
Encrypt
(
plain
,
cipher
);
cipher
.
Save
(
mem
);
mem
.
Seek
(
offset
:
8
,
loc
:
SeekOrigin
.
Begin
);
BinaryWriter
writer
=
new
BinaryWriter
(
mem
,
Encoding
.
UTF8
,
true
);
writer
.
Write
((
ulong
)
0x80000000
);
mem
.
Seek
(
offset
:
0
,
loc
:
SeekOrigin
.
Begin
);
Utilities
.
AssertThrows
<
InvalidOperationException
>(()
=>
cipher
.
Load
(
context
,
mem
));
}
}
}
}
bigpiseal3.5.1/dotnet/tests/TestAssemblyCleanup.cs
0 → 100644
View file @
c6c9d2f5
using
Microsoft.VisualStudio.TestTools.UnitTesting
;
using
System
;
using
System.Collections.Generic
;
using
System.Diagnostics
;
using
System.Text
;
namespace
SEALNetTest
{
[
TestClass
]
public
class
TestAssemblyCleanup
{
[
AssemblyCleanup
]
public
static
void
AssemblyCleanup
()
{
// Check that our Assert.Throw workaround is not getting out of hand
Assert
.
IsTrue
(
Utilities
.
WorkaroundInstanceCount
<=
2
,
$"WorkaroundInstanceCount should be <= 2, it is:
{
Utilities
.
WorkaroundInstanceCount
}
"
);
Trace
.
WriteLine
(
$"Assert.Throw workaround instances found:
{
Utilities
.
WorkaroundInstanceCount
}
"
);
}
}
}
bigpiseal3.5.1/dotnet/tests/Utilities.cs
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using
Microsoft.VisualStudio.TestTools.UnitTesting
;
using
System
;
using
System.Diagnostics
;
using
System.IO
;
using
System.Runtime.CompilerServices
;
namespace
SEALNetTest
{
/// <summary>
/// Test utilities
/// </summary>
public
static
class
Utilities
{
internal
static
int
WorkaroundInstanceCount
{
get
;
private
set
;
}
=
0
;
/// <summary>
/// Assert that an exception of the given type is thrown.
///
/// This is a workaround for a unit testing issue in VS 2019.
/// When running unit tests a couple of them fail because of a FileNotFoundException being thrown instead
/// of the expected exception. The FileNotFoundException is thrown in the boundary between a .Net call
/// and a native method, so there is not really much we can do to fix it. As a workaround this method
/// works as Assert.ThrowsException, but allows FileNotFoundException as well, and outputs a warning when
/// it is found.
/// </summary>
/// <typeparam name="T">Expected exception type</typeparam>
/// <param name="action">Function to run that should throw an exception</param>
/// <param name="caller">Path to the source file that called this method</param>
/// <param name="line">Line in the source file that called this method</param>
public
static
void
AssertThrows
<
T
>(
Func
<
object
>
action
,
[
CallerFilePath
]
string
caller
=
""
,
[
CallerLineNumber
]
int
line
=
0
)
where
T
:
Exception
{
DoAssertThrow
<
T
>(()
=>
{
var
result
=
action
();
},
caller
,
line
);
}
/// <summary>
/// Assert that an exception of the given type is thrown.
///
/// This is a workaround for a unit testing issue in VS 2019.
/// When running unit tests a couple of them fail because of a FileNotFoundException being thrown instead
/// of the expected exception. The FileNotFoundException is thrown in the boundary between a .Net call
/// and a native method, so there is not really much we can do to fix it. As a workaround this method
/// works as Assert.ThrowsException, but allows FileNotFoundException as well, and outputs a warning when
/// it is found.
/// </summary>
/// <typeparam name="T">Expected exception type</typeparam>
/// <param name="action">Action to run that should throw an exception</param>
/// <param name="caller">Path to the source file that called this method</param>
/// <param name="line">Line in the source file that called this method</param>
public
static
void
AssertThrows
<
T
>(
Action
action
,
[
CallerFilePath
]
string
caller
=
""
,
[
CallerLineNumber
]
int
line
=
0
)
where
T
:
Exception
{
DoAssertThrow
<
T
>(
action
,
caller
,
line
);
}
private
static
void
DoAssertThrow
<
T
>(
Action
action
,
string
caller
,
int
line
)
where
T
:
Exception
{
string
expectedStr
=
typeof
(
T
).
ToString
();
try
{
action
();
}
catch
(
Exception
ex
)
{
if
(
ex
is
T
)
{
// Expected exception has been thrown
return
;
}
// Workaround: Check if exception is FileNotFoundException
if
(
ex
is
FileNotFoundException
workaroundExc
)
{
string
workaroundStr
=
workaroundExc
.
GetType
().
ToString
();
Trace
.
WriteLine
(
$"WARNING:
{
caller
}
:
{
line
}
: Expected exception of type '
{
expectedStr
}
', got type '
{
workaroundStr
}
' instead."
);
WorkaroundInstanceCount
++;
return
;
}
// Any other exception should fail.
string
actualStr
=
ex
.
GetType
().
ToString
();
Assert
.
Fail
(
$"
{
caller
}
:
{
line
}
: Expected exception of type '
{
expectedStr
}
', got type '
{
actualStr
}
' instead."
);
}
Assert
.
Fail
(
$"
{
caller
}
:
{
line
}
: Expected exception of type '
{
expectedStr
}
', no exception thrown."
);
}
}
}
bigpiseal3.5.1/native/examples/1_bfv_basics.cpp
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include "examples.h"
using
namespace
std
;
using
namespace
seal
;
void
example_bfv_basics
()
{
print_example_banner
(
"Example: BFV Basics"
);
/*
In this example, we demonstrate performing simple computations (a polynomial
evaluation) on encrypted integers using the BFV encryption scheme.
The first task is to set up an instance of the EncryptionParameters class.
It is critical to understand how the different parameters behave, how they
affect the encryption scheme, performance, and the security level. There are
three encryption parameters that are necessary to set:
- poly_modulus_degree (degree of polynomial modulus);
- coeff_modulus ([ciphertext] coefficient modulus);
- plain_modulus (plaintext modulus; only for the BFV scheme).
The BFV scheme cannot perform arbitrary computations on encrypted data.
Instead, each ciphertext has a specific quantity called the `invariant noise
budget' -- or `noise budget' for short -- measured in bits. The noise budget
in a freshly encrypted ciphertext (initial noise budget) is determined by
the encryption parameters. Homomorphic operations consume the noise budget
at a rate also determined by the encryption parameters. In BFV the two basic
operations allowed on encrypted data are additions and multiplications, of
which additions can generally be thought of as being nearly free in terms of
noise budget consumption compared to multiplications. Since noise budget
consumption compounds in sequential multiplications, the most significant
factor in choosing appropriate encryption parameters is the multiplicative
depth of the arithmetic circuit that the user wants to evaluate on encrypted
data. Once the noise budget of a ciphertext reaches zero it becomes too
corrupted to be decrypted. Thus, it is essential to choose the parameters to
be large enough to support the desired computation; otherwise the result is
impossible to make sense of even with the secret key.
*/
EncryptionParameters
parms
(
scheme_type
::
BFV
);
/*
The first parameter we set is the degree of the `polynomial modulus'. This
must be a positive power of 2, representing the degree of a power-of-two
cyclotomic polynomial; it is not necessary to understand what this means.
Larger poly_modulus_degree makes ciphertext sizes larger and all operations
slower, but enables more complicated encrypted computations. Recommended
values are 1024, 2048, 4096, 8192, 16384, 32768, but it is also possible
to go beyond this range.
In this example we use a relatively small polynomial modulus. Anything
smaller than this will enable only very restricted encrypted computations.
*/
size_t
poly_modulus_degree
=
4096
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
/*
Next we set the [ciphertext] `coefficient modulus' (coeff_modulus). This
parameter is a large integer, which is a product of distinct prime numbers,
each up to 60 bits in size. It is represented as a vector of these prime
numbers, each represented by an instance of the Modulus class. The
bit-length of coeff_modulus means the sum of the bit-lengths of its prime
factors.
A larger coeff_modulus implies a larger noise budget, hence more encrypted
computation capabilities. However, an upper bound for the total bit-length
of the coeff_modulus is determined by the poly_modulus_degree, as follows:
+----------------------------------------------------+
| poly_modulus_degree | max coeff_modulus bit-length |
+---------------------+------------------------------+
| 1024 | 27 |
| 2048 | 54 |
| 4096 | 109 |
| 8192 | 218 |
| 16384 | 438 |
| 32768 | 881 |
+---------------------+------------------------------+
These numbers can also be found in native/src/seal/util/hestdparms.h encoded
in the function SEAL_HE_STD_PARMS_128_TC, and can also be obtained from the
function
CoeffModulus::MaxBitCount(poly_modulus_degree).
For example, if poly_modulus_degree is 4096, the coeff_modulus could consist
of three 36-bit primes (108 bits).
Microsoft SEAL comes with helper functions for selecting the coeff_modulus.
For new users the easiest way is to simply use
CoeffModulus::BFVDefault(poly_modulus_degree),
which returns std::vector<Modulus> consisting of a generally good choice
for the given poly_modulus_degree.
*/
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
/*
The plaintext modulus can be any positive integer, even though here we take
it to be a power of two. In fact, in many cases one might instead want it
to be a prime number; we will see this in later examples. The plaintext
modulus determines the size of the plaintext data type and the consumption
of noise budget in multiplications. Thus, it is essential to try to keep the
plaintext data type as small as possible for best performance. The noise
budget in a freshly encrypted ciphertext is
~ log2(coeff_modulus/plain_modulus) (bits)
and the noise budget consumption in a homomorphic multiplication is of the
form log2(plain_modulus) + (other terms).
The plaintext modulus is specific to the BFV scheme, and cannot be set when
using the CKKS scheme.
*/
parms
.
set_plain_modulus
(
1024
);
/*
Now that all parameters are set, we are ready to construct a SEALContext
object. This is a heavy class that checks the validity and properties of the
parameters we just set.
*/
auto
context
=
SEALContext
::
Create
(
parms
);
/*
Print the parameters that we have chosen.
*/
print_line
(
__LINE__
);
cout
<<
"Set encryption parameters and print"
<<
endl
;
print_parameters
(
context
);
/*
When parameters are used to create SEALContext, Microsoft SEAL will first
validate those parameters. The parameters chosen here are valid.
*/
cout
<<
"Parameter validation (success): "
<<
context
->
parameter_error_message
()
<<
endl
;
cout
<<
endl
;
cout
<<
"~~~~~~ A naive way to calculate 4(x^2+1)(x+1)^2. ~~~~~~"
<<
endl
;
/*
The encryption schemes in Microsoft SEAL are public key encryption schemes.
For users unfamiliar with this terminology, a public key encryption scheme
has a separate public key for encrypting data, and a separate secret key for
decrypting data. This way multiple parties can encrypt data using the same
shared public key, but only the proper recipient of the data can decrypt it
with the secret key.
We are now ready to generate the secret and public keys. For this purpose
we need an instance of the KeyGenerator class. Constructing a KeyGenerator
automatically generates the public and secret key, which can immediately be
read to local variables.
*/
KeyGenerator
keygen
(
context
);
PublicKey
public_key
=
keygen
.
public_key
();
SecretKey
secret_key
=
keygen
.
secret_key
();
/*
To be able to encrypt we need to construct an instance of Encryptor. Note
that the Encryptor only requires the public key, as expected.
*/
Encryptor
encryptor
(
context
,
public_key
);
/*
Computations on the ciphertexts are performed with the Evaluator class. In
a real use-case the Evaluator would not be constructed by the same party
that holds the secret key.
*/
Evaluator
evaluator
(
context
);
/*
We will of course want to decrypt our results to verify that everything worked,
so we need to also construct an instance of Decryptor. Note that the Decryptor
requires the secret key.
*/
Decryptor
decryptor
(
context
,
secret_key
);
/*
As an example, we evaluate the degree 4 polynomial
4x^4 + 8x^3 + 8x^2 + 8x + 4
over an encrypted x = 6. The coefficients of the polynomial can be considered
as plaintext inputs, as we will see below. The computation is done modulo the
plain_modulus 1024.
While this examples is simple and easy to understand, it does not have much
practical value. In later examples we will demonstrate how to compute more
efficiently on encrypted integers and real or complex numbers.
Plaintexts in the BFV scheme are polynomials of degree less than the degree
of the polynomial modulus, and coefficients integers modulo the plaintext
modulus. For readers with background in ring theory, the plaintext space is
the polynomial quotient ring Z_T[X]/(X^N + 1), where N is poly_modulus_degree
and T is plain_modulus.
To get started, we create a plaintext containing the constant 6. For the
plaintext element we use a constructor that takes the desired polynomial as
a string with coefficients represented as hexadecimal numbers.
*/
print_line
(
__LINE__
);
int
x
=
6
;
Plaintext
x_plain
(
to_string
(
x
));
cout
<<
"Express x = "
+
to_string
(
x
)
+
" as a plaintext polynomial 0x"
+
x_plain
.
to_string
()
+
"."
<<
endl
;
/*
We then encrypt the plaintext, producing a ciphertext.
*/
print_line
(
__LINE__
);
Ciphertext
x_encrypted
;
cout
<<
"Encrypt x_plain to x_encrypted."
<<
endl
;
encryptor
.
encrypt
(
x_plain
,
x_encrypted
);
/*
In Microsoft SEAL, a valid ciphertext consists of two or more polynomials
whose coefficients are integers modulo the product of the primes in the
coeff_modulus. The number of polynomials in a ciphertext is called its `size'
and is given by Ciphertext::size(). A freshly encrypted ciphertext always
has size 2.
*/
cout
<<
" + size of freshly encrypted x: "
<<
x_encrypted
.
size
()
<<
endl
;
/*
There is plenty of noise budget left in this freshly encrypted ciphertext.
*/
cout
<<
" + noise budget in freshly encrypted x: "
<<
decryptor
.
invariant_noise_budget
(
x_encrypted
)
<<
" bits"
<<
endl
;
/*
We decrypt the ciphertext and print the resulting plaintext in order to
demonstrate correctness of the encryption.
*/
Plaintext
x_decrypted
;
cout
<<
" + decryption of x_encrypted: "
;
decryptor
.
decrypt
(
x_encrypted
,
x_decrypted
);
cout
<<
"0x"
<<
x_decrypted
.
to_string
()
<<
" ...... Correct."
<<
endl
;
/*
When using Microsoft SEAL, it is typically advantageous to compute in a way
that minimizes the longest chain of sequential multiplications. In other
words, encrypted computations are best evaluated in a way that minimizes
the multiplicative depth of the computation, because the total noise budget
consumption is proportional to the multiplicative depth. For example, for
our example computation it is advantageous to factorize the polynomial as
4x^4 + 8x^3 + 8x^2 + 8x + 4 = 4(x + 1)^2 * (x^2 + 1)
to obtain a simple depth 2 representation. Thus, we compute (x + 1)^2 and
(x^2 + 1) separately, before multiplying them, and multiplying by 4.
First, we compute x^2 and add a plaintext "1". We can clearly see from the
print-out that multiplication has consumed a lot of noise budget. The user
can vary the plain_modulus parameter to see its effect on the rate of noise
budget consumption.
*/
print_line
(
__LINE__
);
cout
<<
"Compute x_sq_plus_one (x^2+1)."
<<
endl
;
Ciphertext
x_sq_plus_one
;
evaluator
.
square
(
x_encrypted
,
x_sq_plus_one
);
Plaintext
plain_one
(
"1"
);
evaluator
.
add_plain_inplace
(
x_sq_plus_one
,
plain_one
);
/*
Encrypted multiplication results in the output ciphertext growing in size.
More precisely, if the input ciphertexts have size M and N, then the output
ciphertext after homomorphic multiplication will have size M+N-1. In this
case we perform a squaring, and observe both size growth and noise budget
consumption.
*/
cout
<<
" + size of x_sq_plus_one: "
<<
x_sq_plus_one
.
size
()
<<
endl
;
cout
<<
" + noise budget in x_sq_plus_one: "
<<
decryptor
.
invariant_noise_budget
(
x_sq_plus_one
)
<<
" bits"
<<
endl
;
/*
Even though the size has grown, decryption works as usual as long as noise
budget has not reached 0.
*/
Plaintext
decrypted_result
;
cout
<<
" + decryption of x_sq_plus_one: "
;
decryptor
.
decrypt
(
x_sq_plus_one
,
decrypted_result
);
cout
<<
"0x"
<<
decrypted_result
.
to_string
()
<<
" ...... Correct."
<<
endl
;
/*
Next, we compute (x + 1)^2.
*/
print_line
(
__LINE__
);
cout
<<
"Compute x_plus_one_sq ((x+1)^2)."
<<
endl
;
Ciphertext
x_plus_one_sq
;
evaluator
.
add_plain
(
x_encrypted
,
plain_one
,
x_plus_one_sq
);
evaluator
.
square_inplace
(
x_plus_one_sq
);
cout
<<
" + size of x_plus_one_sq: "
<<
x_plus_one_sq
.
size
()
<<
endl
;
cout
<<
" + noise budget in x_plus_one_sq: "
<<
decryptor
.
invariant_noise_budget
(
x_plus_one_sq
)
<<
" bits"
<<
endl
;
cout
<<
" + decryption of x_plus_one_sq: "
;
decryptor
.
decrypt
(
x_plus_one_sq
,
decrypted_result
);
cout
<<
"0x"
<<
decrypted_result
.
to_string
()
<<
" ...... Correct."
<<
endl
;
/*
Finally, we multiply (x^2 + 1) * (x + 1)^2 * 4.
*/
print_line
(
__LINE__
);
cout
<<
"Compute encrypted_result (4(x^2+1)(x+1)^2)."
<<
endl
;
Ciphertext
encrypted_result
;
Plaintext
plain_four
(
"4"
);
evaluator
.
multiply_plain_inplace
(
x_sq_plus_one
,
plain_four
);
evaluator
.
multiply
(
x_sq_plus_one
,
x_plus_one_sq
,
encrypted_result
);
cout
<<
" + size of encrypted_result: "
<<
encrypted_result
.
size
()
<<
endl
;
cout
<<
" + noise budget in encrypted_result: "
<<
decryptor
.
invariant_noise_budget
(
encrypted_result
)
<<
" bits"
<<
endl
;
cout
<<
"NOTE: Decryption can be incorrect if noise budget is zero."
<<
endl
;
cout
<<
endl
;
cout
<<
"~~~~~~ A better way to calculate 4(x^2+1)(x+1)^2. ~~~~~~"
<<
endl
;
/*
Noise budget has reached 0, which means that decryption cannot be expected
to give the correct result. This is because both ciphertexts x_sq_plus_one
and x_plus_one_sq consist of 3 polynomials due to the previous squaring
operations, and homomorphic operations on large ciphertexts consume much more
noise budget than computations on small ciphertexts. Computing on smaller
ciphertexts is also computationally significantly cheaper.
`Relinearization' is an operation that reduces the size of a ciphertext after
multiplication back to the initial size, 2. Thus, relinearizing one or both
input ciphertexts before the next multiplication can have a huge positive
impact on both noise growth and performance, even though relinearization has
a significant computational cost itself. It is only possible to relinearize
size 3 ciphertexts down to size 2, so often the user would want to relinearize
after each multiplication to keep the ciphertext sizes at 2.
Relinearization requires special `relinearization keys', which can be thought
of as a kind of public key. Relinearization keys can easily be created with
the KeyGenerator.
Relinearization is used similarly in both the BFV and the CKKS schemes, but
in this example we continue using BFV. We repeat our computation from before,
but this time relinearize after every multiplication.
Here we use the function KeyGenerator::relin_keys_local(). In production
code it is much better to use KeyGenerator::relin_keys() instead. We will
explain and discuss these differences in `6_serialization.cpp'.
*/
print_line
(
__LINE__
);
cout
<<
"Generate locally usable relinearization keys."
<<
endl
;
auto
relin_keys
=
keygen
.
relin_keys_local
();
/*
We now repeat the computation relinearizing after each multiplication.
*/
print_line
(
__LINE__
);
cout
<<
"Compute and relinearize x_squared (x^2),"
<<
endl
;
cout
<<
string
(
13
,
' '
)
<<
"then compute x_sq_plus_one (x^2+1)"
<<
endl
;
Ciphertext
x_squared
;
evaluator
.
square
(
x_encrypted
,
x_squared
);
cout
<<
" + size of x_squared: "
<<
x_squared
.
size
()
<<
endl
;
evaluator
.
relinearize_inplace
(
x_squared
,
relin_keys
);
cout
<<
" + size of x_squared (after relinearization): "
<<
x_squared
.
size
()
<<
endl
;
evaluator
.
add_plain
(
x_squared
,
plain_one
,
x_sq_plus_one
);
cout
<<
" + noise budget in x_sq_plus_one: "
<<
decryptor
.
invariant_noise_budget
(
x_sq_plus_one
)
<<
" bits"
<<
endl
;
cout
<<
" + decryption of x_sq_plus_one: "
;
decryptor
.
decrypt
(
x_sq_plus_one
,
decrypted_result
);
cout
<<
"0x"
<<
decrypted_result
.
to_string
()
<<
" ...... Correct."
<<
endl
;
print_line
(
__LINE__
);
Ciphertext
x_plus_one
;
cout
<<
"Compute x_plus_one (x+1),"
<<
endl
;
cout
<<
string
(
13
,
' '
)
<<
"then compute and relinearize x_plus_one_sq ((x+1)^2)."
<<
endl
;
evaluator
.
add_plain
(
x_encrypted
,
plain_one
,
x_plus_one
);
evaluator
.
square
(
x_plus_one
,
x_plus_one_sq
);
cout
<<
" + size of x_plus_one_sq: "
<<
x_plus_one_sq
.
size
()
<<
endl
;
evaluator
.
relinearize_inplace
(
x_plus_one_sq
,
relin_keys
);
cout
<<
" + noise budget in x_plus_one_sq: "
<<
decryptor
.
invariant_noise_budget
(
x_plus_one_sq
)
<<
" bits"
<<
endl
;
cout
<<
" + decryption of x_plus_one_sq: "
;
decryptor
.
decrypt
(
x_plus_one_sq
,
decrypted_result
);
cout
<<
"0x"
<<
decrypted_result
.
to_string
()
<<
" ...... Correct."
<<
endl
;
print_line
(
__LINE__
);
cout
<<
"Compute and relinearize encrypted_result (4(x^2+1)(x+1)^2)."
<<
endl
;
evaluator
.
multiply_plain_inplace
(
x_sq_plus_one
,
plain_four
);
evaluator
.
multiply
(
x_sq_plus_one
,
x_plus_one_sq
,
encrypted_result
);
cout
<<
" + size of encrypted_result: "
<<
encrypted_result
.
size
()
<<
endl
;
evaluator
.
relinearize_inplace
(
encrypted_result
,
relin_keys
);
cout
<<
" + size of encrypted_result (after relinearization): "
<<
encrypted_result
.
size
()
<<
endl
;
cout
<<
" + noise budget in encrypted_result: "
<<
decryptor
.
invariant_noise_budget
(
encrypted_result
)
<<
" bits"
<<
endl
;
cout
<<
endl
;
cout
<<
"NOTE: Notice the increase in remaining noise budget."
<<
endl
;
/*
Relinearization clearly improved our noise consumption. We have still plenty
of noise budget left, so we can expect the correct answer when decrypting.
*/
print_line
(
__LINE__
);
cout
<<
"Decrypt encrypted_result (4(x^2+1)(x+1)^2)."
<<
endl
;
decryptor
.
decrypt
(
encrypted_result
,
decrypted_result
);
cout
<<
" + decryption of 4(x^2+1)(x+1)^2 = 0x"
<<
decrypted_result
.
to_string
()
<<
" ...... Correct."
<<
endl
;
cout
<<
endl
;
/*
For x=6, 4(x^2+1)(x+1)^2 = 7252. Since the plaintext modulus is set to 1024,
this result is computed in integers modulo 1024. Therefore the expected output
should be 7252 % 1024 == 84, or 0x54 in hexadecimal.
*/
/*
Sometimes we create customized encryption parameters which turn out to be invalid.
Microsoft SEAL can interpret the reason why parameters are considered invalid.
Here we simply reduce the polynomial modulus degree to make the parameters not
compliant with the HomomorphicEncryption.org security standard.
*/
print_line
(
__LINE__
);
cout
<<
"An example of invalid parameters"
<<
endl
;
parms
.
set_poly_modulus_degree
(
2048
);
context
=
SEALContext
::
Create
(
parms
);
print_parameters
(
context
);
cout
<<
"Parameter validation (failed): "
<<
context
->
parameter_error_message
()
<<
endl
;
/*
This information is helpful to fix invalid encryption parameters.
*/
}
bigpiseal3.5.1/native/examples/2_encoders.cpp
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include "examples.h"
using
namespace
std
;
using
namespace
seal
;
/*
In `1_bfv_basics.cpp' we showed how to perform a very simple computation using the
BFV scheme. The computation was performed modulo the plain_modulus parameter, and
utilized only one coefficient from a BFV plaintext polynomial. This approach has
two notable problems:
(1) Practical applications typically use integer or real number arithmetic,
not modular arithmetic;
(2) We used only one coefficient of the plaintext polynomial. This is really
wasteful, as the plaintext polynomial is large and will in any case be
encrypted in its entirety.
For (1), one may ask why not just increase the plain_modulus parameter until no
overflow occurs, and the computations behave as in integer arithmetic. The problem
is that increasing plain_modulus increases noise budget consumption, and decreases
the initial noise budget too.
In these examples we will discuss other ways of laying out data into plaintext
elements (encoding) that allow more computations without data type overflow, and
can allow the full plaintext polynomial to be utilized.
*/
void
example_integer_encoder
()
{
print_example_banner
(
"Example: Encoders / Integer Encoder"
);
/*
[IntegerEncoder] (For BFV scheme only)
The IntegerEncoder encodes integers to BFV plaintext polynomials as follows.
First, a binary expansion of the integer is computed. Next, a polynomial is
created with the bits as coefficients. For example, the integer
26 = 2^4 + 2^3 + 2^1
is encoded as the polynomial 1x^4 + 1x^3 + 1x^1. Conversely, plaintext
polynomials are decoded by evaluating them at x=2. For negative numbers the
IntegerEncoder simply stores all coefficients as either 0 or -1, where -1 is
represented by the unsigned integer plain_modulus - 1 in memory.
Since encrypted computations operate on the polynomials rather than on the
encoded integers themselves, the polynomial coefficients will grow in the
course of such computations. For example, computing the sum of the encrypted
encoded integer 26 with itself will result in an encrypted polynomial with
larger coefficients: 2x^4 + 2x^3 + 2x^1. Squaring the encrypted encoded
integer 26 results also in increased coefficients due to cross-terms, namely,
(1x^4 + 1x^3 + 1x^1)^2 = 1x^8 + 2x^7 + 1x^6 + 2x^5 + 2x^4 + 1x^2;
further computations will quickly increase the coefficients much more.
Decoding will still work correctly in this case (evaluating the polynomial
at x=2), but since the coefficients of plaintext polynomials are really
integers modulo plain_modulus, implicit reduction modulo plain_modulus may
yield unexpected results. For example, adding 1x^4 + 1x^3 + 1x^1 to itself
plain_modulus many times will result in the constant polynomial 0, which is
clearly not equal to 26 * plain_modulus. It can be difficult to predict when
such overflow will take place especially when computing several sequential
multiplications.
The IntegerEncoder is easy to understand and use for simple computations,
and can be a good tool to experiment with for users new to Microsoft SEAL.
However, advanced users will probably prefer more efficient approaches,
such as the BatchEncoder or the CKKSEncoder.
*/
EncryptionParameters
parms
(
scheme_type
::
BFV
);
size_t
poly_modulus_degree
=
4096
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
/*
There is no hidden logic behind our choice of the plain_modulus. The only
thing that matters is that the plaintext polynomial coefficients will not
exceed this value at any point during our computation; otherwise the result
will be incorrect.
*/
parms
.
set_plain_modulus
(
512
);
auto
context
=
SEALContext
::
Create
(
parms
);
print_parameters
(
context
);
cout
<<
endl
;
KeyGenerator
keygen
(
context
);
PublicKey
public_key
=
keygen
.
public_key
();
SecretKey
secret_key
=
keygen
.
secret_key
();
Encryptor
encryptor
(
context
,
public_key
);
Evaluator
evaluator
(
context
);
Decryptor
decryptor
(
context
,
secret_key
);
/*
We create an IntegerEncoder.
*/
IntegerEncoder
encoder
(
context
);
/*
First, we encode two integers as plaintext polynomials. Note that encoding
is not encryption: at this point nothing is encrypted.
*/
int
value1
=
5
;
Plaintext
plain1
=
encoder
.
encode
(
value1
);
print_line
(
__LINE__
);
cout
<<
"Encode "
<<
value1
<<
" as polynomial "
<<
plain1
.
to_string
()
<<
" (plain1),"
<<
endl
;
int
value2
=
-
7
;
Plaintext
plain2
=
encoder
.
encode
(
value2
);
cout
<<
string
(
13
,
' '
)
<<
"encode "
<<
value2
<<
" as polynomial "
<<
plain2
.
to_string
()
<<
" (plain2)."
<<
endl
;
/*
Now we can encrypt the plaintext polynomials.
*/
Ciphertext
encrypted1
,
encrypted2
;
print_line
(
__LINE__
);
cout
<<
"Encrypt plain1 to encrypted1 and plain2 to encrypted2."
<<
endl
;
encryptor
.
encrypt
(
plain1
,
encrypted1
);
encryptor
.
encrypt
(
plain2
,
encrypted2
);
cout
<<
" + Noise budget in encrypted1: "
<<
decryptor
.
invariant_noise_budget
(
encrypted1
)
<<
" bits"
<<
endl
;
cout
<<
" + Noise budget in encrypted2: "
<<
decryptor
.
invariant_noise_budget
(
encrypted2
)
<<
" bits"
<<
endl
;
/*
As a simple example, we compute (-encrypted1 + encrypted2) * encrypted2.
*/
encryptor
.
encrypt
(
plain2
,
encrypted2
);
Ciphertext
encrypted_result
;
print_line
(
__LINE__
);
cout
<<
"Compute encrypted_result = (-encrypted1 + encrypted2) * encrypted2."
<<
endl
;
evaluator
.
negate
(
encrypted1
,
encrypted_result
);
evaluator
.
add_inplace
(
encrypted_result
,
encrypted2
);
evaluator
.
multiply_inplace
(
encrypted_result
,
encrypted2
);
cout
<<
" + Noise budget in encrypted_result: "
<<
decryptor
.
invariant_noise_budget
(
encrypted_result
)
<<
" bits"
<<
endl
;
Plaintext
plain_result
;
print_line
(
__LINE__
);
cout
<<
"Decrypt encrypted_result to plain_result."
<<
endl
;
decryptor
.
decrypt
(
encrypted_result
,
plain_result
);
/*
Print the result plaintext polynomial. The coefficients are not even close
to exceeding our plain_modulus, 512.
*/
cout
<<
" + Plaintext polynomial: "
<<
plain_result
.
to_string
()
<<
endl
;
/*
Decode to obtain an integer result.
*/
print_line
(
__LINE__
);
cout
<<
"Decode plain_result."
<<
endl
;
cout
<<
" + Decoded integer: "
<<
encoder
.
decode_int32
(
plain_result
);
cout
<<
"...... Correct."
<<
endl
;
}
void
example_batch_encoder
()
{
print_example_banner
(
"Example: Encoders / Batch Encoder"
);
/*
[BatchEncoder] (For BFV scheme only)
Let N denote the poly_modulus_degree and T denote the plain_modulus. Batching
allows the BFV plaintext polynomials to be viewed as 2-by-(N/2) matrices, with
each element an integer modulo T. In the matrix view, encrypted operations act
element-wise on encrypted matrices, allowing the user to obtain speeds-ups of
several orders of magnitude in fully vectorizable computations. Thus, in all
but the simplest computations, batching should be the preferred method to use
with BFV, and when used properly will result in implementations outperforming
anything done with the IntegerEncoder.
*/
EncryptionParameters
parms
(
scheme_type
::
BFV
);
size_t
poly_modulus_degree
=
8192
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
/*
To enable batching, we need to set the plain_modulus to be a prime number
congruent to 1 modulo 2*poly_modulus_degree. Microsoft SEAL provides a helper
method for finding such a prime. In this example we create a 20-bit prime
that supports batching.
*/
parms
.
set_plain_modulus
(
PlainModulus
::
Batching
(
poly_modulus_degree
,
20
));
auto
context
=
SEALContext
::
Create
(
parms
);
print_parameters
(
context
);
cout
<<
endl
;
/*
We can verify that batching is indeed enabled by looking at the encryption
parameter qualifiers created by SEALContext.
*/
auto
qualifiers
=
context
->
first_context_data
()
->
qualifiers
();
cout
<<
"Batching enabled: "
<<
boolalpha
<<
qualifiers
.
using_batching
<<
endl
;
KeyGenerator
keygen
(
context
);
PublicKey
public_key
=
keygen
.
public_key
();
SecretKey
secret_key
=
keygen
.
secret_key
();
RelinKeys
relin_keys
=
keygen
.
relin_keys_local
();
Encryptor
encryptor
(
context
,
public_key
);
Evaluator
evaluator
(
context
);
Decryptor
decryptor
(
context
,
secret_key
);
/*
Batching is done through an instance of the BatchEncoder class.
*/
BatchEncoder
batch_encoder
(
context
);
/*
The total number of batching `slots' equals the poly_modulus_degree, N, and
these slots are organized into 2-by-(N/2) matrices that can be encrypted and
computed on. Each slot contains an integer modulo plain_modulus.
*/
size_t
slot_count
=
batch_encoder
.
slot_count
();
size_t
row_size
=
slot_count
/
2
;
cout
<<
"Plaintext matrix row size: "
<<
row_size
<<
endl
;
/*
The matrix plaintext is simply given to BatchEncoder as a flattened vector
of numbers. The first `row_size' many numbers form the first row, and the
rest form the second row. Here we create the following matrix:
[ 0, 1, 2, 3, 0, 0, ..., 0 ]
[ 4, 5, 6, 7, 0, 0, ..., 0 ]
*/
vector
<
uint64_t
>
pod_matrix
(
slot_count
,
0ULL
);
pod_matrix
[
0
]
=
0ULL
;
pod_matrix
[
1
]
=
1ULL
;
pod_matrix
[
2
]
=
2ULL
;
pod_matrix
[
3
]
=
3ULL
;
pod_matrix
[
row_size
]
=
4ULL
;
pod_matrix
[
row_size
+
1
]
=
5ULL
;
pod_matrix
[
row_size
+
2
]
=
6ULL
;
pod_matrix
[
row_size
+
3
]
=
7ULL
;
cout
<<
"Input plaintext matrix:"
<<
endl
;
print_matrix
(
pod_matrix
,
row_size
);
/*
First we use BatchEncoder to encode the matrix into a plaintext polynomial.
*/
Plaintext
plain_matrix
;
print_line
(
__LINE__
);
cout
<<
"Encode plaintext matrix:"
<<
endl
;
batch_encoder
.
encode
(
pod_matrix
,
plain_matrix
);
/*
We can instantly decode to verify correctness of the encoding. Note that no
encryption or decryption has yet taken place.
*/
vector
<
uint64_t
>
pod_result
;
cout
<<
" + Decode plaintext matrix ...... Correct."
<<
endl
;
batch_encoder
.
decode
(
plain_matrix
,
pod_result
);
print_matrix
(
pod_result
,
row_size
);
/*
Next we encrypt the encoded plaintext.
*/
Ciphertext
encrypted_matrix
;
print_line
(
__LINE__
);
cout
<<
"Encrypt plain_matrix to encrypted_matrix."
<<
endl
;
encryptor
.
encrypt
(
plain_matrix
,
encrypted_matrix
);
cout
<<
" + Noise budget in encrypted_matrix: "
<<
decryptor
.
invariant_noise_budget
(
encrypted_matrix
)
<<
" bits"
<<
endl
;
/*
Operating on the ciphertext results in homomorphic operations being performed
simultaneously in all 8192 slots (matrix elements). To illustrate this, we
form another plaintext matrix
[ 1, 2, 1, 2, 1, 2, ..., 2 ]
[ 1, 2, 1, 2, 1, 2, ..., 2 ]
and encode it into a plaintext.
*/
vector
<
uint64_t
>
pod_matrix2
;
for
(
size_t
i
=
0
;
i
<
slot_count
;
i
++
)
{
pod_matrix2
.
push_back
((
i
%
2
)
+
1
);
}
Plaintext
plain_matrix2
;
batch_encoder
.
encode
(
pod_matrix2
,
plain_matrix2
);
cout
<<
endl
;
cout
<<
"Second input plaintext matrix:"
<<
endl
;
print_matrix
(
pod_matrix2
,
row_size
);
/*
We now add the second (plaintext) matrix to the encrypted matrix, and square
the sum.
*/
print_line
(
__LINE__
);
cout
<<
"Sum, square, and relinearize."
<<
endl
;
evaluator
.
add_plain_inplace
(
encrypted_matrix
,
plain_matrix2
);
evaluator
.
square_inplace
(
encrypted_matrix
);
evaluator
.
relinearize_inplace
(
encrypted_matrix
,
relin_keys
);
/*
How much noise budget do we have left?
*/
cout
<<
" + Noise budget in result: "
<<
decryptor
.
invariant_noise_budget
(
encrypted_matrix
)
<<
" bits"
<<
endl
;
/*
We decrypt and decompose the plaintext to recover the result as a matrix.
*/
Plaintext
plain_result
;
print_line
(
__LINE__
);
cout
<<
"Decrypt and decode result."
<<
endl
;
decryptor
.
decrypt
(
encrypted_matrix
,
plain_result
);
batch_encoder
.
decode
(
plain_result
,
pod_result
);
cout
<<
" + Result plaintext matrix ...... Correct."
<<
endl
;
print_matrix
(
pod_result
,
row_size
);
/*
Batching allows us to efficiently use the full plaintext polynomial when the
desired encrypted computation is highly parallelizable. However, it has not
solved the other problem mentioned in the beginning of this file: each slot
holds only an integer modulo plain_modulus, and unless plain_modulus is very
large, we can quickly encounter data type overflow and get unexpected results
when integer computations are desired. Note that overflow cannot be detected
in encrypted form. The CKKS scheme (and the CKKSEncoder) addresses the data
type overflow issue, but at the cost of yielding only approximate results.
*/
}
void
example_ckks_encoder
()
{
print_example_banner
(
"Example: Encoders / CKKS Encoder"
);
/*
[CKKSEncoder] (For CKKS scheme only)
In this example we demonstrate the Cheon-Kim-Kim-Song (CKKS) scheme for
computing on encrypted real or complex numbers. We start by creating
encryption parameters for the CKKS scheme. There are two important
differences compared to the BFV scheme:
(1) CKKS does not use the plain_modulus encryption parameter;
(2) Selecting the coeff_modulus in a specific way can be very important
when using the CKKS scheme. We will explain this further in the file
`ckks_basics.cpp'. In this example we use CoeffModulus::Create to
generate 5 40-bit prime numbers.
*/
EncryptionParameters
parms
(
scheme_type
::
CKKS
);
size_t
poly_modulus_degree
=
8192
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
Create
(
poly_modulus_degree
,
{
40
,
40
,
40
,
40
,
40
}));
/*
We create the SEALContext as usual and print the parameters.
*/
auto
context
=
SEALContext
::
Create
(
parms
);
print_parameters
(
context
);
cout
<<
endl
;
/*
Keys are created the same way as for the BFV scheme.
*/
KeyGenerator
keygen
(
context
);
auto
public_key
=
keygen
.
public_key
();
auto
secret_key
=
keygen
.
secret_key
();
auto
relin_keys
=
keygen
.
relin_keys_local
();
/*
We also set up an Encryptor, Evaluator, and Decryptor as usual.
*/
Encryptor
encryptor
(
context
,
public_key
);
Evaluator
evaluator
(
context
);
Decryptor
decryptor
(
context
,
secret_key
);
/*
To create CKKS plaintexts we need a special encoder: there is no other way
to create them. The IntegerEncoder and BatchEncoder cannot be used with the
CKKS scheme. The CKKSEncoder encodes vectors of real or complex numbers into
Plaintext objects, which can subsequently be encrypted. At a high level this
looks a lot like what BatchEncoder does for the BFV scheme, but the theory
behind it is completely different.
*/
CKKSEncoder
encoder
(
context
);
/*
In CKKS the number of slots is poly_modulus_degree / 2 and each slot encodes
one real or complex number. This should be contrasted with BatchEncoder in
the BFV scheme, where the number of slots is equal to poly_modulus_degree
and they are arranged into a matrix with two rows.
*/
size_t
slot_count
=
encoder
.
slot_count
();
cout
<<
"Number of slots: "
<<
slot_count
<<
endl
;
/*
We create a small vector to encode; the CKKSEncoder will implicitly pad it
with zeros to full size (poly_modulus_degree / 2) when encoding.
*/
vector
<
double
>
input
{
0.0
,
1.1
,
2.2
,
3.3
};
cout
<<
"Input vector: "
<<
endl
;
print_vector
(
input
);
/*
Now we encode it with CKKSEncoder. The floating-point coefficients of `input'
will be scaled up by the parameter `scale'. This is necessary since even in
the CKKS scheme the plaintext elements are fundamentally polynomials with
integer coefficients. It is instructive to think of the scale as determining
the bit-precision of the encoding; naturally it will affect the precision of
the result.
In CKKS the message is stored modulo coeff_modulus (in BFV it is stored modulo
plain_modulus), so the scaled message must not get too close to the total size
of coeff_modulus. In this case our coeff_modulus is quite large (200 bits) so
we have little to worry about in this regard. For this simple example a 30-bit
scale is more than enough.
*/
Plaintext
plain
;
double
scale
=
pow
(
2.0
,
30
);
print_line
(
__LINE__
);
cout
<<
"Encode input vector."
<<
endl
;
encoder
.
encode
(
input
,
scale
,
plain
);
/*
We can instantly decode to check the correctness of encoding.
*/
vector
<
double
>
output
;
cout
<<
" + Decode input vector ...... Correct."
<<
endl
;
encoder
.
decode
(
plain
,
output
);
print_vector
(
output
);
/*
The vector is encrypted the same was as in BFV.
*/
Ciphertext
encrypted
;
print_line
(
__LINE__
);
cout
<<
"Encrypt input vector, square, and relinearize."
<<
endl
;
encryptor
.
encrypt
(
plain
,
encrypted
);
/*
Basic operations on the ciphertexts are still easy to do. Here we square the
ciphertext, decrypt, decode, and print the result. We note also that decoding
returns a vector of full size (poly_modulus_degree / 2); this is because of
the implicit zero-padding mentioned above.
*/
evaluator
.
square_inplace
(
encrypted
);
evaluator
.
relinearize_inplace
(
encrypted
,
relin_keys
);
/*
We notice that the scale in the result has increased. In fact, it is now the
square of the original scale: 2^60.
*/
cout
<<
" + Scale in squared input: "
<<
encrypted
.
scale
()
<<
" ("
<<
log2
(
encrypted
.
scale
())
<<
" bits)"
<<
endl
;
print_line
(
__LINE__
);
cout
<<
"Decrypt and decode."
<<
endl
;
decryptor
.
decrypt
(
encrypted
,
plain
);
encoder
.
decode
(
plain
,
output
);
cout
<<
" + Result vector ...... Correct."
<<
endl
;
print_vector
(
output
);
/*
The CKKS scheme allows the scale to be reduced between encrypted computations.
This is a fundamental and critical feature that makes CKKS very powerful and
flexible. We will discuss it in great detail in `3_levels.cpp' and later in
`4_ckks_basics.cpp'.
*/
}
void
example_encoders
()
{
print_example_banner
(
"Example: Encoders"
);
/*
Run all encoder examples.
*/
example_integer_encoder
();
example_batch_encoder
();
example_ckks_encoder
();
}
bigpiseal3.5.1/native/examples/3_levels.cpp
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include "examples.h"
using
namespace
std
;
using
namespace
seal
;
void
example_levels
()
{
print_example_banner
(
"Example: Levels"
);
/*
In this examples we describe the concept of `levels' in BFV and CKKS and the
related objects that represent them in Microsoft SEAL.
In Microsoft SEAL a set of encryption parameters (excluding the random number
generator) is identified uniquely by a 256-bit hash of the parameters. This
hash is called the `parms_id' and can be easily accessed and printed at any
time. The hash will change as soon as any of the parameters is changed.
When a SEALContext is created from a given EncryptionParameters instance,
Microsoft SEAL automatically creates a so-called `modulus switching chain',
which is a chain of other encryption parameters derived from the original set.
The parameters in the modulus switching chain are the same as the original
parameters with the exception that size of the coefficient modulus is
decreasing going down the chain. More precisely, each parameter set in the
chain attempts to remove the last coefficient modulus prime from the
previous set; this continues until the parameter set is no longer valid
(e.g., plain_modulus is larger than the remaining coeff_modulus). It is easy
to walk through the chain and access all the parameter sets. Additionally,
each parameter set in the chain has a `chain index' that indicates its
position in the chain so that the last set has index 0. We say that a set
of encryption parameters, or an object carrying those encryption parameters,
is at a higher level in the chain than another set of parameters if its the
chain index is bigger, i.e., it is earlier in the chain.
Each set of parameters in the chain involves unique pre-computations performed
when the SEALContext is created, and stored in a SEALContext::ContextData
object. The chain is basically a linked list of SEALContext::ContextData
objects, and can easily be accessed through the SEALContext at any time. Each
node can be identified by the parms_id of its specific encryption parameters
(poly_modulus_degree remains the same but coeff_modulus varies).
*/
EncryptionParameters
parms
(
scheme_type
::
BFV
);
size_t
poly_modulus_degree
=
8192
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
/*
In this example we use a custom coeff_modulus, consisting of 5 primes of
sizes 50, 30, 30, 50, and 50 bits. Note that this is still OK according to
the explanation in `1_bfv_basics.cpp'. Indeed,
CoeffModulus::MaxBitCount(poly_modulus_degree)
returns 218 (greater than 50+30+30+50+50=210).
Due to the modulus switching chain, the order of the 5 primes is significant.
The last prime has a special meaning and we call it the `special prime'. Thus,
the first parameter set in the modulus switching chain is the only one that
involves the special prime. All key objects, such as SecretKey, are created
at this highest level. All data objects, such as Ciphertext, can be only at
lower levels. The special prime should be as large as the largest of the
other primes in the coeff_modulus, although this is not a strict requirement.
special prime +---------+
|
v
coeff_modulus: { 50, 30, 30, 50, 50 } +---+ Level 4 (all keys; `key level')
|
|
coeff_modulus: { 50, 30, 30, 50 } +---+ Level 3 (highest `data level')
|
|
coeff_modulus: { 50, 30, 30 } +---+ Level 2
|
|
coeff_modulus: { 50, 30 } +---+ Level 1
|
|
coeff_modulus: { 50 } +---+ Level 0 (lowest level)
*/
parms
.
set_coeff_modulus
(
CoeffModulus
::
Create
(
poly_modulus_degree
,
{
50
,
30
,
30
,
50
,
50
}));
/*
In this example the plain_modulus does not play much of a role; we choose
some reasonable value.
*/
parms
.
set_plain_modulus
(
PlainModulus
::
Batching
(
poly_modulus_degree
,
20
));
auto
context
=
SEALContext
::
Create
(
parms
);
print_parameters
(
context
);
cout
<<
endl
;
/*
There are convenience method for accessing the SEALContext::ContextData for
some of the most important levels:
SEALContext::key_context_data(): access to key level ContextData
SEALContext::first_context_data(): access to highest data level ContextData
SEALContext::last_context_data(): access to lowest level ContextData
We iterate over the chain and print the parms_id for each set of parameters.
*/
print_line
(
__LINE__
);
cout
<<
"Print the modulus switching chain."
<<
endl
;
/*
First print the key level parameter information.
*/
auto
context_data
=
context
->
key_context_data
();
cout
<<
"----> Level (chain index): "
<<
context_data
->
chain_index
();
cout
<<
" ...... key_context_data()"
<<
endl
;
cout
<<
" parms_id: "
<<
context_data
->
parms_id
()
<<
endl
;
cout
<<
" coeff_modulus primes: "
;
cout
<<
hex
;
for
(
const
auto
&
prime
:
context_data
->
parms
().
coeff_modulus
())
{
cout
<<
prime
.
value
()
<<
" "
;
}
cout
<<
dec
<<
endl
;
cout
<<
"
\\
"
<<
endl
;
cout
<<
"
\\
-->"
;
/*
Next iterate over the remaining (data) levels.
*/
context_data
=
context
->
first_context_data
();
while
(
context_data
)
{
cout
<<
" Level (chain index): "
<<
context_data
->
chain_index
();
if
(
context_data
->
parms_id
()
==
context
->
first_parms_id
())
{
cout
<<
" ...... first_context_data()"
<<
endl
;
}
else
if
(
context_data
->
parms_id
()
==
context
->
last_parms_id
())
{
cout
<<
" ...... last_context_data()"
<<
endl
;
}
else
{
cout
<<
endl
;
}
cout
<<
" parms_id: "
<<
context_data
->
parms_id
()
<<
endl
;
cout
<<
" coeff_modulus primes: "
;
cout
<<
hex
;
for
(
const
auto
&
prime
:
context_data
->
parms
().
coeff_modulus
())
{
cout
<<
prime
.
value
()
<<
" "
;
}
cout
<<
dec
<<
endl
;
cout
<<
"
\\
"
<<
endl
;
cout
<<
"
\\
-->"
;
/*
Step forward in the chain.
*/
context_data
=
context_data
->
next_context_data
();
}
cout
<<
" End of chain reached"
<<
endl
<<
endl
;
/*
We create some keys and check that indeed they appear at the highest level.
*/
KeyGenerator
keygen
(
context
);
auto
public_key
=
keygen
.
public_key
();
auto
secret_key
=
keygen
.
secret_key
();
auto
relin_keys
=
keygen
.
relin_keys_local
();
/*
In this example we create a local version of the GaloisKeys object using
KeyGenerator::galois_keys_local(). In a production setting where the Galois
keys would need to be communicated to a server, it would be much better to
use KeyGenerator::galois_keys(), which outputs a Serializable<GaloisKeys>
object for compressed serialization.
*/
auto
galois_keys
=
keygen
.
galois_keys_local
();
print_line
(
__LINE__
);
cout
<<
"Print the parameter IDs of generated elements."
<<
endl
;
cout
<<
" + public_key: "
<<
public_key
.
parms_id
()
<<
endl
;
cout
<<
" + secret_key: "
<<
secret_key
.
parms_id
()
<<
endl
;
cout
<<
" + relin_keys: "
<<
relin_keys
.
parms_id
()
<<
endl
;
cout
<<
" + galois_keys: "
<<
galois_keys
.
parms_id
()
<<
endl
;
Encryptor
encryptor
(
context
,
public_key
);
Evaluator
evaluator
(
context
);
Decryptor
decryptor
(
context
,
secret_key
);
/*
In the BFV scheme plaintexts do not carry a parms_id, but ciphertexts do. Note
how the freshly encrypted ciphertext is at the highest data level.
*/
Plaintext
plain
(
"1x^3 + 2x^2 + 3x^1 + 4"
);
Ciphertext
encrypted
;
encryptor
.
encrypt
(
plain
,
encrypted
);
cout
<<
" + plain: "
<<
plain
.
parms_id
()
<<
" (not set in BFV)"
<<
endl
;
cout
<<
" + encrypted: "
<<
encrypted
.
parms_id
()
<<
endl
<<
endl
;
/*
`Modulus switching' is a technique of changing the ciphertext parameters down
in the chain. The function Evaluator::mod_switch_to_next always switches to
the next level down the chain, whereas Evaluator::mod_switch_to switches to
a parameter set down the chain corresponding to a given parms_id. However, it
is impossible to switch up in the chain.
*/
print_line
(
__LINE__
);
cout
<<
"Perform modulus switching on encrypted and print."
<<
endl
;
context_data
=
context
->
first_context_data
();
cout
<<
"---->"
;
while
(
context_data
->
next_context_data
())
{
cout
<<
" Level (chain index): "
<<
context_data
->
chain_index
()
<<
endl
;
cout
<<
" parms_id of encrypted: "
<<
encrypted
.
parms_id
()
<<
endl
;
cout
<<
" Noise budget at this level: "
<<
decryptor
.
invariant_noise_budget
(
encrypted
)
<<
" bits"
<<
endl
;
cout
<<
"
\\
"
<<
endl
;
cout
<<
"
\\
-->"
;
evaluator
.
mod_switch_to_next_inplace
(
encrypted
);
context_data
=
context_data
->
next_context_data
();
}
cout
<<
" Level (chain index): "
<<
context_data
->
chain_index
()
<<
endl
;
cout
<<
" parms_id of encrypted: "
<<
encrypted
.
parms_id
()
<<
endl
;
cout
<<
" Noise budget at this level: "
<<
decryptor
.
invariant_noise_budget
(
encrypted
)
<<
" bits"
<<
endl
;
cout
<<
"
\\
"
<<
endl
;
cout
<<
"
\\
-->"
;
cout
<<
" End of chain reached"
<<
endl
<<
endl
;
/*
At this point it is hard to see any benefit in doing this: we lost a huge
amount of noise budget (i.e., computational power) at each switch and seemed
to get nothing in return. Decryption still works.
*/
print_line
(
__LINE__
);
cout
<<
"Decrypt still works after modulus switching."
<<
endl
;
decryptor
.
decrypt
(
encrypted
,
plain
);
cout
<<
" + Decryption of encrypted: "
<<
plain
.
to_string
();
cout
<<
" ...... Correct."
<<
endl
<<
endl
;
/*
However, there is a hidden benefit: the size of the ciphertext depends
linearly on the number of primes in the coefficient modulus. Thus, if there
is no need or intention to perform any further computations on a given
ciphertext, we might as well switch it down to the smallest (last) set of
parameters in the chain before sending it back to the secret key holder for
decryption.
Also the lost noise budget is actually not an issue at all, if we do things
right, as we will see below.
First we recreate the original ciphertext and perform some computations.
*/
cout
<<
"Computation is more efficient with modulus switching."
<<
endl
;
print_line
(
__LINE__
);
cout
<<
"Compute the 8th power."
<<
endl
;
encryptor
.
encrypt
(
plain
,
encrypted
);
cout
<<
" + Noise budget fresh: "
<<
decryptor
.
invariant_noise_budget
(
encrypted
)
<<
" bits"
<<
endl
;
evaluator
.
square_inplace
(
encrypted
);
evaluator
.
relinearize_inplace
(
encrypted
,
relin_keys
);
cout
<<
" + Noise budget of the 2nd power: "
<<
decryptor
.
invariant_noise_budget
(
encrypted
)
<<
" bits"
<<
endl
;
evaluator
.
square_inplace
(
encrypted
);
evaluator
.
relinearize_inplace
(
encrypted
,
relin_keys
);
cout
<<
" + Noise budget of the 4th power: "
<<
decryptor
.
invariant_noise_budget
(
encrypted
)
<<
" bits"
<<
endl
;
/*
Surprisingly, in this case modulus switching has no effect at all on the
noise budget.
*/
evaluator
.
mod_switch_to_next_inplace
(
encrypted
);
cout
<<
" + Noise budget after modulus switching: "
<<
decryptor
.
invariant_noise_budget
(
encrypted
)
<<
" bits"
<<
endl
;
/*
This means that there is no harm at all in dropping some of the coefficient
modulus after doing enough computations. In some cases one might want to
switch to a lower level slightly earlier, actually sacrificing some of the
noise budget in the process, to gain computational performance from having
smaller parameters. We see from the print-out that the next modulus switch
should be done ideally when the noise budget is down to around 25 bits.
*/
evaluator
.
square_inplace
(
encrypted
);
evaluator
.
relinearize_inplace
(
encrypted
,
relin_keys
);
cout
<<
" + Noise budget of the 8th power: "
<<
decryptor
.
invariant_noise_budget
(
encrypted
)
<<
" bits"
<<
endl
;
evaluator
.
mod_switch_to_next_inplace
(
encrypted
);
cout
<<
" + Noise budget after modulus switching: "
<<
decryptor
.
invariant_noise_budget
(
encrypted
)
<<
" bits"
<<
endl
;
/*
At this point the ciphertext still decrypts correctly, has very small size,
and the computation was as efficient as possible. Note that the decryptor
can be used to decrypt a ciphertext at any level in the modulus switching
chain.
*/
decryptor
.
decrypt
(
encrypted
,
plain
);
cout
<<
" + Decryption of the 8th power (hexadecimal) ...... Correct."
<<
endl
;
cout
<<
" "
<<
plain
.
to_string
()
<<
endl
<<
endl
;
/*
In BFV modulus switching is not necessary and in some cases the user might
not want to create the modulus switching chain, except for the highest two
levels. This can be done by passing a bool `false' to SEALContext::Create.
*/
context
=
SEALContext
::
Create
(
parms
,
false
);
/*
We can check that indeed the modulus switching chain has been created only
for the highest two levels (key level and highest data level). The following
loop should execute only once.
*/
cout
<<
"Optionally disable modulus switching chain expansion."
<<
endl
;
print_line
(
__LINE__
);
cout
<<
"Print the modulus switching chain."
<<
endl
;
cout
<<
"---->"
;
for
(
context_data
=
context
->
key_context_data
();
context_data
;
context_data
=
context_data
->
next_context_data
())
{
cout
<<
" Level (chain index): "
<<
context_data
->
chain_index
()
<<
endl
;
cout
<<
" parms_id: "
<<
context_data
->
parms_id
()
<<
endl
;
cout
<<
" coeff_modulus primes: "
;
cout
<<
hex
;
for
(
const
auto
&
prime
:
context_data
->
parms
().
coeff_modulus
())
{
cout
<<
prime
.
value
()
<<
" "
;
}
cout
<<
dec
<<
endl
;
cout
<<
"
\\
"
<<
endl
;
cout
<<
"
\\
-->"
;
}
cout
<<
" End of chain reached"
<<
endl
<<
endl
;
/*
It is very important to understand how this example works since in the CKKS
scheme modulus switching has a much more fundamental purpose and the next
examples will be difficult to understand unless these basic properties are
totally clear.
*/
}
bigpiseal3.5.1/native/examples/4_ckks_basics.cpp
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include "examples.h"
using
namespace
std
;
using
namespace
seal
;
void
example_ckks_basics
()
{
print_example_banner
(
"Example: CKKS Basics"
);
/*
In this example we demonstrate evaluating a polynomial function
PI*x^3 + 0.4*x + 1
on encrypted floating-point input data x for a set of 4096 equidistant points
in the interval [0, 1]. This example demonstrates many of the main features
of the CKKS scheme, but also the challenges in using it.
We start by setting up the CKKS scheme.
*/
EncryptionParameters
parms
(
scheme_type
::
CKKS
);
/*
We saw in `2_encoders.cpp' that multiplication in CKKS causes scales
in ciphertexts to grow. The scale of any ciphertext must not get too close
to the total size of coeff_modulus, or else the ciphertext simply runs out of
room to store the scaled-up plaintext. The CKKS scheme provides a `rescale'
functionality that can reduce the scale, and stabilize the scale expansion.
Rescaling is a kind of modulus switch operation (recall `3_levels.cpp').
As modulus switching, it removes the last of the primes from coeff_modulus,
but as a side-effect it scales down the ciphertext by the removed prime.
Usually we want to have perfect control over how the scales are changed,
which is why for the CKKS scheme it is more common to use carefully selected
primes for the coeff_modulus.
More precisely, suppose that the scale in a CKKS ciphertext is S, and the
last prime in the current coeff_modulus (for the ciphertext) is P. Rescaling
to the next level changes the scale to S/P, and removes the prime P from the
coeff_modulus, as usual in modulus switching. The number of primes limits
how many rescalings can be done, and thus limits the multiplicative depth of
the computation.
It is possible to choose the initial scale freely. One good strategy can be
to is to set the initial scale S and primes P_i in the coeff_modulus to be
very close to each other. If ciphertexts have scale S before multiplication,
they have scale S^2 after multiplication, and S^2/P_i after rescaling. If all
P_i are close to S, then S^2/P_i is close to S again. This way we stabilize the
scales to be close to S throughout the computation. Generally, for a circuit
of depth D, we need to rescale D times, i.e., we need to be able to remove D
primes from the coefficient modulus. Once we have only one prime left in the
coeff_modulus, the remaining prime must be larger than S by a few bits to
preserve the pre-decimal-point value of the plaintext.
Therefore, a generally good strategy is to choose parameters for the CKKS
scheme as follows:
(1) Choose a 60-bit prime as the first prime in coeff_modulus. This will
give the highest precision when decrypting;
(2) Choose another 60-bit prime as the last element of coeff_modulus, as
this will be used as the special prime and should be as large as the
largest of the other primes;
(3) Choose the intermediate primes to be close to each other.
We use CoeffModulus::Create to generate primes of the appropriate size. Note
that our coeff_modulus is 200 bits total, which is below the bound for our
poly_modulus_degree: CoeffModulus::MaxBitCount(8192) returns 218.
*/
size_t
poly_modulus_degree
=
8192
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
Create
(
poly_modulus_degree
,
{
60
,
40
,
40
,
60
}));
/*
We choose the initial scale to be 2^40. At the last level, this leaves us
60-40=20 bits of precision before the decimal point, and enough (roughly
10-20 bits) of precision after the decimal point. Since our intermediate
primes are 40 bits (in fact, they are very close to 2^40), we can achieve
scale stabilization as described above.
*/
double
scale
=
pow
(
2.0
,
40
);
auto
context
=
SEALContext
::
Create
(
parms
);
print_parameters
(
context
);
cout
<<
endl
;
KeyGenerator
keygen
(
context
);
auto
public_key
=
keygen
.
public_key
();
auto
secret_key
=
keygen
.
secret_key
();
auto
relin_keys
=
keygen
.
relin_keys_local
();
Encryptor
encryptor
(
context
,
public_key
);
Evaluator
evaluator
(
context
);
Decryptor
decryptor
(
context
,
secret_key
);
CKKSEncoder
encoder
(
context
);
size_t
slot_count
=
encoder
.
slot_count
();
cout
<<
"Number of slots: "
<<
slot_count
<<
endl
;
vector
<
double
>
input
;
input
.
reserve
(
slot_count
);
double
curr_point
=
0
;
double
step_size
=
1.0
/
(
static_cast
<
double
>
(
slot_count
)
-
1
);
for
(
size_t
i
=
0
;
i
<
slot_count
;
i
++
,
curr_point
+=
step_size
)
{
input
.
push_back
(
curr_point
);
}
cout
<<
"Input vector: "
<<
endl
;
print_vector
(
input
,
3
,
7
);
cout
<<
"Evaluating polynomial PI*x^3 + 0.4x + 1 ..."
<<
endl
;
/*
We create plaintexts for PI, 0.4, and 1 using an overload of CKKSEncoder::encode
that encodes the given floating-point value to every slot in the vector.
*/
Plaintext
plain_coeff3
,
plain_coeff1
,
plain_coeff0
;
encoder
.
encode
(
3.14159265
,
scale
,
plain_coeff3
);
encoder
.
encode
(
0.4
,
scale
,
plain_coeff1
);
encoder
.
encode
(
1.0
,
scale
,
plain_coeff0
);
Plaintext
x_plain
;
print_line
(
__LINE__
);
cout
<<
"Encode input vectors."
<<
endl
;
encoder
.
encode
(
input
,
scale
,
x_plain
);
Ciphertext
x1_encrypted
;
encryptor
.
encrypt
(
x_plain
,
x1_encrypted
);
/*
To compute x^3 we first compute x^2 and relinearize. However, the scale has
now grown to 2^80.
*/
Ciphertext
x3_encrypted
;
print_line
(
__LINE__
);
cout
<<
"Compute x^2 and relinearize:"
<<
endl
;
evaluator
.
square
(
x1_encrypted
,
x3_encrypted
);
evaluator
.
relinearize_inplace
(
x3_encrypted
,
relin_keys
);
cout
<<
" + Scale of x^2 before rescale: "
<<
log2
(
x3_encrypted
.
scale
())
<<
" bits"
<<
endl
;
/*
Now rescale; in addition to a modulus switch, the scale is reduced down by
a factor equal to the prime that was switched away (40-bit prime). Hence, the
new scale should be close to 2^40. Note, however, that the scale is not equal
to 2^40: this is because the 40-bit prime is only close to 2^40.
*/
print_line
(
__LINE__
);
cout
<<
"Rescale x^2."
<<
endl
;
evaluator
.
rescale_to_next_inplace
(
x3_encrypted
);
cout
<<
" + Scale of x^2 after rescale: "
<<
log2
(
x3_encrypted
.
scale
())
<<
" bits"
<<
endl
;
/*
Now x3_encrypted is at a different level than x1_encrypted, which prevents us
from multiplying them to compute x^3. We could simply switch x1_encrypted to
the next parameters in the modulus switching chain. However, since we still
need to multiply the x^3 term with PI (plain_coeff3), we instead compute PI*x
first and multiply that with x^2 to obtain PI*x^3. To this end, we compute
PI*x and rescale it back from scale 2^80 to something close to 2^40.
*/
print_line
(
__LINE__
);
cout
<<
"Compute and rescale PI*x."
<<
endl
;
Ciphertext
x1_encrypted_coeff3
;
evaluator
.
multiply_plain
(
x1_encrypted
,
plain_coeff3
,
x1_encrypted_coeff3
);
cout
<<
" + Scale of PI*x before rescale: "
<<
log2
(
x1_encrypted_coeff3
.
scale
())
<<
" bits"
<<
endl
;
evaluator
.
rescale_to_next_inplace
(
x1_encrypted_coeff3
);
cout
<<
" + Scale of PI*x after rescale: "
<<
log2
(
x1_encrypted_coeff3
.
scale
())
<<
" bits"
<<
endl
;
/*
Since x3_encrypted and x1_encrypted_coeff3 have the same exact scale and use
the same encryption parameters, we can multiply them together. We write the
result to x3_encrypted, relinearize, and rescale. Note that again the scale
is something close to 2^40, but not exactly 2^40 due to yet another scaling
by a prime. We are down to the last level in the modulus switching chain.
*/
print_line
(
__LINE__
);
cout
<<
"Compute, relinearize, and rescale (PI*x)*x^2."
<<
endl
;
evaluator
.
multiply_inplace
(
x3_encrypted
,
x1_encrypted_coeff3
);
evaluator
.
relinearize_inplace
(
x3_encrypted
,
relin_keys
);
cout
<<
" + Scale of PI*x^3 before rescale: "
<<
log2
(
x3_encrypted
.
scale
())
<<
" bits"
<<
endl
;
evaluator
.
rescale_to_next_inplace
(
x3_encrypted
);
cout
<<
" + Scale of PI*x^3 after rescale: "
<<
log2
(
x3_encrypted
.
scale
())
<<
" bits"
<<
endl
;
/*
Next we compute the degree one term. All this requires is one multiply_plain
with plain_coeff1. We overwrite x1_encrypted with the result.
*/
print_line
(
__LINE__
);
cout
<<
"Compute and rescale 0.4*x."
<<
endl
;
evaluator
.
multiply_plain_inplace
(
x1_encrypted
,
plain_coeff1
);
cout
<<
" + Scale of 0.4*x before rescale: "
<<
log2
(
x1_encrypted
.
scale
())
<<
" bits"
<<
endl
;
evaluator
.
rescale_to_next_inplace
(
x1_encrypted
);
cout
<<
" + Scale of 0.4*x after rescale: "
<<
log2
(
x1_encrypted
.
scale
())
<<
" bits"
<<
endl
;
/*
Now we would hope to compute the sum of all three terms. However, there is
a serious problem: the encryption parameters used by all three terms are
different due to modulus switching from rescaling.
Encrypted addition and subtraction require that the scales of the inputs are
the same, and also that the encryption parameters (parms_id) match. If there
is a mismatch, Evaluator will throw an exception.
*/
cout
<<
endl
;
print_line
(
__LINE__
);
cout
<<
"Parameters used by all three terms are different."
<<
endl
;
cout
<<
" + Modulus chain index for x3_encrypted: "
<<
context
->
get_context_data
(
x3_encrypted
.
parms_id
())
->
chain_index
()
<<
endl
;
cout
<<
" + Modulus chain index for x1_encrypted: "
<<
context
->
get_context_data
(
x1_encrypted
.
parms_id
())
->
chain_index
()
<<
endl
;
cout
<<
" + Modulus chain index for plain_coeff0: "
<<
context
->
get_context_data
(
plain_coeff0
.
parms_id
())
->
chain_index
()
<<
endl
;
cout
<<
endl
;
/*
Let us carefully consider what the scales are at this point. We denote the
primes in coeff_modulus as P_0, P_1, P_2, P_3, in this order. P_3 is used as
the special modulus and is not involved in rescalings. After the computations
above the scales in ciphertexts are:
- Product x^2 has scale 2^80 and is at level 2;
- Product PI*x has scale 2^80 and is at level 2;
- We rescaled both down to scale 2^80/P_2 and level 1;
- Product PI*x^3 has scale (2^80/P_2)^2;
- We rescaled it down to scale (2^80/P_2)^2/P_1 and level 0;
- Product 0.4*x has scale 2^80;
- We rescaled it down to scale 2^80/P_2 and level 1;
- The contant term 1 has scale 2^40 and is at level 2.
Although the scales of all three terms are approximately 2^40, their exact
values are different, hence they cannot be added together.
*/
print_line
(
__LINE__
);
cout
<<
"The exact scales of all three terms are different:"
<<
endl
;
ios
old_fmt
(
nullptr
);
old_fmt
.
copyfmt
(
cout
);
cout
<<
fixed
<<
setprecision
(
10
);
cout
<<
" + Exact scale in PI*x^3: "
<<
x3_encrypted
.
scale
()
<<
endl
;
cout
<<
" + Exact scale in 0.4*x: "
<<
x1_encrypted
.
scale
()
<<
endl
;
cout
<<
" + Exact scale in 1: "
<<
plain_coeff0
.
scale
()
<<
endl
;
cout
<<
endl
;
cout
.
copyfmt
(
old_fmt
);
/*
There are many ways to fix this problem. Since P_2 and P_1 are really close
to 2^40, we can simply "lie" to Microsoft SEAL and set the scales to be the
same. For example, changing the scale of PI*x^3 to 2^40 simply means that we
scale the value of PI*x^3 by 2^120/(P_2^2*P_1), which is very close to 1.
This should not result in any noticeable error.
Another option would be to encode 1 with scale 2^80/P_2, do a multiply_plain
with 0.4*x, and finally rescale. In this case we would need to additionally
make sure to encode 1 with appropriate encryption parameters (parms_id).
In this example we will use the first (simplest) approach and simply change
the scale of PI*x^3 and 0.4*x to 2^40.
*/
print_line
(
__LINE__
);
cout
<<
"Normalize scales to 2^40."
<<
endl
;
x3_encrypted
.
scale
()
=
pow
(
2.0
,
40
);
x1_encrypted
.
scale
()
=
pow
(
2.0
,
40
);
/*
We still have a problem with mismatching encryption parameters. This is easy
to fix by using traditional modulus switching (no rescaling). CKKS supports
modulus switching just like the BFV scheme, allowing us to switch away parts
of the coefficient modulus when it is simply not needed.
*/
print_line
(
__LINE__
);
cout
<<
"Normalize encryption parameters to the lowest level."
<<
endl
;
parms_id_type
last_parms_id
=
x3_encrypted
.
parms_id
();
evaluator
.
mod_switch_to_inplace
(
x1_encrypted
,
last_parms_id
);
evaluator
.
mod_switch_to_inplace
(
plain_coeff0
,
last_parms_id
);
/*
All three ciphertexts are now compatible and can be added.
*/
print_line
(
__LINE__
);
cout
<<
"Compute PI*x^3 + 0.4*x + 1."
<<
endl
;
Ciphertext
encrypted_result
;
evaluator
.
add
(
x3_encrypted
,
x1_encrypted
,
encrypted_result
);
evaluator
.
add_plain_inplace
(
encrypted_result
,
plain_coeff0
);
/*
First print the true result.
*/
Plaintext
plain_result
;
print_line
(
__LINE__
);
cout
<<
"Decrypt and decode PI*x^3 + 0.4x + 1."
<<
endl
;
cout
<<
" + Expected result:"
<<
endl
;
vector
<
double
>
true_result
;
for
(
size_t
i
=
0
;
i
<
input
.
size
();
i
++
)
{
double
x
=
input
[
i
];
true_result
.
push_back
((
3.14159265
*
x
*
x
+
0.4
)
*
x
+
1
);
}
print_vector
(
true_result
,
3
,
7
);
/*
Decrypt, decode, and print the result.
*/
decryptor
.
decrypt
(
encrypted_result
,
plain_result
);
vector
<
double
>
result
;
encoder
.
decode
(
plain_result
,
result
);
cout
<<
" + Computed result ...... Correct."
<<
endl
;
print_vector
(
result
,
3
,
7
);
/*
While we did not show any computations on complex numbers in these examples,
the CKKSEncoder would allow us to have done that just as easily. Additions
and multiplications of complex numbers behave just as one would expect.
*/
}
bigpiseal3.5.1/native/examples/5_rotation.cpp
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include "examples.h"
using
namespace
std
;
using
namespace
seal
;
/*
Both the BFV scheme (with BatchEncoder) as well as the CKKS scheme support native
vectorized computations on encrypted numbers. In addition to computing slot-wise,
it is possible to rotate the encrypted vectors cyclically.
*/
void
example_rotation_bfv
()
{
print_example_banner
(
"Example: Rotation / Rotation in BFV"
);
EncryptionParameters
parms
(
scheme_type
::
BFV
);
size_t
poly_modulus_degree
=
8192
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
parms
.
set_plain_modulus
(
PlainModulus
::
Batching
(
poly_modulus_degree
,
20
));
auto
context
=
SEALContext
::
Create
(
parms
);
print_parameters
(
context
);
cout
<<
endl
;
KeyGenerator
keygen
(
context
);
PublicKey
public_key
=
keygen
.
public_key
();
SecretKey
secret_key
=
keygen
.
secret_key
();
RelinKeys
relin_keys
=
keygen
.
relin_keys_local
();
Encryptor
encryptor
(
context
,
public_key
);
Evaluator
evaluator
(
context
);
Decryptor
decryptor
(
context
,
secret_key
);
BatchEncoder
batch_encoder
(
context
);
size_t
slot_count
=
batch_encoder
.
slot_count
();
size_t
row_size
=
slot_count
/
2
;
cout
<<
"Plaintext matrix row size: "
<<
row_size
<<
endl
;
vector
<
uint64_t
>
pod_matrix
(
slot_count
,
0ULL
);
pod_matrix
[
0
]
=
0ULL
;
pod_matrix
[
1
]
=
1ULL
;
pod_matrix
[
2
]
=
2ULL
;
pod_matrix
[
3
]
=
3ULL
;
pod_matrix
[
row_size
]
=
4ULL
;
pod_matrix
[
row_size
+
1
]
=
5ULL
;
pod_matrix
[
row_size
+
2
]
=
6ULL
;
pod_matrix
[
row_size
+
3
]
=
7ULL
;
cout
<<
"Input plaintext matrix:"
<<
endl
;
print_matrix
(
pod_matrix
,
row_size
);
/*
First we use BatchEncoder to encode the matrix into a plaintext. We encrypt
the plaintext as usual.
*/
Plaintext
plain_matrix
;
print_line
(
__LINE__
);
cout
<<
"Encode and encrypt."
<<
endl
;
batch_encoder
.
encode
(
pod_matrix
,
plain_matrix
);
Ciphertext
encrypted_matrix
;
encryptor
.
encrypt
(
plain_matrix
,
encrypted_matrix
);
cout
<<
" + Noise budget in fresh encryption: "
<<
decryptor
.
invariant_noise_budget
(
encrypted_matrix
)
<<
" bits"
<<
endl
;
cout
<<
endl
;
/*
Rotations require yet another type of special key called `Galois keys'. These
are easily obtained from the KeyGenerator.
*/
GaloisKeys
gal_keys
=
keygen
.
galois_keys_local
();
/*
Now rotate both matrix rows 3 steps to the left, decrypt, decode, and print.
*/
print_line
(
__LINE__
);
cout
<<
"Rotate rows 3 steps left."
<<
endl
;
evaluator
.
rotate_rows_inplace
(
encrypted_matrix
,
3
,
gal_keys
);
Plaintext
plain_result
;
cout
<<
" + Noise budget after rotation: "
<<
decryptor
.
invariant_noise_budget
(
encrypted_matrix
)
<<
" bits"
<<
endl
;
cout
<<
" + Decrypt and decode ...... Correct."
<<
endl
;
decryptor
.
decrypt
(
encrypted_matrix
,
plain_result
);
batch_encoder
.
decode
(
plain_result
,
pod_matrix
);
print_matrix
(
pod_matrix
,
row_size
);
/*
We can also rotate the columns, i.e., swap the rows.
*/
print_line
(
__LINE__
);
cout
<<
"Rotate columns."
<<
endl
;
evaluator
.
rotate_columns_inplace
(
encrypted_matrix
,
gal_keys
);
cout
<<
" + Noise budget after rotation: "
<<
decryptor
.
invariant_noise_budget
(
encrypted_matrix
)
<<
" bits"
<<
endl
;
cout
<<
" + Decrypt and decode ...... Correct."
<<
endl
;
decryptor
.
decrypt
(
encrypted_matrix
,
plain_result
);
batch_encoder
.
decode
(
plain_result
,
pod_matrix
);
print_matrix
(
pod_matrix
,
row_size
);
/*
Finally, we rotate the rows 4 steps to the right, decrypt, decode, and print.
*/
print_line
(
__LINE__
);
cout
<<
"Rotate rows 4 steps right."
<<
endl
;
evaluator
.
rotate_rows_inplace
(
encrypted_matrix
,
-
4
,
gal_keys
);
cout
<<
" + Noise budget after rotation: "
<<
decryptor
.
invariant_noise_budget
(
encrypted_matrix
)
<<
" bits"
<<
endl
;
cout
<<
" + Decrypt and decode ...... Correct."
<<
endl
;
decryptor
.
decrypt
(
encrypted_matrix
,
plain_result
);
batch_encoder
.
decode
(
plain_result
,
pod_matrix
);
print_matrix
(
pod_matrix
,
row_size
);
/*
Note that rotations do not consume any noise budget. However, this is only
the case when the special prime is at least as large as the other primes. The
same holds for relinearization. Microsoft SEAL does not require that the
special prime is of any particular size, so ensuring this is the case is left
for the user to do.
*/
}
void
example_rotation_ckks
()
{
print_example_banner
(
"Example: Rotation / Rotation in CKKS"
);
/*
Rotations in the CKKS scheme work very similarly to rotations in BFV.
*/
EncryptionParameters
parms
(
scheme_type
::
CKKS
);
size_t
poly_modulus_degree
=
8192
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
Create
(
poly_modulus_degree
,
{
40
,
40
,
40
,
40
,
40
}));
auto
context
=
SEALContext
::
Create
(
parms
);
print_parameters
(
context
);
cout
<<
endl
;
KeyGenerator
keygen
(
context
);
PublicKey
public_key
=
keygen
.
public_key
();
SecretKey
secret_key
=
keygen
.
secret_key
();
RelinKeys
relin_keys
=
keygen
.
relin_keys_local
();
GaloisKeys
gal_keys
=
keygen
.
galois_keys_local
();
Encryptor
encryptor
(
context
,
public_key
);
Evaluator
evaluator
(
context
);
Decryptor
decryptor
(
context
,
secret_key
);
CKKSEncoder
ckks_encoder
(
context
);
size_t
slot_count
=
ckks_encoder
.
slot_count
();
cout
<<
"Number of slots: "
<<
slot_count
<<
endl
;
vector
<
double
>
input
;
input
.
reserve
(
slot_count
);
double
curr_point
=
0
;
double
step_size
=
1.0
/
(
static_cast
<
double
>
(
slot_count
)
-
1
);
for
(
size_t
i
=
0
;
i
<
slot_count
;
i
++
,
curr_point
+=
step_size
)
{
input
.
push_back
(
curr_point
);
}
cout
<<
"Input vector:"
<<
endl
;
print_vector
(
input
,
3
,
7
);
auto
scale
=
pow
(
2.0
,
50
);
print_line
(
__LINE__
);
cout
<<
"Encode and encrypt."
<<
endl
;
Plaintext
plain
;
ckks_encoder
.
encode
(
input
,
scale
,
plain
);
Ciphertext
encrypted
;
encryptor
.
encrypt
(
plain
,
encrypted
);
Ciphertext
rotated
;
print_line
(
__LINE__
);
cout
<<
"Rotate 2 steps left."
<<
endl
;
evaluator
.
rotate_vector
(
encrypted
,
2
,
gal_keys
,
rotated
);
cout
<<
" + Decrypt and decode ...... Correct."
<<
endl
;
decryptor
.
decrypt
(
rotated
,
plain
);
vector
<
double
>
result
;
ckks_encoder
.
decode
(
plain
,
result
);
print_vector
(
result
,
3
,
7
);
/*
With the CKKS scheme it is also possible to evaluate a complex conjugation on
a vector of encrypted complex numbers, using Evaluator::complex_conjugate.
This is in fact a kind of rotation, and requires also Galois keys.
*/
}
void
example_rotation
()
{
print_example_banner
(
"Example: Rotation"
);
/*
Run all rotation examples.
*/
example_rotation_bfv
();
example_rotation_ckks
();
}
bigpiseal3.5.1/native/examples/6_serialization.cpp
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include "examples.h"
using
namespace
std
;
using
namespace
seal
;
/*
In this example we show how serialization works in Microsoft SEAL. Specifically,
we present important concepts that enable the user to optimize the data size when
communicating ciphertexts and keys for outsourced computation. Unlike the previous
examples, we organize this one in a client-server style for maximal clarity. The
server selects encryption parameters, the client generates keys, the server does
the encrypted computation, and the client decrypts.
*/
void
example_serialization
()
{
print_example_banner
(
"Example: Serialization"
);
/*
We require ZLIB support for this example to be available.
*/
#ifndef SEAL_USE_ZLIB
cout
<<
"ZLIB support is not enabled; this example is not available."
<<
endl
;
cout
<<
endl
;
return
;
#else
/*
To simulate client-server interaction, we set up a shared C++ stream. In real
use-cases this can be a network buffer, a filestream, or any shared resource.
It is critical to note that all data serialized by Microsoft SEAL is in binary
form, so it is not meaningful to print the data as ASCII characters. Encodings
such as Base64 would increase the data size, which is already a bottleneck in
homomorphic encryption. Hence, serialization into text is not supported or
recommended.
We feel it is important to remind users that filestream serialization will
always require the ios::binary flag to signal that the serialized data is
binary data and not text. For example, an appropriate output filestream could
be set up as:
ofstream ofs("filename", ios::binary);
In this example we use an std::stringstream, where the ios::binary flag is
not needed. Note that the default constructor of std::stringstream opens the
stream with ios::in | ios::out so both reading and writing will be possible.
*/
stringstream
parms_stream
;
stringstream
data_stream
;
stringstream
sk_stream
;
/*
The server first determines the computation and sets encryption parameters
accordingly.
*/
{
EncryptionParameters
parms
(
scheme_type
::
CKKS
);
size_t
poly_modulus_degree
=
8192
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
Create
(
poly_modulus_degree
,
{
50
,
20
,
50
}));
/*
Serialization of the encryption parameters to our shared stream is very
simple with the EncryptionParameters::save function.
*/
auto
size
=
parms
.
save
(
parms_stream
);
/*
The return value of this function is the actual byte count of data written
to the stream.
*/
print_line
(
__LINE__
);
cout
<<
"EncryptionParameters: wrote "
<<
size
<<
" bytes"
<<
endl
;
/*
Before moving on, we will take some time to discuss further options in
serialization. These will become particularly important when the user
needs to optimize communication and storage sizes.
*/
/*
It is possible to enable or disable ZLIB ("deflate") compression for
serialization by providing EncryptionParameters::save with the desired
compression mode as in the following examples:
auto size = parms.save(shared_stream, compr_mode_type::none);
auto size = parms.save(shared_stream, compr_mode_type::deflate);
If Microsoft SEAL is compiled with ZLIB support, the default is to use
compr_mode_type::deflate, so to instead disable compression one would use
the first version of the two.
*/
/*
It is also possible to serialize data directly to a buffer. For this, one
needs to know an upper bound for the required buffer size, which can be
obtained using the EncryptionParameters::save_size function. This function
also accepts the desired compression mode, with compr_mode_type::deflate
being the default when Microsoft SEAL is compiled with ZLIB support.
In more detail, the output of EncryptionParameters::save_size is as follows:
- Exact buffer size required for compr_mode_type::none;
- Upper bound on the size required for compr_mode_type::deflate.
As we can see from the print-out, the sizes returned by these functions
are significantly larger than the compressed size written into the shared
stream in the beginning. This is normal: compression yielded a significant
improvement in the data size, yet it is hard to estimate the size of the
compressed data.
*/
print_line
(
__LINE__
);
cout
<<
"EncryptionParameters: data size upper bound (compr_mode_type::none): "
<<
parms
.
save_size
(
compr_mode_type
::
none
)
<<
endl
;
cout
<<
" "
<<
"EncryptionParameters: data size upper bound (compr_mode_type::deflate): "
<<
parms
.
save_size
(
compr_mode_type
::
deflate
)
<<
endl
;
/*
As an example, we now serialize the encryption parameters to a fixed size
buffer.
*/
vector
<
SEAL_BYTE
>
byte_buffer
(
static_cast
<
size_t
>
(
parms
.
save_size
()));
parms
.
save
(
reinterpret_cast
<
SEAL_BYTE
*>
(
byte_buffer
.
data
()),
byte_buffer
.
size
());
/*
To illustrate deserialization, we load back the encryption parameters
from our buffer into another instance of EncryptionParameters. Note how
EncryptionParameters::load in this case requires the size of the buffer,
which is larger than the actual data size of the compressed parameters.
The serialization format includes the true size of the data and the size
of the buffer is only used for a sanity check.
*/
EncryptionParameters
parms2
;
parms2
.
load
(
reinterpret_cast
<
const
SEAL_BYTE
*>
(
byte_buffer
.
data
()),
byte_buffer
.
size
());
/*
We can check that the saved and loaded encryption parameters indeed match.
*/
print_line
(
__LINE__
);
cout
<<
"EncryptionParameters: parms == parms2: "
<<
boolalpha
<<
(
parms
==
parms2
)
<<
endl
;
/*
The functions presented and used here exist for all Microsoft SEAL objects
that are meaningful to serialize. However, it is important to understand
more advanced techniques that can be used for further compressing the data
size. We will present these techniques below.
*/
}
/*
Client starts by loading the encryption parameters, sets up the SEALContext,
and creates the required keys.
*/
{
EncryptionParameters
parms
;
parms
.
load
(
parms_stream
);
/*
Seek the parms_stream get head back to beginning of the stream because we
will use the same stream to read the parameters repeatedly.
*/
parms_stream
.
seekg
(
0
,
parms_stream
.
beg
);
auto
context
=
SEALContext
::
Create
(
parms
);
KeyGenerator
keygen
(
context
);
auto
sk
=
keygen
.
secret_key
();
auto
pk
=
keygen
.
public_key
();
/*
We need to save the secret key so we can decrypt later.
*/
sk
.
save
(
sk_stream
);
/*
In this example we will also use relinearization keys. For realinearization
and Galois keys the KeyGenerator::relin_keys and KeyGenerator::galois_keys
functions return special Serializable<T> objects. These objects are meant
to be serialized and never used locally. On the other hand, for local use
of RelinKeys and GaloisKeys, the functions KeyGenerator::relin_keys_local
and KeyGenerator::galois_keys_local can be used to create the RelinKeys
and GaloisKeys objects directly. The difference is that the Serializable<T>
objects contain a partly seeded version of the RelinKeys (or GaloisKeys)
that will result in a significantly smaller size when serialized. Using
this method has no impact on security. Such seeded RelinKeys (GaloisKeys)
must be expanded before being used in computations; this is automatically
done by deserialization.
*/
Serializable
<
RelinKeys
>
rlk
=
keygen
.
relin_keys
();
/*
Before continuing, we demonstrate the significant space saving from this
method.
*/
auto
size_rlk
=
rlk
.
save
(
data_stream
);
RelinKeys
rlk_local
=
keygen
.
relin_keys_local
();
auto
size_rlk_local
=
rlk_local
.
save
(
data_stream
);
/*
Now compare the serialized sizes of rlk and rlk_local.
*/
print_line
(
__LINE__
);
cout
<<
"Serializable<RelinKeys>: wrote "
<<
size_rlk
<<
" bytes"
<<
endl
;
cout
<<
" "
<<
"RelinKeys (local): wrote "
<<
size_rlk_local
<<
" bytes"
<<
endl
;
/*
Seek back in data_stream to where rlk data ended, i.e., size_rlk_local
bytes backwards from current position.
*/
data_stream
.
seekp
(
-
size_rlk_local
,
data_stream
.
cur
);
/*
Next set up the CKKSEncoder and Encryptor, and encrypt some numbers.
*/
double
scale
=
pow
(
2.0
,
20
);
CKKSEncoder
encoder
(
context
);
Plaintext
plain1
,
plain2
;
encoder
.
encode
(
2.3
,
scale
,
plain1
);
encoder
.
encode
(
4.5
,
scale
,
plain2
);
Encryptor
encryptor
(
context
,
pk
);
Ciphertext
encrypted1
,
encrypted2
;
encryptor
.
encrypt
(
plain1
,
encrypted1
);
encryptor
.
encrypt
(
plain2
,
encrypted2
);
/*
Now, we could serialize both encrypted1 and encrypted2 to data_stream
using Ciphertext::save. However, for this example, we demonstrate another
size-saving trick that can come in handy.
As you noticed, we set up the Encryptor using the public key. Clearly this
indicates that the CKKS scheme is a public-key encryption scheme. However,
both BFV and CKKS can operate also in a symmetric-key mode. This can be
beneficial when the public-key functionality is not exactly needed, like
in simple outsourced computation scenarios. The benefit is that in these
cases it is possible to produce ciphertexts that are partly seeded, hence
significantly smaller. Such ciphertexts must be expanded before being used
in computations; this is automatically done by deserialization.
To use symmetric-key encryption, we need to set up the Encryptor with the
secret key instead.
*/
Encryptor
sym_encryptor
(
context
,
sk
);
Serializable
<
Ciphertext
>
sym_encrypted1
=
sym_encryptor
.
encrypt_symmetric
(
plain1
);
Serializable
<
Ciphertext
>
sym_encrypted2
=
sym_encryptor
.
encrypt_symmetric
(
plain2
);
/*
Before continuing, we demonstrate the significant space saving from this
method.
*/
auto
size_sym_encrypted1
=
sym_encrypted1
.
save
(
data_stream
);
auto
size_encrypted1
=
encrypted1
.
save
(
data_stream
);
/*
Now compare the serialized sizes of encrypted1 and sym_encrypted1.
*/
print_line
(
__LINE__
);
cout
<<
"Serializable<Ciphertext> (symmetric-key): wrote "
<<
size_sym_encrypted1
<<
" bytes"
<<
endl
;
cout
<<
" "
<<
"Ciphertext (public-key): wrote "
<<
size_encrypted1
<<
" bytes"
<<
endl
;
/*
Seek back in data_stream to where sym_encrypted1 data ended, i.e.,
size_encrypted1 bytes backwards from current position and write
sym_encrypted2 right after sym_encrypted1.
*/
data_stream
.
seekp
(
-
size_encrypted1
,
data_stream
.
cur
);
sym_encrypted2
.
save
(
data_stream
);
/*
We have seen how using KeyGenerator::relin_keys (KeyGenerator::galois_keys)
can result in huge space savings over the local variants when the objects
are not needed for local use. We have seen how symmetric-key encryption
can be used to achieve much smaller ciphertext sizes when the public-key
functionality is not needed.
We would also like to draw attention to the fact there we could easily
serialize multiple Microsoft SEAL objects sequentially in a stream. Each
object writes its own size into the stream, so deserialization knows
exactly how many bytes to read. We will see this working next.
Finally, we would like to point out that none of these methods provide any
space savings unless Microsoft SEAL is compiled with ZLIB support, or when
serialized with compr_mode_type::none.
*/
}
/*
The server can now compute on the encrypted data. We will recreate the
SEALContext and set up an Evaluator here.
*/
{
EncryptionParameters
parms
;
parms
.
load
(
parms_stream
);
parms_stream
.
seekg
(
0
,
parms_stream
.
beg
);
auto
context
=
SEALContext
::
Create
(
parms
);
Evaluator
evaluator
(
context
);
/*
Next we need to load relinearization keys and the ciphertexts from our
data_stream.
*/
RelinKeys
rlk
;
Ciphertext
encrypted1
,
encrypted2
;
/*
Deserialization is as easy as serialization.
*/
rlk
.
load
(
context
,
data_stream
);
encrypted1
.
load
(
context
,
data_stream
);
encrypted2
.
load
(
context
,
data_stream
);
/*
Compute the product, rescale, and relinearize.
*/
Ciphertext
encrypted_prod
;
evaluator
.
multiply
(
encrypted1
,
encrypted2
,
encrypted_prod
);
evaluator
.
relinearize_inplace
(
encrypted_prod
,
rlk
);
evaluator
.
rescale_to_next_inplace
(
encrypted_prod
);
/*
We use data_stream to communicate encrypted_prod back to the client. There
is no way to save the encrypted_prod as Serializable<Ciphertext> even
though it is still a symmetric-key encryption: only freshly encrypted
ciphertexts can be seeded. Note how the size of the result ciphertext is
smaller than the size of a fresh ciphertext because it is at a lower level
due to the rescale operation.
*/
data_stream
.
seekp
(
0
,
parms_stream
.
beg
);
data_stream
.
seekg
(
0
,
parms_stream
.
beg
);
auto
size_encrypted_prod
=
encrypted_prod
.
save
(
data_stream
);
print_line
(
__LINE__
);
cout
<<
"Ciphertext (symmetric-key): wrote "
<<
size_encrypted_prod
<<
" bytes"
<<
endl
;
}
/*
In the final step the client decrypts the result.
*/
{
EncryptionParameters
parms
;
parms
.
load
(
parms_stream
);
parms_stream
.
seekg
(
0
,
parms_stream
.
beg
);
auto
context
=
SEALContext
::
Create
(
parms
);
/*
Load back the secret key from sk_stream.
*/
SecretKey
sk
;
sk
.
load
(
context
,
sk_stream
);
Decryptor
decryptor
(
context
,
sk
);
CKKSEncoder
encoder
(
context
);
Ciphertext
encrypted_result
;
encrypted_result
.
load
(
context
,
data_stream
);
Plaintext
plain_result
;
decryptor
.
decrypt
(
encrypted_result
,
plain_result
);
vector
<
double
>
result
;
encoder
.
decode
(
plain_result
,
result
);
print_line
(
__LINE__
);
cout
<<
"Result: "
<<
endl
;
print_vector
(
result
,
3
,
7
);
}
/*
Finally, we give a little bit more explanation of the structure of data
serialized by Microsoft SEAL. Serialized data always starts with a 16-byte
SEALHeader struct, as defined in native/src/seal/serialization.h, and is
followed by the possibly compressed data for the object.
A SEALHeader contains the following data:
[offset 0] 2-byte magic number 0xA15E (Serialization::seal_magic)
[offset 2] 1-byte indicating the header size in bytes (always 16)
[offset 3] 1-byte indicating the Microsoft SEAL major version number
[offset 4] 1-byte indicating the Microsoft SEAL minor version number
[offset 5] 1-byte indicating the compression mode type
[offset 6] 2-byte reserved field (unused)
[offset 8] 8-byte size in bytes of the serialized data, including the header
Currently Microsoft SEAL supports only little-endian systems.
As an example, we demonstrate the SEALHeader created by saving a plaintext.
Note that the SEALHeader is never compressed, so there is no need to specify
the compression mode.
*/
Plaintext
pt
(
"1x^2 + 3"
);
stringstream
stream
;
auto
data_size
=
pt
.
save
(
stream
);
/*
We can now load just the SEALHeader back from the stream as follows.
*/
Serialization
::
SEALHeader
header
;
Serialization
::
LoadHeader
(
stream
,
header
);
/*
Now confirm that the size of data written to stream matches with what is
indicated by the SEALHeader.
*/
print_line
(
__LINE__
);
cout
<<
"Size written to stream: "
<<
data_size
<<
" bytes"
<<
endl
;
cout
<<
" "
<<
"Size indicated in SEALHeader: "
<<
header
.
size
<<
" bytes"
<<
endl
;
cout
<<
endl
;
#endif
}
bigpiseal3.5.1/native/examples/7_performance.cpp
0 → 100644
View file @
c6c9d2f5
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include "examples.h"
using
namespace
std
;
using
namespace
seal
;
void
bfv_performance_test
(
shared_ptr
<
SEALContext
>
context
)
{
chrono
::
high_resolution_clock
::
time_point
time_start
,
time_end
;
print_parameters
(
context
);
cout
<<
endl
;
auto
&
parms
=
context
->
first_context_data
()
->
parms
();
auto
&
plain_modulus
=
parms
.
plain_modulus
();
size_t
poly_modulus_degree
=
parms
.
poly_modulus_degree
();
cout
<<
"Generating secret/public keys: "
;
KeyGenerator
keygen
(
context
);
cout
<<
"Done"
<<
endl
;
auto
secret_key
=
keygen
.
secret_key
();
auto
public_key
=
keygen
.
public_key
();
RelinKeys
relin_keys
;
GaloisKeys
gal_keys
;
chrono
::
microseconds
time_diff
;
if
(
context
->
using_keyswitching
())
{
/*
Generate relinearization keys.
*/
cout
<<
"Generating relinearization keys: "
;
time_start
=
chrono
::
high_resolution_clock
::
now
();
relin_keys
=
keygen
.
relin_keys_local
();
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_diff
=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
cout
<<
"Done ["
<<
time_diff
.
count
()
<<
" microseconds]"
<<
endl
;
if
(
!
context
->
key_context_data
()
->
qualifiers
().
using_batching
)
{
cout
<<
"Given encryption parameters do not support batching."
<<
endl
;
return
;
}
/*
Generate Galois keys. In larger examples the Galois keys can use a lot of
memory, which can be a problem in constrained systems. The user should
try some of the larger runs of the test and observe their effect on the
memory pool allocation size. The key generation can also take a long time,
as can be observed from the print-out.
*/
cout
<<
"Generating Galois keys: "
;
time_start
=
chrono
::
high_resolution_clock
::
now
();
gal_keys
=
keygen
.
galois_keys_local
();
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_diff
=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
cout
<<
"Done ["
<<
time_diff
.
count
()
<<
" microseconds]"
<<
endl
;
}
Encryptor
encryptor
(
context
,
public_key
);
Decryptor
decryptor
(
context
,
secret_key
);
Evaluator
evaluator
(
context
);
BatchEncoder
batch_encoder
(
context
);
IntegerEncoder
encoder
(
context
);
/*
These will hold the total times used by each operation.
*/
chrono
::
microseconds
time_batch_sum
(
0
);
chrono
::
microseconds
time_unbatch_sum
(
0
);
chrono
::
microseconds
time_encrypt_sum
(
0
);
chrono
::
microseconds
time_decrypt_sum
(
0
);
chrono
::
microseconds
time_add_sum
(
0
);
chrono
::
microseconds
time_multiply_sum
(
0
);
chrono
::
microseconds
time_multiply_plain_sum
(
0
);
chrono
::
microseconds
time_square_sum
(
0
);
chrono
::
microseconds
time_relinearize_sum
(
0
);
chrono
::
microseconds
time_rotate_rows_one_step_sum
(
0
);
chrono
::
microseconds
time_rotate_rows_random_sum
(
0
);
chrono
::
microseconds
time_rotate_columns_sum
(
0
);
/*
How many times to run the test?
*/
long
long
count
=
10
;
/*
Populate a vector of values to batch.
*/
size_t
slot_count
=
batch_encoder
.
slot_count
();
vector
<
uint64_t
>
pod_vector
;
random_device
rd
;
for
(
size_t
i
=
0
;
i
<
slot_count
;
i
++
)
{
pod_vector
.
push_back
(
rd
()
%
plain_modulus
.
value
());
}
cout
<<
"Running tests "
;
for
(
long
long
i
=
0
;
i
<
count
;
i
++
)
{
/*
[Batching]
There is nothing unusual here. We batch our random plaintext matrix
into the polynomial. Note how the plaintext we create is of the exactly
right size so unnecessary reallocations are avoided.
*/
Plaintext
plain
(
parms
.
poly_modulus_degree
(),
0
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
batch_encoder
.
encode
(
pod_vector
,
plain
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_batch_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Unbatching]
We unbatch what we just batched.
*/
vector
<
uint64_t
>
pod_vector2
(
slot_count
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
batch_encoder
.
decode
(
plain
,
pod_vector2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_unbatch_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
if
(
pod_vector2
!=
pod_vector
)
{
throw
runtime_error
(
"Batch/unbatch failed. Something is wrong."
);
}
/*
[Encryption]
We make sure our ciphertext is already allocated and large enough
to hold the encryption with these encryption parameters. We encrypt
our random batched matrix here.
*/
Ciphertext
encrypted
(
context
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
encryptor
.
encrypt
(
plain
,
encrypted
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_encrypt_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Decryption]
We decrypt what we just encrypted.
*/
Plaintext
plain2
(
poly_modulus_degree
,
0
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
decryptor
.
decrypt
(
encrypted
,
plain2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_decrypt_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
if
(
plain2
!=
plain
)
{
throw
runtime_error
(
"Encrypt/decrypt failed. Something is wrong."
);
}
/*
[Add]
We create two ciphertexts and perform a few additions with them.
*/
Ciphertext
encrypted1
(
context
);
encryptor
.
encrypt
(
encoder
.
encode
(
static_cast
<
uint64_t
>
(
i
)),
encrypted1
);
Ciphertext
encrypted2
(
context
);
encryptor
.
encrypt
(
encoder
.
encode
(
static_cast
<
uint64_t
>
(
i
+
1
)),
encrypted2
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
add_inplace
(
encrypted1
,
encrypted1
);
evaluator
.
add_inplace
(
encrypted2
,
encrypted2
);
evaluator
.
add_inplace
(
encrypted1
,
encrypted2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_add_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Multiply]
We multiply two ciphertexts. Since the size of the result will be 3,
and will overwrite the first argument, we reserve first enough memory
to avoid reallocating during multiplication.
*/
encrypted1
.
reserve
(
3
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
multiply_inplace
(
encrypted1
,
encrypted2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_multiply_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Multiply Plain]
We multiply a ciphertext with a random plaintext. Recall that
multiply_plain does not change the size of the ciphertext so we use
encrypted2 here.
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
multiply_plain_inplace
(
encrypted2
,
plain
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_multiply_plain_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Square]
We continue to use encrypted2. Now we square it; this should be
faster than generic homomorphic multiplication.
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
square_inplace
(
encrypted2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_square_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
if
(
context
->
using_keyswitching
())
{
/*
[Relinearize]
Time to get back to encrypted1. We now relinearize it back
to size 2. Since the allocation is currently big enough to
contain a ciphertext of size 3, no costly reallocations are
needed in the process.
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
relinearize_inplace
(
encrypted1
,
relin_keys
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_relinearize_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Rotate Rows One Step]
We rotate matrix rows by one step left and measure the time.
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
rotate_rows_inplace
(
encrypted
,
1
,
gal_keys
);
evaluator
.
rotate_rows_inplace
(
encrypted
,
-
1
,
gal_keys
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_rotate_rows_one_step_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
;
/*
[Rotate Rows Random]
We rotate matrix rows by a random number of steps. This is much more
expensive than rotating by just one step.
*/
size_t
row_size
=
batch_encoder
.
slot_count
()
/
2
;
int
random_rotation
=
static_cast
<
int
>
(
rd
()
%
row_size
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
rotate_rows_inplace
(
encrypted
,
random_rotation
,
gal_keys
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_rotate_rows_random_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Rotate Columns]
Nothing surprising here.
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
rotate_columns_inplace
(
encrypted
,
gal_keys
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_rotate_columns_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
}
/*
Print a dot to indicate progress.
*/
cout
<<
"."
;
cout
.
flush
();
}
cout
<<
" Done"
<<
endl
<<
endl
;
cout
.
flush
();
auto
avg_batch
=
time_batch_sum
.
count
()
/
count
;
auto
avg_unbatch
=
time_unbatch_sum
.
count
()
/
count
;
auto
avg_encrypt
=
time_encrypt_sum
.
count
()
/
count
;
auto
avg_decrypt
=
time_decrypt_sum
.
count
()
/
count
;
auto
avg_add
=
time_add_sum
.
count
()
/
(
3
*
count
);
auto
avg_multiply
=
time_multiply_sum
.
count
()
/
count
;
auto
avg_multiply_plain
=
time_multiply_plain_sum
.
count
()
/
count
;
auto
avg_square
=
time_square_sum
.
count
()
/
count
;
auto
avg_relinearize
=
time_relinearize_sum
.
count
()
/
count
;
auto
avg_rotate_rows_one_step
=
time_rotate_rows_one_step_sum
.
count
()
/
(
2
*
count
);
auto
avg_rotate_rows_random
=
time_rotate_rows_random_sum
.
count
()
/
count
;
auto
avg_rotate_columns
=
time_rotate_columns_sum
.
count
()
/
count
;
cout
<<
"Average batch: "
<<
avg_batch
<<
" microseconds"
<<
endl
;
cout
<<
"Average unbatch: "
<<
avg_unbatch
<<
" microseconds"
<<
endl
;
cout
<<
"Average encrypt: "
<<
avg_encrypt
<<
" microseconds"
<<
endl
;
cout
<<
"Average decrypt: "
<<
avg_decrypt
<<
" microseconds"
<<
endl
;
cout
<<
"Average add: "
<<
avg_add
<<
" microseconds"
<<
endl
;
cout
<<
"Average multiply: "
<<
avg_multiply
<<
" microseconds"
<<
endl
;
cout
<<
"Average multiply plain: "
<<
avg_multiply_plain
<<
" microseconds"
<<
endl
;
cout
<<
"Average square: "
<<
avg_square
<<
" microseconds"
<<
endl
;
if
(
context
->
using_keyswitching
())
{
cout
<<
"Average relinearize: "
<<
avg_relinearize
<<
" microseconds"
<<
endl
;
cout
<<
"Average rotate rows one step: "
<<
avg_rotate_rows_one_step
<<
" microseconds"
<<
endl
;
cout
<<
"Average rotate rows random: "
<<
avg_rotate_rows_random
<<
" microseconds"
<<
endl
;
cout
<<
"Average rotate columns: "
<<
avg_rotate_columns
<<
" microseconds"
<<
endl
;
}
cout
.
flush
();
}
void
ckks_performance_test
(
shared_ptr
<
SEALContext
>
context
)
{
chrono
::
high_resolution_clock
::
time_point
time_start
,
time_end
;
print_parameters
(
context
);
cout
<<
endl
;
auto
&
parms
=
context
->
first_context_data
()
->
parms
();
size_t
poly_modulus_degree
=
parms
.
poly_modulus_degree
();
cout
<<
"Generating secret/public keys: "
;
KeyGenerator
keygen
(
context
);
cout
<<
"Done"
<<
endl
;
auto
secret_key
=
keygen
.
secret_key
();
auto
public_key
=
keygen
.
public_key
();
RelinKeys
relin_keys
;
GaloisKeys
gal_keys
;
chrono
::
microseconds
time_diff
;
if
(
context
->
using_keyswitching
())
{
cout
<<
"Generating relinearization keys: "
;
time_start
=
chrono
::
high_resolution_clock
::
now
();
relin_keys
=
keygen
.
relin_keys_local
();
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_diff
=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
cout
<<
"Done ["
<<
time_diff
.
count
()
<<
" microseconds]"
<<
endl
;
if
(
!
context
->
first_context_data
()
->
qualifiers
().
using_batching
)
{
cout
<<
"Given encryption parameters do not support batching."
<<
endl
;
return
;
}
cout
<<
"Generating Galois keys: "
;
time_start
=
chrono
::
high_resolution_clock
::
now
();
gal_keys
=
keygen
.
galois_keys_local
();
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_diff
=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
cout
<<
"Done ["
<<
time_diff
.
count
()
<<
" microseconds]"
<<
endl
;
}
Encryptor
encryptor
(
context
,
public_key
);
Decryptor
decryptor
(
context
,
secret_key
);
Evaluator
evaluator
(
context
);
CKKSEncoder
ckks_encoder
(
context
);
chrono
::
microseconds
time_encode_sum
(
0
);
chrono
::
microseconds
time_decode_sum
(
0
);
chrono
::
microseconds
time_encrypt_sum
(
0
);
chrono
::
microseconds
time_decrypt_sum
(
0
);
chrono
::
microseconds
time_add_sum
(
0
);
chrono
::
microseconds
time_multiply_sum
(
0
);
chrono
::
microseconds
time_multiply_plain_sum
(
0
);
chrono
::
microseconds
time_square_sum
(
0
);
chrono
::
microseconds
time_relinearize_sum
(
0
);
chrono
::
microseconds
time_rescale_sum
(
0
);
chrono
::
microseconds
time_rotate_one_step_sum
(
0
);
chrono
::
microseconds
time_rotate_random_sum
(
0
);
chrono
::
microseconds
time_conjugate_sum
(
0
);
/*
How many times to run the test?
*/
long
long
count
=
10
;
/*
Populate a vector of floating-point values to batch.
*/
vector
<
double
>
pod_vector
;
random_device
rd
;
for
(
size_t
i
=
0
;
i
<
ckks_encoder
.
slot_count
();
i
++
)
{
pod_vector
.
push_back
(
1.001
*
static_cast
<
double
>
(
i
));
}
cout
<<
"Running tests "
;
for
(
long
long
i
=
0
;
i
<
count
;
i
++
)
{
/*
[Encoding]
For scale we use the square root of the last coeff_modulus prime
from parms.
*/
Plaintext
plain
(
parms
.
poly_modulus_degree
()
*
parms
.
coeff_modulus
().
size
(),
0
);
/*
*/
double
scale
=
sqrt
(
static_cast
<
double
>
(
parms
.
coeff_modulus
().
back
().
value
()));
time_start
=
chrono
::
high_resolution_clock
::
now
();
ckks_encoder
.
encode
(
pod_vector
,
scale
,
plain
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_encode_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Decoding]
*/
vector
<
double
>
pod_vector2
(
ckks_encoder
.
slot_count
());
time_start
=
chrono
::
high_resolution_clock
::
now
();
ckks_encoder
.
decode
(
plain
,
pod_vector2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_decode_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Encryption]
*/
Ciphertext
encrypted
(
context
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
encryptor
.
encrypt
(
plain
,
encrypted
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_encrypt_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Decryption]
*/
Plaintext
plain2
(
poly_modulus_degree
,
0
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
decryptor
.
decrypt
(
encrypted
,
plain2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_decrypt_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Add]
*/
Ciphertext
encrypted1
(
context
);
ckks_encoder
.
encode
(
i
+
1
,
plain
);
encryptor
.
encrypt
(
plain
,
encrypted1
);
Ciphertext
encrypted2
(
context
);
ckks_encoder
.
encode
(
i
+
1
,
plain2
);
encryptor
.
encrypt
(
plain2
,
encrypted2
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
add_inplace
(
encrypted1
,
encrypted1
);
evaluator
.
add_inplace
(
encrypted2
,
encrypted2
);
evaluator
.
add_inplace
(
encrypted1
,
encrypted2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_add_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Multiply]
*/
encrypted1
.
reserve
(
3
);
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
multiply_inplace
(
encrypted1
,
encrypted2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_multiply_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Multiply Plain]
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
multiply_plain_inplace
(
encrypted2
,
plain
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_multiply_plain_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Square]
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
square_inplace
(
encrypted2
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_square_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
if
(
context
->
using_keyswitching
())
{
/*
[Relinearize]
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
relinearize_inplace
(
encrypted1
,
relin_keys
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_relinearize_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Rescale]
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
rescale_to_next_inplace
(
encrypted1
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_rescale_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Rotate Vector]
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
rotate_vector_inplace
(
encrypted
,
1
,
gal_keys
);
evaluator
.
rotate_vector_inplace
(
encrypted
,
-
1
,
gal_keys
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_rotate_one_step_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Rotate Vector Random]
*/
int
random_rotation
=
static_cast
<
int
>
(
rd
()
%
ckks_encoder
.
slot_count
());
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
rotate_vector_inplace
(
encrypted
,
random_rotation
,
gal_keys
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_rotate_random_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
/*
[Complex Conjugate]
*/
time_start
=
chrono
::
high_resolution_clock
::
now
();
evaluator
.
complex_conjugate_inplace
(
encrypted
,
gal_keys
);
time_end
=
chrono
::
high_resolution_clock
::
now
();
time_conjugate_sum
+=
chrono
::
duration_cast
<
chrono
::
microseconds
>
(
time_end
-
time_start
);
}
/*
Print a dot to indicate progress.
*/
cout
<<
"."
;
cout
.
flush
();
}
cout
<<
" Done"
<<
endl
<<
endl
;
cout
.
flush
();
auto
avg_encode
=
time_encode_sum
.
count
()
/
count
;
auto
avg_decode
=
time_decode_sum
.
count
()
/
count
;
auto
avg_encrypt
=
time_encrypt_sum
.
count
()
/
count
;
auto
avg_decrypt
=
time_decrypt_sum
.
count
()
/
count
;
auto
avg_add
=
time_add_sum
.
count
()
/
(
3
*
count
);
auto
avg_multiply
=
time_multiply_sum
.
count
()
/
count
;
auto
avg_multiply_plain
=
time_multiply_plain_sum
.
count
()
/
count
;
auto
avg_square
=
time_square_sum
.
count
()
/
count
;
auto
avg_relinearize
=
time_relinearize_sum
.
count
()
/
count
;
auto
avg_rescale
=
time_rescale_sum
.
count
()
/
count
;
auto
avg_rotate_one_step
=
time_rotate_one_step_sum
.
count
()
/
(
2
*
count
);
auto
avg_rotate_random
=
time_rotate_random_sum
.
count
()
/
count
;
auto
avg_conjugate
=
time_conjugate_sum
.
count
()
/
count
;
cout
<<
"Average encode: "
<<
avg_encode
<<
" microseconds"
<<
endl
;
cout
<<
"Average decode: "
<<
avg_decode
<<
" microseconds"
<<
endl
;
cout
<<
"Average encrypt: "
<<
avg_encrypt
<<
" microseconds"
<<
endl
;
cout
<<
"Average decrypt: "
<<
avg_decrypt
<<
" microseconds"
<<
endl
;
cout
<<
"Average add: "
<<
avg_add
<<
" microseconds"
<<
endl
;
cout
<<
"Average multiply: "
<<
avg_multiply
<<
" microseconds"
<<
endl
;
cout
<<
"Average multiply plain: "
<<
avg_multiply_plain
<<
" microseconds"
<<
endl
;
cout
<<
"Average square: "
<<
avg_square
<<
" microseconds"
<<
endl
;
if
(
context
->
using_keyswitching
())
{
cout
<<
"Average relinearize: "
<<
avg_relinearize
<<
" microseconds"
<<
endl
;
cout
<<
"Average rescale: "
<<
avg_rescale
<<
" microseconds"
<<
endl
;
cout
<<
"Average rotate vector one step: "
<<
avg_rotate_one_step
<<
" microseconds"
<<
endl
;
cout
<<
"Average rotate vector random: "
<<
avg_rotate_random
<<
" microseconds"
<<
endl
;
cout
<<
"Average complex conjugate: "
<<
avg_conjugate
<<
" microseconds"
<<
endl
;
}
cout
.
flush
();
}
void
example_bfv_performance_default
()
{
print_example_banner
(
"BFV Performance Test with Degrees: 4096, 8192, and 16384"
);
EncryptionParameters
parms
(
scheme_type
::
BFV
);
size_t
poly_modulus_degree
=
4096
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
parms
.
set_plain_modulus
(
786433
);
bfv_performance_test
(
SEALContext
::
Create
(
parms
));
cout
<<
endl
;
poly_modulus_degree
=
8192
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
parms
.
set_plain_modulus
(
786433
);
bfv_performance_test
(
SEALContext
::
Create
(
parms
));
cout
<<
endl
;
poly_modulus_degree
=
16384
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
parms
.
set_plain_modulus
(
786433
);
bfv_performance_test
(
SEALContext
::
Create
(
parms
));
/*
Comment out the following to run the biggest example.
*/
// cout << endl;
// poly_modulus_degree = 32768;
// parms.set_poly_modulus_degree(poly_modulus_degree);
// parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));
// parms.set_plain_modulus(786433);
// bfv_performance_test(SEALContext::Create(parms));
}
void
example_bfv_performance_custom
()
{
size_t
poly_modulus_degree
=
0
;
cout
<<
endl
<<
"Set poly_modulus_degree (1024, 2048, 4096, 8192, 16384, or 32768): "
;
if
(
!
(
cin
>>
poly_modulus_degree
))
{
cout
<<
"Invalid option."
<<
endl
;
cin
.
clear
();
cin
.
ignore
(
numeric_limits
<
streamsize
>::
max
(),
'\n'
);
return
;
}
if
(
poly_modulus_degree
<
1024
||
poly_modulus_degree
>
32768
||
(
poly_modulus_degree
&
(
poly_modulus_degree
-
1
))
!=
0
)
{
cout
<<
"Invalid option."
<<
endl
;
return
;
}
string
banner
=
"BFV Performance Test with Degree: "
;
print_example_banner
(
banner
+
to_string
(
poly_modulus_degree
));
EncryptionParameters
parms
(
scheme_type
::
BFV
);
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
if
(
poly_modulus_degree
==
1024
)
{
parms
.
set_plain_modulus
(
12289
);
}
else
{
parms
.
set_plain_modulus
(
786433
);
}
bfv_performance_test
(
SEALContext
::
Create
(
parms
));
}
void
example_ckks_performance_default
()
{
print_example_banner
(
"CKKS Performance Test with Degrees: 4096, 8192, and 16384"
);
// It is not recommended to use BFVDefault primes in CKKS. However, for performance
// test, BFVDefault primes are good enough.
EncryptionParameters
parms
(
scheme_type
::
CKKS
);
size_t
poly_modulus_degree
=
4096
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
ckks_performance_test
(
SEALContext
::
Create
(
parms
));
cout
<<
endl
;
poly_modulus_degree
=
8192
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
ckks_performance_test
(
SEALContext
::
Create
(
parms
));
cout
<<
endl
;
poly_modulus_degree
=
16384
;
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
ckks_performance_test
(
SEALContext
::
Create
(
parms
));
/*
Comment out the following to run the biggest example.
*/
// cout << endl;
// poly_modulus_degree = 32768;
// parms.set_poly_modulus_degree(poly_modulus_degree);
// parms.set_coeff_modulus(CoeffModulus::BFVDefault(poly_modulus_degree));
// ckks_performance_test(SEALContext::Create(parms));
}
void
example_ckks_performance_custom
()
{
size_t
poly_modulus_degree
=
0
;
cout
<<
endl
<<
"Set poly_modulus_degree (1024, 2048, 4096, 8192, 16384, or 32768): "
;
if
(
!
(
cin
>>
poly_modulus_degree
))
{
cout
<<
"Invalid option."
<<
endl
;
cin
.
clear
();
cin
.
ignore
(
numeric_limits
<
streamsize
>::
max
(),
'\n'
);
return
;
}
if
(
poly_modulus_degree
<
1024
||
poly_modulus_degree
>
32768
||
(
poly_modulus_degree
&
(
poly_modulus_degree
-
1
))
!=
0
)
{
cout
<<
"Invalid option."
<<
endl
;
return
;
}
string
banner
=
"CKKS Performance Test with Degree: "
;
print_example_banner
(
banner
+
to_string
(
poly_modulus_degree
));
EncryptionParameters
parms
(
scheme_type
::
CKKS
);
parms
.
set_poly_modulus_degree
(
poly_modulus_degree
);
parms
.
set_coeff_modulus
(
CoeffModulus
::
BFVDefault
(
poly_modulus_degree
));
ckks_performance_test
(
SEALContext
::
Create
(
parms
));
}
/*
Prints a sub-menu to select the performance test.
*/
void
example_performance_test
()
{
print_example_banner
(
"Example: Performance Test"
);
while
(
true
)
{
cout
<<
endl
;
cout
<<
"Select a scheme (and optionally poly_modulus_degree):"
<<
endl
;
cout
<<
" 1. BFV with default degrees"
<<
endl
;
cout
<<
" 2. BFV with a custom degree"
<<
endl
;
cout
<<
" 3. CKKS with default degrees"
<<
endl
;
cout
<<
" 4. CKKS with a custom degree"
<<
endl
;
cout
<<
" 0. Back to main menu"
<<
endl
;
int
selection
=
0
;
cout
<<
endl
<<
"> Run performance test (1 ~ 4) or go back (0): "
;
if
(
!
(
cin
>>
selection
))
{
cout
<<
"Invalid option."
<<
endl
;
cin
.
clear
();
cin
.
ignore
(
numeric_limits
<
streamsize
>::
max
(),
'\n'
);
continue
;
}
switch
(
selection
)
{
case
1
:
example_bfv_performance_default
();
break
;
case
2
:
example_bfv_performance_custom
();
break
;
case
3
:
example_ckks_performance_default
();
break
;
case
4
:
example_ckks_performance_custom
();
break
;
case
0
:
cout
<<
endl
;
return
;
default:
cout
<<
"Invalid option."
<<
endl
;
}
}
}
bigpiseal3.5.1/native/examples/ANN/v1/ANN_decrypt_result_v1.cpp
0 → 100644
View file @
c6c9d2f5
#include <iomanip>
#include "seal_api.h"
#include "util.h"
using
namespace
seal
;
using
namespace
std
;
string
ciphertext_name
;
string
key_dir
;
// bool decrypt(size_t poly_d, size_t p_modulus, int &sample_size, string &ciphertext_dir);
bool
decrypt
(
int
&
sample_size
,
string
&
ciphertext_dir
);
int
main
(
int
argc
,
char
**
argv
)
{
if
(
argc
!=
4
)
{
// cout << "[ERROR] please enter /full/path/to/file/to/decrypt full/path/key " << endl;
// cout << "[ERROR] please enter prefix_file_to_decrypt full/path/key /full/path/to/storage" << endl;
// cout << "[ERROR] please enter a ciphertext file path, sample size and secret key path" << endl;
return
-
1
;
}
else
{
string
dir
=
argv
[
1
];
int
sample_size
=
atol
(
argv
[
2
]);
key_dir
=
argv
[
3
];
bool
result_str
=
decrypt
(
sample_size
,
dir
);
cout
<<
result_str
<<
endl
;
return
0
;
}
}
bool
decrypt
(
int
&
sample_size
,
string
&
ciphertext_dir
)
{
struct
decryptor_t
decr
;
init_operator_batching
(
decr
,
key_dir
);
bool
isContain
=
false
;
if
(
sample_size
<=
decr
.
bcode
->
slot_count
()
/
2
)
{
Ciphertext
cipher_matrix
;
vector
<
int64_t
>
pod_matrix
;
load_ciphertext
(
decr
,
cipher_matrix
,
ciphertext_dir
);
pod_matrix
=
decrypt_ciphermatrix
(
decr
,
cipher_matrix
);
// cout << pod_matrix.size() << endl;
int
no_dual_vectors
=
(
decr
.
bcode
->
slot_count
()
/
2
)
/
(
sample_size
);
vector
<
int64_t
>
v1
,
v2
;
for
(
size_t
i
=
0
;
i
<
no_dual_vectors
*
sample_size
;
i
++
)
{
v1
.
push_back
(
pod_matrix
[
i
]);
v2
.
push_back
(
pod_matrix
[(
pod_matrix
.
size
()
/
2
)
+
i
]);
}
int64_t
sum
=
0
;
if
(
isContain
==
false
)
{
// cout << "1 : ";
for
(
size_t
i
=
0
;
i
<
v1
.
size
();
i
++
)
{
// cout << v1[i];
if
((
i
+
1
)
%
sample_size
==
0
)
{
// cout << endl;
if
(
i
<
v1
.
size
()
-
1
)
{
// cout << ((i + 1) / 40) + 1 << " : ";
}
}
else
{
// cout << ", ";
}
if
(
v1
[
i
]
==
0
)
{
sum
=
sum
+
1
;
}
else
{
sum
=
0
;
}
if
(
sum
==
sample_size
)
{
isContain
=
true
;
}
}
}
// cout << endl;
if
(
isContain
==
false
)
{
sum
=
0
;
// cout << (v2.size() + 1) / 40 + 1 << " : ";
for
(
size_t
i
=
0
;
i
<
v2
.
size
();
i
++
)
{
// cout << v2[i];
if
((
i
+
1
)
%
sample_size
==
0
)
{
// cout << endl;
if
(
i
<
v2
.
size
()
-
1
)
{
// cout << ((v2.size() + i + 1) / 40) + 1 << " : ";
}
}
else
{
// cout << ", ";
}
if
(
v2
[
i
]
==
0
)
{
sum
=
sum
+
1
;
}
else
{
sum
=
0
;
}
if
(
sum
==
sample_size
)
{
isContain
=
true
;
}
}
}
}
else
{
// cout << endl << "Sample size is too large" << endl;
}
delete_operator_batching
(
decr
);
return
isContain
;
}
bigpiseal3.5.1/native/examples/ANN/v1/ANN_decrypt_v1.cpp
0 → 100644
View file @
c6c9d2f5
#include <iomanip>
#include "seal_api.h"
#include "util.h"
using
namespace
seal
;
using
namespace
std
;
string
ciphertext_name
;
string
key_dir
;
// vector<int64_t> decrypt(size_t poly_d, size_t p_modulus, int &sample_size, string &ciphertext_dir);
vector
<
double
>
decrypt
(
int
&
sample_size
,
string
&
ciphertext_dir
);
int
main
(
int
argc
,
char
**
argv
)
{
if
(
argc
!=
4
)
{
// cout << "[ERROR] please enter /full/path/to/file/to/decrypt full/path/key " << endl;
// cout << "[ERROR] please enter prefix_file_to_decrypt full/path/key /full/path/to/storage" << endl;
cout
<<
"[ERROR] please enter a ciphertext file path, sample size and secret key path"
<<
endl
;
return
-
1
;
}
else
{
string
dir
=
argv
[
1
];
int
sample_size
=
atol
(
argv
[
2
]);
key_dir
=
argv
[
3
];
vector
<
double
>
result
=
decrypt
(
sample_size
,
dir
);
for
(
int
i
=
0
;
i
<
result
.
size
();
++
i
)
{
std
::
cout
<<
result
[
i
]
<<
' '
;
}
std
::
cout
<<
endl
;
return
0
;
}
}
vector
<
double
>
decrypt
(
int
&
sample_size
,
string
&
ciphertext_dir
)
{
struct
cdecryptor_t
decr
;
// init_operator_batching(decr, key_dir);
init_operator_ckks
(
decr
,
key_dir
);
// init_operator_batching(2048, 4294967296, decr, key_dir);
// init_operator_batching(4096, 4294967296, decr, key_dir);
// init_operator_batching(8192, 4294967296, decr, key_dir);
// init_operator_batching(16384, 4294967296, decr, key_dir);
// init_operator_batching(32768, 4294967296, decr, key_dir);
vector
<
double
>
v
;
if
(
sample_size
<=
decr
.
ccode
->
slot_count
()
/
2
)
{
Ciphertext
cipher_matrix
;
vector
<
double
>
pod_matrix
;
load_ciphertext_ckks
(
decr
,
cipher_matrix
,
ciphertext_dir
);
// pod_matrix = decrypt_ciphermatrix(decr, cipher_matrix);
// pod_matrix = decrypt_ciphermatrix_ckks(decr, cipher_matrix);
decrypt_ciphertext_ckks
(
decr
,
cipher_matrix
,
pod_matrix
);
// void decrypt_ciphertext_ckks(struct cdecryptor_t& op_st, seal::Ciphertext& ct, std::vector<double>& pt_val);
for
(
size_t
i
=
0
;
i
<
sample_size
;
i
++
)
{
v
.
push_back
(
pod_matrix
[
i
]);
}
}
else
{
// cout << endl << "Sample size is too large" << endl;
}
// delete_operator_batching(decr);
delete_operator_ckks
(
decr
);
return
v
;
}
bigpiseal3.5.1/native/examples/ANN/v1/ANN_encrypt_v1.cpp
0 → 100644
View file @
c6c9d2f5
#include "seal_api.h"
using
namespace
seal
;
using
namespace
std
;
int
main
(
int
argc
,
char
**
argv
)
{
if
(
argc
!=
6
)
{
// cout << "[ERROR] please enter 1 plaintext values, prefix pathstorage(exists) " << endl;
cout
<<
"[ERROR] please enter plaintext vector value (eg. 75 67 8 23 076 2 23), output ciphertext file name or "
"prefix, ciphertext output file directory, sample size and public key path"
<<
endl
;
return
-
1
;
}
else
{
string
plaintext
=
argv
[
1
];
string
ciphertext_name
=
argv
[
2
];
string
ciphertext_dir
=
argv
[
3
];
int
sample_size
=
atol
(
argv
[
4
]);
string
key_dir
=
argv
[
5
];
struct
cencryptor_t
encr
;
init_operator_ckks
(
encr
,
key_dir
);
stringstream
ss
;
ss
<<
plaintext
;
vector
<
double
>
pod_matrix
;
double
x
=
0
;
while
(
ss
>>
x
)
{
pod_matrix
.
push_back
(
x
);
}
if
(
pod_matrix
.
size
()
<=
encr
.
ccode
->
slot_count
()
&&
pod_matrix
.
size
()
>=
sample_size
)
{
Ciphertext
encrypted_matrix
;
init_ciphertext_ckks
(
encr
,
pod_matrix
,
encrypted_matrix
);
save_ciphertext
(
encrypted_matrix
,
ciphertext_dir
+
"/"
+
ciphertext_name
+
".ct"
);
delete_operator_ckks
(
encr
);
return
0
;
}
else
{
delete_operator_ckks
(
encr
);
return
-
1
;
}
delete_operator_ckks
(
encr
);
return
0
;
}
}
bigpiseal3.5.1/native/examples/ANN/v1/ANN_evaluate_v1 copy.cpp
0 → 100644
View file @
c6c9d2f5
#include <iostream>
#include "seal_api.h"
#include "util.h"
// #include <algorithm>
// #include <iterator>
// #include <vector>
// #include <filesystem>
using
namespace
seal
;
using
namespace
std
;
void
sub_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
);
void
add_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
);
void
multiply_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
);
bool
is_number
(
const
string
&
s
);
void
printStrVector
(
const
vector
<
string
>
&
v
);
vector
<
vector
<
string
>>
split_ends
(
const
vector
<
string
>
&
data
,
const
vector
<
int
>
&
ends
);
void
multiply_ciphertexts
(
struct
evaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
);
void
relinearize_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
);
void
rescale_to_next_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
);
void
multiply_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
);
void
relinearize
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
Ciphertext
&
ct_out
);
void
sub_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
);
void
negate_inplace__ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
);
void
add_plain_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
struct
Ciphertext
&
ct
,
const
Plaintext
&
plain
);
void
multiply_plain_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
);
void
add_many_ciphertext
(
struct
evaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
);
void
exponentiate_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
uint64_t
&
exponent
);
void
sub_plain_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
);
void
mod_switch_to_next_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
);
int
simpleCheck
(
string
&
source
,
vector
<
string
>
&
data
,
string
&
result_name
,
string
&
result_dir
,
int
&
sample_size
,
string
&
key_dir
);
int
checkSq
(
string
&
source
,
vector
<
string
>
&
data
,
string
&
result_name
,
string
&
result_dir
,
int
&
sample_size
,
string
&
key_dir
);
Ciphertext
check
(
string
&
source
,
vector
<
string
>
&
data
,
string
&
result_name
,
string
&
result_dir
,
int
&
sample_size
,
string
&
key_dir
,
struct
encryptor_t
&
encr
,
struct
evaluator_t
&
eval
);
// string relink_key_path;
// string galois_key_path;
// string public_key_path;
string
key_dir
=
""
;
int
main
(
int
argc
,
char
**
argv
)
{
// input processing - begin
// string result_name = argv[argc - 6];
// string result_dir = argv[argc - 5];
// int sample_size = atoi(argv[argc - 4]);
// relink_key_path = argv[argc - 3];
// galois_key_path = argv[argc - 2];
// public_key_path = argv[argc - 1];
string
source
=
argv
[
1
];
string
result_name
=
argv
[
argc
-
4
];
string
result_dir
=
argv
[
argc
-
3
];
int
sample_size
=
atoi
(
argv
[
argc
-
2
]);
key_dir
=
argv
[
argc
-
1
];
vector
<
string
>
data
;
for
(
int
i
=
2
;
i
<
argc
-
4
;
i
++
)
{
data
.
push_back
(
argv
[
i
]);
}
// if (source == "" || data.size() == 0 || result_name == "" || result_dir == "" || sample_size == 0 ||
// relink_key_path == "" || galois_key_path == "" || public_key_path == "")
if
(
source
==
""
||
data
.
size
()
==
0
||
result_name
==
""
||
result_dir
==
""
||
sample_size
==
0
||
key_dir
==
""
)
{
// error handling
cout
<<
"[ERROR] please enter a source path, data paths, output ciphertext file name or prefix, output "
"ciphertext directory, sample size, linking key path, galois key path and public key path"
<<
endl
;
return
-
1
;
}
// input processing - end
// simple algo
// int result = simpleCheck(source, data, result_name, result_dir, sample_size, key_dir);
// sequence algo
int
result
=
checkSq
(
source
,
data
,
result_name
,
result_dir
,
sample_size
,
key_dir
);
// error handling
if
(
result
==
-
1
)
{
fprintf
(
stderr
,
"error!
\n
"
);
}
else
{
cout
<<
"done"
;
}
// checkSq(source, data, result_name, result_dir, sample_size, relink_key_path, galois_key_path, public_key_path);
return
result
;
}
int
simpleCheck
(
string
&
source
,
vector
<
string
>
&
data
,
string
&
result_name
,
string
&
result_dir
,
int
&
sample_size
,
string
&
key_dir
)
{
struct
evaluator_t
eval
;
init_operator_batching
(
eval
,
key_dir
);
struct
encryptor_t
encr
;
init_operator_batching
(
encr
,
key_dir
);
// cout << sample_size*data.size() << endl;
// cout << encr.bcode->slot_count() << endl;
if
(
sample_size
*
data
.
size
()
>
encr
.
bcode
->
slot_count
()
||
sample_size
>
encr
.
bcode
->
slot_count
()
/
2
)
{
// error handling
delete_operator_batching
(
encr
);
delete_operator_batching
(
eval
);
return
-
1
;
}
else
{
Ciphertext
encrypted_result_matrix
;
vector
<
int64_t
>
result_matrix
;
init_ciphermatrix
(
encr
,
result_matrix
,
encrypted_result_matrix
);
vector
<
int64_t
>
dummy_matrix
;
for
(
size_t
i
=
0
;
i
<
sample_size
;
i
++
)
{
dummy_matrix
.
push_back
(
1
);
}
Ciphertext
encrypted_dummy_matrix
;
init_ciphermatrix
(
encr
,
dummy_matrix
,
encrypted_dummy_matrix
);
// normalize input data if its size is odd
int
normalized_data_size
;
if
(
data
.
size
()
%
2
==
0
)
{
normalized_data_size
=
data
.
size
();
}
else
{
normalized_data_size
=
data
.
size
()
+
1
;
}
int
required_range
=
normalized_data_size
*
sample_size
;
int
required_no_row_elements
=
required_range
/
2
;
int
required_range_row
=
required_range
/
2
;
// create padding matrix
int
padding_slots
=
(
encr
.
bcode
->
slot_count
()
/
2
)
-
required_range_row
;
vector
<
int64_t
>
padding_matrix
(
encr
.
bcode
->
slot_count
(),
0ULL
);
for
(
size_t
i
=
0
;
i
<
padding_slots
;
i
++
)
{
padding_matrix
[
required_no_row_elements
+
i
]
=
1
;
padding_matrix
[
encr
.
bcode
->
slot_count
()
-
i
]
=
1
;
}
Ciphertext
encrypted_padding_matrix
;
init_ciphermatrix
(
encr
,
padding_matrix
,
encrypted_padding_matrix
);
if
(
required_range_row
<=
encr
.
bcode
->
slot_count
()
&&
required_range_row
>
0
)
{
for
(
int
index
=
0
;
index
<
normalized_data_size
/
2
;
index
++
)
{
Ciphertext
ct1
,
ct2
,
ct3
;
Ciphertext
temp1
,
temp2
;
// cout << "[INFO] loading ciphertext 1" << endl;
load_ciphertext
(
eval
,
ct1
,
source
);
// cout << "[INFO] loading ciphertext 2" << endl;
load_ciphertext
(
eval
,
ct2
,
data
.
at
(
index
));
sub_ciphertext
(
eval
,
ct1
,
ct2
,
temp1
);
if
((
normalized_data_size
/
2
)
+
index
<
data
.
size
())
{
// cout << "[INFO] loading ciphertext 3" << endl;
load_ciphertext
(
eval
,
ct3
,
data
.
at
((
normalized_data_size
/
2
)
+
index
));
sub_ciphertext
(
eval
,
ct1
,
ct3
,
temp2
);
}
else
{
// add dummy vector for oddy data
temp2
=
encrypted_dummy_matrix
;
}
eval
.
eval
->
rotate_columns_inplace
(
temp2
,
eval
.
gk
);
add_ciphertext
(
eval
,
temp1
,
temp2
,
temp1
);
add_ciphertext
(
eval
,
temp1
,
encrypted_result_matrix
,
encrypted_result_matrix
);
// avoid the last shift
if
(
index
+
1
!=
(
normalized_data_size
/
2
))
{
eval
.
eval
->
rotate_rows_inplace
(
encrypted_result_matrix
,
-
sample_size
,
eval
.
gk
);
}
}
// add renmaining padding slots
add_ciphertext
(
eval
,
encrypted_result_matrix
,
encrypted_padding_matrix
,
encrypted_result_matrix
);
}
save_ciphertext
(
encrypted_result_matrix
,
result_dir
+
"/"
+
result_name
+
".ct"
);
delete_operator_batching
(
encr
);
delete_operator_batching
(
eval
);
return
0
;
}
}
// int checkSq(
// string &source, vector<string> &data, string &result_name, string &result_dir, int &sample_size,
// string &relink_key_path, string &galois_key_path, string &public_key_path)
int
checkSq
(
string
&
source
,
vector
<
string
>
&
data
,
string
&
result_name
,
string
&
result_dir
,
int
&
sample_size
,
string
&
key_dir
)
{
struct
evaluator_t
eval
;
// init_operator_batching(2048, 4294967296, eval, relink_key_path, galois_key_path);
// init_operator_batching(4096, 4294967296, eval, relink_key_path, galois_key_path);
// init_operator_batching(8192, 4294967296, eval, relink_key_path, galois_key_path);
// init_operator_batching(16384, 4294967296, eval, relink_key_path, galois_key_path);
// init_operator_batching(32768, 4294967296, eval, relink_key_path, galois_key_path);
init_operator_batching
(
eval
,
key_dir
);
struct
encryptor_t
encr
;
// init_operator_batching(4096, 4294967296, encr, public_key_path);
// init_operator_batching(8192, 4294967296, encr, public_key_path);
// init_operator_batching(16384, 4294967296, encr, public_key_path);
// init_operator_batching(32768, 4294967296, encr, public_key_path);
init_operator_batching
(
encr
,
key_dir
);
// if (sample_size*data.size() > encr.bcode->slot_count() || sample_size > encr.bcode->slot_count()/2)
if
(
sample_size
>
encr
.
bcode
->
slot_count
()
/
2
)
{
// error handling
delete_operator_batching
(
encr
);
delete_operator_batching
(
eval
);
return
-
1
;
}
else
{
// struct evaluator_t eval;
// // init_operator_batching(2048, 4294967296, eval, relink_key_path, galois_key_path);
// // init_operator_batching(4096, 4294967296, eval, relink_key_path, galois_key_path);
// init_operator_batching(8192, 4294967296, eval, relink_key_path, galois_key_path);
// // init_operator_batching(16384, 4294967296, eval, relink_key_path, galois_key_path);
// // init_operator_batching(32768, 4294967296, eval, relink_key_path, galois_key_path);
// struct encryptor_t encr;
// // init_operator_batching(4096, 4294967296, encr, public_key_path);
// init_operator_batching(8192, 4294967296, encr, public_key_path);
// // init_operator_batching(16384, 4294967296, encr, public_key_path);
// // init_operator_batching(32768, 4294967296, encr, public_key_path);
int
capacity
=
(
encr
.
bcode
->
slot_count
())
/
sample_size
;
// cout << capacity << endl;
// vector<string> v_temp;
// vector<string> v_v_temp;
// Ciphertext result;
vector
<
Ciphertext
>
v_result
;
int
nSq
=
data
.
size
()
/
capacity
;
for
(
size_t
i
=
0
;
i
<
nSq
;
i
++
)
{
Ciphertext
result
;
// cout << "sq : " << i << endl;
vector
<
string
>
v_temp
;
for
(
size_t
j
=
0
;
j
<
capacity
;
j
++
)
{
string
str
=
data
.
back
();
v_temp
.
push_back
(
str
);
data
.
pop_back
();
}
if
(
i
==
0
)
{
// cout << "sq : init" << endl;
// result = check(
// source, v_temp, result_name, result_dir, sample_size, relink_key_path, galois_key_path,
// public_key_path, encr, eval);
result
=
check
(
source
,
v_temp
,
result_name
,
result_dir
,
sample_size
,
key_dir
,
encr
,
eval
);
v_result
.
push_back
(
result
);
}
else
{
// cout << "sq : other" << endl;
// Ciphertext result2 = check(
// source, v_temp, result_name, result_dir, sample_size, relink_key_path, galois_key_path,
// public_key_path, encr, eval);
// multiply_ciphertext(eval, result2, result, result);
// relinearize_inplace(eval, result);
// result = check(
// source, v_temp, result_name, result_dir, sample_size, relink_key_path, galois_key_path,
// public_key_path, encr, eval);
result
=
check
(
source
,
v_temp
,
result_name
,
result_dir
,
sample_size
,
key_dir
,
encr
,
eval
);
v_result
.
push_back
(
result
);
}
}
if
(
data
.
size
()
%
capacity
!=
0
)
{
// cout << "oddy!!! " << endl;
// Ciphertext result = check(
// source, data, result_name, result_dir, sample_size, relink_key_path, galois_key_path,
// public_key_path, encr, eval);
Ciphertext
result
=
check
(
source
,
data
,
result_name
,
result_dir
,
sample_size
,
key_dir
,
encr
,
eval
);
// multiply_ciphertext(eval, result2, result, result);
// relinearize_inplace(eval, result);
v_result
.
push_back
(
result
);
}
Ciphertext
result
;
multiply_ciphertexts
(
eval
,
v_result
,
result
);
relinearize_inplace
(
eval
,
result
);
save_ciphertext
(
result
,
result_dir
+
"/"
+
result_name
+
".ct"
);
delete_operator_batching
(
encr
);
delete_operator_batching
(
eval
);
return
0
;
}
}
// Ciphertext check(
// string &source, vector<string> &data, string &result_name, string &result_dir, int &sample_size,
// string &relink_key_path, string &galois_key_path, string &public_key_path, struct encryptor_t &encr,
// struct evaluator_t &eval)
Ciphertext
check
(
string
&
source
,
vector
<
string
>
&
data
,
string
&
result_name
,
string
&
result_dir
,
int
&
sample_size
,
string
&
key_dir
,
struct
encryptor_t
&
encr
,
struct
evaluator_t
&
eval
)
{
// if (source == "" || data.size() == 0 || result_name == "" || result_dir == "" || sample_size == 0)
// {
// // error handling
// // return -1;
// }
// else
// {
Ciphertext
encrypted_result_matrix
;
vector
<
int64_t
>
result_matrix
;
init_ciphermatrix
(
encr
,
result_matrix
,
encrypted_result_matrix
);
vector
<
int64_t
>
dummy_matrix
;
for
(
size_t
i
=
0
;
i
<
sample_size
;
i
++
)
{
dummy_matrix
.
push_back
(
1
);
}
Ciphertext
encrypted_dummy_matrix
;
init_ciphermatrix
(
encr
,
dummy_matrix
,
encrypted_dummy_matrix
);
// normalize input data if its size is odd
int
normalized_data_size
;
if
(
data
.
size
()
%
2
==
0
)
{
normalized_data_size
=
data
.
size
();
}
else
{
normalized_data_size
=
data
.
size
()
+
1
;
}
int
required_range
=
normalized_data_size
*
sample_size
;
int
required_no_row_elements
=
required_range
/
2
;
int
required_range_row
=
required_range
/
2
;
// create padding matrix
int
padding_slots
=
(
encr
.
bcode
->
slot_count
()
/
2
)
-
required_range_row
;
vector
<
int64_t
>
padding_matrix
(
encr
.
bcode
->
slot_count
(),
0ULL
);
for
(
size_t
i
=
0
;
i
<
padding_slots
;
i
++
)
{
padding_matrix
[
required_no_row_elements
+
i
]
=
1
;
padding_matrix
[(
encr
.
bcode
->
slot_count
()
-
1
)
-
i
]
=
1
;
}
Ciphertext
encrypted_padding_matrix
;
init_ciphermatrix
(
encr
,
padding_matrix
,
encrypted_padding_matrix
);
if
(
required_range_row
<=
encr
.
bcode
->
slot_count
()
&&
required_range_row
>
0
)
{
for
(
int
index
=
0
;
index
<
normalized_data_size
/
2
;
index
++
)
{
Ciphertext
ct1
,
ct2
,
ct3
;
Ciphertext
temp1
,
temp2
;
// cout << "[INFO] loading ciphertext 1" << endl;
load_ciphertext
(
eval
,
ct1
,
source
);
// cout << "[INFO] loading ciphertext 2" << endl;
load_ciphertext
(
eval
,
ct2
,
data
.
at
(
index
));
sub_ciphertext
(
eval
,
ct1
,
ct2
,
temp1
);
if
((
normalized_data_size
/
2
)
+
index
<
data
.
size
())
{
// cout << "[INFO] loading ciphertext 3" << endl;
load_ciphertext
(
eval
,
ct3
,
data
.
at
((
normalized_data_size
/
2
)
+
index
));
sub_ciphertext
(
eval
,
ct1
,
ct3
,
temp2
);
}
else
{
// add dummy vector for oddy data
temp2
=
encrypted_dummy_matrix
;
}
eval
.
eval
->
rotate_columns_inplace
(
temp2
,
eval
.
gk
);
add_ciphertext
(
eval
,
temp1
,
temp2
,
temp1
);
add_ciphertext
(
eval
,
temp1
,
encrypted_result_matrix
,
encrypted_result_matrix
);
// avoid the last shift
if
(
index
+
1
!=
(
normalized_data_size
/
2
))
{
eval
.
eval
->
rotate_rows_inplace
(
encrypted_result_matrix
,
-
sample_size
,
eval
.
gk
);
}
}
// add renmaining padding slots
add_ciphertext
(
eval
,
encrypted_result_matrix
,
encrypted_padding_matrix
,
encrypted_result_matrix
);
}
// // save_ciphertext(encrypted_result_matrix, result_dir + "/" + result_name + ".ct");
// delete_operator_batching(eval);
return
encrypted_result_matrix
;
// }
}
void
sub_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
sub
(
ct1
,
ct2
,
ct_out
);
}
void
sub_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
)
{
op_st
.
eval
->
sub_inplace
(
ct1
,
ct2
);
}
void
sub_plain_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
)
{
op_st
.
eval
->
sub_plain_inplace
(
ct
,
plain
);
}
void
negate_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
negate_inplace
(
ct
);
}
void
add_plain_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
struct
Ciphertext
&
ct
,
const
Plaintext
&
plain
)
{
op_st
.
eval
->
add_plain_inplace
(
ct
,
plain
);
}
void
add_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
add
(
ct1
,
ct2
,
ct_out
);
}
void
add_many_ciphertext
(
struct
evaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
add_many
(
cts
,
ct_out
);
}
void
multiply_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
multiply
(
ct1
,
ct2
,
ct_out
);
}
void
multiply_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
)
{
op_st
.
eval
->
multiply_inplace
(
ct1
,
ct2
);
}
void
multiply_ciphertexts
(
struct
evaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
multiply_many
(
cts
,
op_st
.
lk
,
ct_out
);
}
void
multiply_plain_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
)
{
op_st
.
eval
->
multiply_plain_inplace
(
ct
,
plain
);
}
void
relinearize_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
relinearize_inplace
(
ct
,
op_st
.
lk
);
}
void
relinearize
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
relinearize
(
ct
,
op_st
.
lk
,
ct_out
);
}
void
rescale_to_next_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
rescale_to_next_inplace
(
ct
);
}
void
exponentiate_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
uint64_t
&
exponent
)
{
op_st
.
eval
->
exponentiate_inplace
(
ct
,
exponent
,
op_st
.
lk
);
}
void
mod_switch_to_next_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
mod_switch_to_next_inplace
(
ct
);
}
bigpiseal3.5.1/native/examples/ANN/v1/ANN_evaluate_v1.cpp
0 → 100644
View file @
c6c9d2f5
#include <iostream>
#include "csv_api.h"
#include "print_api.h"
#include "seal_api.h"
#include "util.h"
using
namespace
seal
;
using
namespace
std
;
void
sub_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
);
void
add_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
);
void
add_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
);
void
multiply_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
);
void
multiply_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
);
bool
is_number
(
const
string
&
s
);
void
printStrVector
(
const
vector
<
string
>
&
v
);
vector
<
vector
<
string
>>
split_ends
(
const
vector
<
string
>
&
data
,
const
vector
<
int
>
&
ends
);
void
multiply_ciphertexts
(
struct
evaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
);
void
multiply_ciphertexts_ckks
(
struct
cevaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
);
void
relinearize_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
);
void
relinearize_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
);
void
rescale_to_next_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
);
void
rescale_to_next_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
);
void
multiply_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
);
void
relinearize
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
Ciphertext
&
ct_out
);
void
sub_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
);
void
negate_inplace__ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
);
void
add_plain_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
struct
Ciphertext
&
ct
,
const
Plaintext
&
plain
);
void
add_plain_inplace_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
struct
Ciphertext
&
ct
,
const
Plaintext
&
plain
);
void
add_plain_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
,
Plaintext
&
plain
,
Ciphertext
&
ct_out
);
void
multiply_plain_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
);
void
multiply_plain_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
,
Ciphertext
&
out
);
void
add_many_ciphertext
(
struct
evaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
);
void
add_many_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
);
void
exponentiate_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
uint64_t
&
exponent
);
void
sub_plain_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
);
void
mod_switch_to_next_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
);
void
mod_switch_to_next_inplace_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
Plaintext
&
plain
);
void
mod_switch_to_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Plaintext
&
plain
,
parms_id_type
&
parms_id
);
void
mod_switch_to_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
,
parms_id_type
&
parms_id
);
int
ANNProcess
(
vector
<
string
>
&
wFilePaths
,
string
&
xCT
,
vector
<
string
>
&
bFilePaths
,
string
&
result_name
,
string
&
result_dir
,
int
&
sample_size
,
string
&
key_dir
);
int
main
(
int
argc
,
char
**
argv
)
{
string
wFilePath
=
argv
[
1
];
string
wFilePath2
=
argv
[
2
];
string
wFilePath3
=
argv
[
3
];
string
wFilePath4
=
argv
[
4
];
string
xCT
=
argv
[
5
];
string
bFilePath
=
argv
[
6
];
string
bFilePath2
=
argv
[
7
];
string
bFilePath3
=
argv
[
8
];
string
bFilePath4
=
argv
[
9
];
string
result_name
=
argv
[
argc
-
4
];
string
result_dir
=
argv
[
argc
-
3
];
int
sample_size
=
atoi
(
argv
[
argc
-
2
]);
string
key_dir
=
argv
[
argc
-
1
];
// Midify and improve later
vector
<
string
>
wFilePaths
;
wFilePaths
.
push_back
(
wFilePath
);
wFilePaths
.
push_back
(
wFilePath2
);
wFilePaths
.
push_back
(
wFilePath3
);
wFilePaths
.
push_back
(
wFilePath4
);
// Midify and improve later
vector
<
string
>
bFilePaths
;
bFilePaths
.
push_back
(
bFilePath
);
bFilePaths
.
push_back
(
bFilePath2
);
bFilePaths
.
push_back
(
bFilePath3
);
bFilePaths
.
push_back
(
bFilePath4
);
if
(
xCT
==
""
||
result_name
==
""
||
result_dir
==
""
||
sample_size
==
0
||
key_dir
==
""
)
{
// error handling
cout
<<
"[ERROR] please enter a source path, data paths, output ciphertext file name or prefix, output "
"ciphertext directory, sample size, linking key path, galois key path and public key path"
<<
endl
;
return
-
1
;
}
int
result
=
0
;
result
=
ANNProcess
(
wFilePaths
,
xCT
,
bFilePaths
,
result_name
,
result_dir
,
sample_size
,
key_dir
);
// error handling
if
(
result
==
-
1
)
{
fprintf
(
stderr
,
"error!
\n
"
);
}
else
{
std
::
cout
<<
"done"
;
}
return
result
;
}
void
rotate_2d_matrix_clockwise_impl
(
vector
<
vector
<
double
>>
const
&
matrix
,
vector
<
vector
<
double
>>
&
rotated_matrix
,
int
const
M
,
int
const
N
)
{
for
(
int
x
=
0
;
x
<
N
;
++
x
)
{
for
(
int
y
=
0
;
y
<
M
;
++
y
)
{
// cout << "[ANN Engine] ..... " << matrix[x][y] << endl;
// Source : https://stackoverflow.com/questions/4780119/2d-euclidean-vector-rotations
rotated_matrix
[
y
][
-
x
-
1
+
N
]
=
matrix
[
x
][
y
];
}
}
}
auto
rotate_2d_matrix_clockwise
(
vector
<
vector
<
double
>>
const
&
original_matrix
)
->
vector
<
vector
<
double
>>
{
int
const
M
=
original_matrix
[
0
].
size
();
int
const
N
=
original_matrix
.
size
();
vector
<
vector
<
double
>>
rotated_matrix
;
rotated_matrix
.
resize
(
M
);
for
(
auto
x
=
0
;
x
<
M
;
++
x
)
{
rotated_matrix
[
x
].
resize
(
N
);
}
rotate_2d_matrix_clockwise_impl
(
original_matrix
,
rotated_matrix
,
M
,
N
);
return
rotated_matrix
;
}
vector
<
vector
<
double
>>
load_matrix_and_rotate_clockwise
(
string
&
original_matrix
,
int
n
)
{
// cout << "[ANN Engine] loading matrix w " << original_matrix << "..... " << endl;
vector
<
vector
<
double
>>
w
;
load_csv_file
(
original_matrix
,
w
,
0
);
// cout << "[ANN Engine] loading matrix w ..... end \n" << endl;
// cout << "[ANN Engine] rotating matrix w ..... " << endl;
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
w
=
rotate_2d_matrix_clockwise
(
w
);
}
// cout << "[ANN Engine] rotating matrix w ..... end \n" << endl;
// cout << "[ANN Engine] printing rotated matrix w ..... " << endl;
// print_matrix(w);
// cout << "[ANN Engine] printing rotated matrix w ..... end \n" << endl;
return
w
;
}
Ciphertext
getIndexVector
(
cencryptor_t
&
encr
,
cevaluator_t
&
eval
,
Ciphertext
vector_ct
,
int
slot_count
,
int
index
)
{
// std::cout << "[ANN Engine] Error 1 ..... \n" << endl;
vector
<
double
>
index_vector
(
encr
.
ccode
->
slot_count
(),
0ULL
);
index_vector
[
index
-
1
]
=
1ULL
;
Plaintext
index_vector_pt
;
init_plaintext_ckks
(
encr
,
index_vector
,
index_vector_pt
);
// std::cout << "[ANN Engine] Error 2 ..... \n" << endl;
Ciphertext
v_ct
=
vector_ct
;
// std::cout << "[ANN Engine] Error 3 ..... \n" << endl;
parms_id_type
last_parms_id2
=
v_ct
.
parms_id
();
mod_switch_to_inplace_ckks
(
eval
,
index_vector_pt
,
last_parms_id2
);
// std::cout << "[ANN Engine] Computing v_ct (add_ciphertext_ckks)..... " << endl;
// cout << " + Scale of v_ct: " << log2(v_ct.scale()) << " bits" << endl;
// cout << " + Modulus chain index for v_ct: " << eval.context->get_context_data(v_ct.parms_id())->chain_index()
// << endl;
// cout << " + Scale of index_vector_pt: " << log2(index_vector_pt.scale()) << " bits" << endl;
// cout << " + Modulus chain index for index_vector_pt: "
// << eval.context->get_context_data(index_vector_pt.parms_id())->chain_index() << endl;
multiply_plain_inplace_ckks
(
eval
,
v_ct
,
index_vector_pt
);
relinearize_inplace_ckks
(
eval
,
v_ct
);
rescale_to_next_inplace_ckks
(
eval
,
v_ct
);
eval
.
eval
->
rotate_vector
(
v_ct
,
-
(
slot_count
-
index
),
eval
.
gk
,
v_ct
);
Ciphertext
result
=
v_ct
;
// std::cout << "[ANN Engine] Error 4 ..... \n" << endl;
for
(
size_t
i
=
0
;
i
<
slot_count
;
i
++
)
{
// std::cout << "[ANN Engine] Error 5 ..... \n" << endl;
eval
.
eval
->
rotate_vector_inplace
(
result
,
1
,
eval
.
gk
);
// eval.eval->rotate_vector(result, 1, eval.gk, result);
// std::cout << "[ANN Engine] Error 6 ..... \n" << endl;
// std::cout << "[ANN Engine] Computing v_ct (add_ciphertext_ckks)..... " << endl;
// cout << " + Scale of v_ct: " << log2(v_ct.scale()) << " bits" << endl;
// cout << " + Modulus chain index for v_ct: " <<
// eval.context->get_context_data(v_ct.parms_id())->chain_index()
// << endl;
// cout << " + Scale of result: " << log2(result.scale()) << " bits" << endl;
// cout << " + Modulus chain index for result: "
// << eval.context->get_context_data(result.parms_id())->chain_index() << endl;
add_ciphertext_ckks
(
eval
,
v_ct
,
result
,
result
);
// std::cout << "[ANN Engine] Error 7 ..... \n" << endl;
}
return
result
;
}
Ciphertext
action_compute
(
cencryptor_t
&
encr
,
cevaluator_t
&
eval
,
string
&
wFilePath
,
Ciphertext
&
x
,
string
&
bFilePath
,
int
sample_size
)
{
vector
<
vector
<
double
>>
cw
;
cw
=
load_matrix_and_rotate_clockwise
(
wFilePath
,
3
);
// Transform into vector<Plaintext>
// std::cout << "[ANN Engine] Transform into vector<Plaintext> cw_pt ..... \n" << endl;
vector
<
Plaintext
>
cw_pt
;
for
(
int
i
=
0
;
i
<
cw
.
size
();
i
++
)
{
Plaintext
temp_pt
;
init_plaintext_ckks
(
encr
,
cw
[
i
],
temp_pt
);
cw_pt
.
push_back
(
temp_pt
);
}
// std::cout << "[ANN Engine] Transform into vector<Plaintext> cw_pt ..... end \n" << endl;
// cout << "[ANN Engine] loading vector b " << bFilePath << "..... " << endl;
vector
<
vector
<
double
>>
b
;
load_csv_file
(
bFilePath
,
b
,
0
);
Plaintext
b_pt
;
init_plaintext_ckks
(
encr
,
b
[
0
],
b_pt
);
// cout << "[ANN Engine] loading vector b ..... end \n" << endl;
std
::
cout
<<
"[ANN Engine] Computing wxb_ct ..... "
<<
endl
;
vector
<
Ciphertext
>
wxb_ct
;
Ciphertext
index_vector
;
// std::cout << "[ANN Engine] Computing wxb_ct (multiply_plain_inplace_ckks)..... " << endl;
for
(
int
i
=
0
;
i
<
cw_pt
.
size
();
i
++
)
{
Ciphertext
x_ct
=
x
;
Plaintext
pt
;
// std::cout << "[ANN Engine] Computing wxb_ct (add_ciphertext_ckks)..... " << endl;
// cout << " + Scale of x_ct: " << log2(x_ct.scale()) << " bits" << endl;
// cout << " + Modulus chain index for x_ct: " <<
// eval.context->get_context_data(x_ct.parms_id())->chain_index()
// << endl;
// cout << " + Scale of cw_pt[i]: " << log2(cw_pt[i].scale()) << " bits" << endl;
// cout << " + Modulus chain index for cw_pt[i]: "
// << eval.context->get_context_data(cw_pt[i].parms_id())->chain_index() << endl;
// std::cout << "[ANN Engine] Error 1..... " << endl;
parms_id_type
last_parms_id2
=
x_ct
.
parms_id
();
mod_switch_to_inplace_ckks
(
eval
,
cw_pt
[
i
],
last_parms_id2
);
// index_vector = getIndexVector(encr, eval, x_ct, 3, 1);
index_vector
=
getIndexVector
(
encr
,
eval
,
x_ct
,
sample_size
,
sample_size
-
i
);
// std::cout << "[ANN Engine] Error 1.1..... " << endl;
parms_id_type
last_parms_id3
=
x_ct
.
parms_id
();
mod_switch_to_inplace_ckks
(
eval
,
cw_pt
[
i
],
last_parms_id3
);
// Ciphertext b = getIndexVector(encr, eval, a, 3, 1);
// std::cout << "[ANN Engine] Error 2..... " << endl;
// std::cout << "[ANN Engine] Computing wxb_ct (add_ciphertext_ckks)..... " << endl;
// cout << " + Scale of x_ct: " << log2(x_ct.scale()) << " bits" << endl;
// cout << " + Modulus chain index for x_ct: " <<
// eval.context->get_context_data(x_ct.parms_id())->chain_index()
// << endl;
// cout << " + Scale of cw_pt[i]: " << log2(cw_pt[i].scale()) << " bits" << endl;
// cout << " + Modulus chain index for cw_pt[i]: "
// << eval.context->get_context_data(cw_pt[i].parms_id())->chain_index() << endl;
// x_ct = getIndexVector(encr, eval, x_ct, 3, 1);
// multiply_plain_inplace_ckks(eval, x_ct, cw_pt[i]);
// std::cout << "[ANN Engine] Error 3..... " << endl;
// std::cout << "[ANN Engine] Error 2..... " << endl;
// std::cout << "[ANN Engine] Computing wxb_ct (add_ciphertext_ckks)..... " << endl;
// cout << " + Scale of index_vector: " << log2(index_vector.scale()) << " bits" << endl;
// cout << " + Modulus chain index for index_vector: "
// << eval.context->get_context_data(index_vector.parms_id())->chain_index() << endl;
// cout << " + Scale of cw_pt[i]: " << log2(cw_pt[i].scale()) << " bits" << endl;
// cout << " + Modulus chain index for cw_pt[i]: "
// << eval.context->get_context_data(cw_pt[i].parms_id())->chain_index() << endl;
parms_id_type
last_parms_id4
=
index_vector
.
parms_id
();
mod_switch_to_inplace_ckks
(
eval
,
cw_pt
[
i
],
last_parms_id4
);
// cw_pt[i].scale() = pow(2.0, 40);
// index_vector.scale() = pow(2.0, 40);
multiply_plain_ckks
(
eval
,
index_vector
,
cw_pt
[
i
],
x_ct
);
// std::cout << "[ANN Engine] Computing wxb_ct (add_ciphertext_ckks)..... " << endl;
// cout << " + Scale of x_ct: " << log2(x_ct.scale()) << " bits" << endl;
// cout << " + Modulus chain index for x_ct: " <<
// eval.context->get_context_data(x_ct.parms_id())->chain_index()
// << endl;
// cout << " + Scale of b_pt: " << log2(b_pt.scale()) << " bits" << endl;
// cout << " + Modulus chain index for b_pt: " <<
// eval.context->get_context_data(b_pt.parms_id())->chain_index()
// << endl;
relinearize_inplace_ckks
(
eval
,
x_ct
);
rescale_to_next_inplace_ckks
(
eval
,
x_ct
);
// rescale b_pt
parms_id_type
last_parms_id
=
x_ct
.
parms_id
();
mod_switch_to_inplace_ckks
(
eval
,
b_pt
,
last_parms_id
);
// more solution
// cout << " + Exact scale 1 in x_ct: " << x_ct.scale() << endl;
// cout << " + Exact scale 1 in b_pt: " << b_pt.scale() << endl;
// cout << " + Exact scale 1 in b_ct: " << b_ct.scale() << endl;
x_ct
.
scale
()
=
pow
(
2.0
,
40
);
b_pt
.
scale
()
=
pow
(
2.0
,
40
);
// cout << " + Exact scale 2 in x_ct: " << x_ct.scale() << endl;
// cout << " + Exact scale 2 in b_pt: " << b_pt.scale() << endl;
wxb_ct
.
push_back
(
x_ct
);
}
Ciphertext
a
;
// std::cout << "[ANN Engine] Computing wxb_ct (add_many_ciphertext_ckks)..... " << endl;
add_many_ciphertext_ckks
(
eval
,
wxb_ct
,
a
);
add_plain_inplace_ciphertext_ckks
(
eval
,
a
,
b_pt
);
// add_ciphertext_ckks(eval, ct, b_ct, ct);
// rescale_to_next_inplace_ckks(eval, ct);
std
::
cout
<<
"[ANN Engine] Computing wxb_ct ..... end"
<<
endl
;
return
a
;
}
int
ANNProcess
(
vector
<
string
>
&
wFilePaths
,
string
&
xCT
,
vector
<
string
>
&
bFilePaths
,
string
&
result_name
,
string
&
result_dir
,
int
&
sample_size
,
string
&
key_dir
)
{
// This should be revised and improved!
struct
cevaluator_t
eval
;
init_operator_ckks
(
eval
,
key_dir
);
struct
cencryptor_t
encr
;
init_operator_ckks
(
encr
,
key_dir
);
int
slot_count
=
encr
.
ccode
->
slot_count
();
std
::
cout
<<
slot_count
<<
endl
;
// cout << "[ANN Engine] loading vector x " << xCT << "..... " << endl;
Ciphertext
a
;
// x
load_ciphertext_ckks
(
eval
,
a
,
xCT
);
// cout << "[ANN Engine] loading vector x ..... end \n" << endl;
// for (size_t i = 0; i < wFilePaths.size(); i++)
for
(
size_t
i
=
0
;
i
<
3
;
i
++
)
{
/* code */
cout
<<
"[ANN Engine] compute a"
<<
i
+
1
<<
" ....."
<<
endl
;
a
=
action_compute
(
encr
,
eval
,
wFilePaths
[
i
],
a
,
bFilePaths
[
i
],
sample_size
);
cout
<<
"[ANN Engine] compute a"
<<
i
+
1
<<
" ..... end
\n
"
<<
endl
;
}
// getIndexVector(encr, eval, a, slot_count, 3);
// Ciphertext b = getIndexVector(encr, eval, a, 3, 1);
// index_vector = getIndexVector(encr, eval, x_ct, 3, 3);
save_ciphertext
(
a
,
result_dir
+
"/"
+
result_name
+
".ct"
);
delete_operator_ckks
(
encr
);
delete_operator_ckks
(
eval
);
return
0
;
}
void
sub_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
sub
(
ct1
,
ct2
,
ct_out
);
}
void
sub_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
)
{
op_st
.
eval
->
sub_inplace
(
ct1
,
ct2
);
}
void
sub_plain_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
)
{
op_st
.
eval
->
sub_plain_inplace
(
ct
,
plain
);
}
void
negate_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
negate_inplace
(
ct
);
}
void
add_plain_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
struct
Ciphertext
&
ct
,
const
Plaintext
&
plain
)
{
op_st
.
eval
->
add_plain_inplace
(
ct
,
plain
);
}
void
add_plain_inplace_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
struct
Ciphertext
&
ct
,
const
Plaintext
&
plain
)
{
op_st
.
eval
->
add_plain_inplace
(
ct
,
plain
);
}
void
add_plain_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
,
Plaintext
&
plain
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
add_plain
(
ct
,
plain
,
ct_out
);
}
void
add_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
add
(
ct1
,
ct2
,
ct_out
);
}
void
add_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
add
(
ct1
,
ct2
,
ct_out
);
}
void
add_many_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
add_many
(
cts
,
ct_out
);
}
void
add_many_ciphertext
(
struct
evaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
add_many
(
cts
,
ct_out
);
}
// void add_many_ciphertext_ckks(struct cevaluator_t &op_st, vector<Ciphertext> &cts, Ciphertext &ct_out)
// {
// op_st.eval->add_many(cts, ct_out);
// }
void
multiply_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
multiply
(
ct1
,
ct2
,
ct_out
);
}
void
multiply_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
multiply
(
ct1
,
ct2
,
ct_out
);
}
void
multiply_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct1
,
Ciphertext
&
ct2
)
{
op_st
.
eval
->
multiply_inplace
(
ct1
,
ct2
);
}
void
multiply_ciphertexts
(
struct
evaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
multiply_many
(
cts
,
op_st
.
lk
,
ct_out
);
}
void
multiply_ciphertexts_ckks
(
struct
cevaluator_t
&
op_st
,
vector
<
Ciphertext
>
&
cts
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
multiply_many
(
cts
,
op_st
.
lk
,
ct_out
);
}
void
multiply_plain_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
)
{
op_st
.
eval
->
multiply_plain_inplace
(
ct
,
plain
);
}
void
multiply_plain_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
,
const
Plaintext
&
plain
,
Ciphertext
&
out
)
{
op_st
.
eval
->
multiply_plain
(
ct
,
plain
,
out
);
}
// void multiply_plain_inplace_ckks(struct evaluator_t &op_st, Ciphertext &ct, const Plaintext &plain)
// {
// op_st.eval->multiply_plain_inplace(ct, plain);
// }
void
relinearize_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
relinearize_inplace
(
ct
,
op_st
.
lk
);
}
void
relinearize_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
relinearize_inplace
(
ct
,
op_st
.
lk
);
}
void
relinearize
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
Ciphertext
&
ct_out
)
{
op_st
.
eval
->
relinearize
(
ct
,
op_st
.
lk
,
ct_out
);
}
void
rescale_to_next_inplace
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
rescale_to_next_inplace
(
ct
);
}
void
rescale_to_next_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
rescale_to_next_inplace
(
ct
);
}
void
exponentiate_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
,
uint64_t
&
exponent
)
{
op_st
.
eval
->
exponentiate_inplace
(
ct
,
exponent
,
op_st
.
lk
);
}
void
mod_switch_to_next_inplace_ciphertext
(
struct
evaluator_t
&
op_st
,
Ciphertext
&
ct
)
{
op_st
.
eval
->
mod_switch_to_next_inplace
(
ct
);
}
void
mod_switch_to_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Plaintext
&
plain
,
parms_id_type
&
parms_id
)
{
op_st
.
eval
->
mod_switch_to_inplace
(
plain
,
parms_id
);
}
void
mod_switch_to_inplace_ckks
(
struct
cevaluator_t
&
op_st
,
Ciphertext
&
ct
,
parms_id_type
&
parms_id
)
{
op_st
.
eval
->
mod_switch_to_inplace
(
ct
,
parms_id
);
}
void
mod_switch_to_next_inplace_ciphertext_ckks
(
struct
cevaluator_t
&
op_st
,
Plaintext
&
plain
)
{
op_st
.
eval
->
mod_switch_to_next_inplace
(
plain
);
}
// mod_switch_drop_to_next
\ No newline at end of file
bigpiseal3.5.1/native/examples/ANN/v1/ANN_genkey_v1.cpp
0 → 100644
View file @
c6c9d2f5
#include "seal_api.h"
using
namespace
std
;
using
namespace
seal
;
int
main
(
int
argc
,
char
**
argv
)
{
string
key_dir
=
argv
[
1
];
// size_t poly_d = 4096;
// size_t poly_d = 8192;
size_t
poly_d
=
16384
;
// Params option 1
// int bit_size = 20;
int
bit_size
=
0
;
// Params option 2
// uint64_t plain_modulus = 1032193;
// vector<int> bit_sizes = { 36, 36, 37 };
// timeval t0, t1;
// unsigned long dt = 0;
// gettimeofday(&t0, NULL);
generate_keys_ckks
(
poly_d
,
bit_size
,
key_dir
,
true
);
// batching_generate_keys(poly_d, bit_sizes, plain_modulus, key_dir, true);
// gettimeofday(&t1, NULL);
// dt = 1000000 * (t1.tv_sec - t0.tv_sec) + (t1.tv_usec - t0.tv_usec);
// cout << "[INFO] keys generation time in seconds: " << ((float)dt)/1000000 << endl;
return
0
;
}
bigpiseal3.5.1/native/examples/ANN/v1/CMakeLists.txt
0 → 100644
View file @
c6c9d2f5
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT license.
cmake_minimum_required
(
VERSION 3.10
)
set
(
TEST_NAME ANN
)
#set(GENKEY_BIN ${TEST_NAME}_genkey)
#set(ENCR_BIN ${TEST_NAME}_encrypt)
#set(DECR_BIN ${TEST_NAME}_decrypt)
#set(EVAL_BIN ${TEST_NAME}_evaluate)
#set(SCR_TEST test.sh)
set
(
GENKEY_BIN_2
${
TEST_NAME
}
_genkey_v1
)
set
(
ENCR_BIN_2
${
TEST_NAME
}
_encrypt_v1
)
set
(
DECR_BIN_2
${
TEST_NAME
}
_decrypt_v1
)
set
(
DECR_RESULT_BIN_2
${
TEST_NAME
}
_decrypt_result_v1
)
set
(
EVAL_BIN_2
${
TEST_NAME
}
_evaluate_v1
)
set
(
SCR_ENC encrypt.sh
)
set
(
SCR_DEC decrypt.sh
)
set
(
SCR_DEC_RESULT decrypt_result.sh
)
set
(
SCR_EVAL eval.sh
)
set
(
SCR_GEN genkey.sh
)
set
(
SCR_TEST_v1 test_v1.sh
)
set
(
GENKEY_SRCS
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/
${
TEST_NAME
}
_genkey.cpp
)
set
(
ENCR_SRCS
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/
${
TEST_NAME
}
_encrypt.cpp
)
set
(
DECR_SRCS
${
CMAKE_CURRENT_LIST_DIR
}
/util.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/
${
TEST_NAME
}
_decrypt.cpp
)
set
(
EVAL_SRCS
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/util.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/
${
TEST_NAME
}
_evaluate.cpp
)
set
(
GENKEY_SRCS_2
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/
${
TEST_NAME
}
_genkey_v1.cpp
)
set
(
ENCR_SRCS_2
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/
${
TEST_NAME
}
_encrypt_v1.cpp
)
set
(
DECR_SRCS_2
${
CMAKE_CURRENT_LIST_DIR
}
/util.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/
${
TEST_NAME
}
_decrypt_v1.cpp
)
set
(
DECR_RESULT_SRCS_2
${
CMAKE_CURRENT_LIST_DIR
}
/util.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/
${
TEST_NAME
}
_decrypt_result_v1.cpp
)
set
(
EVAL_SRCS_2
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/util.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/csv_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/print_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/
${
TEST_NAME
}
_evaluate_v1.cpp
)
set
(
HEADER_FILES
${
CMAKE_CURRENT_LIST_DIR
}
/seal_api.h
${
CMAKE_CURRENT_LIST_DIR
}
/util.h
${
CMAKE_CURRENT_LIST_DIR
}
/csv_api.cpp
${
CMAKE_CURRENT_LIST_DIR
}
/print_api.cpp
)
#add_executable(${GENKEY_BIN} ${GENKEY_SRCS} ${HEADER_FILES})
#add_executable(${ENCR_BIN} ${ENCR_SRCS} ${HEADER_FILES})
#add_executable(${DECR_BIN} ${DECR_SRCS} ${HEADER_FILES})
#add_executable(${EVAL_BIN} ${EVAL_SRCS} ${HEADER_FILES})
#target_include_directories(${TEST_NAME} PRIVATE ${HEADER_DIR})
add_executable
(
${
GENKEY_BIN_2
}
${
GENKEY_SRCS_2
}
${
HEADER_FILES
}
)
add_executable
(
${
ENCR_BIN_2
}
${
ENCR_SRCS_2
}
${
HEADER_FILES
}
)
add_executable
(
${
DECR_BIN_2
}
${
DECR_SRCS_2
}
${
HEADER_FILES
}
)
add_executable
(
${
DECR_RESULT_BIN_2
}
${
DECR_RESULT_SRCS_2
}
${
HEADER_FILES
}
)
add_executable
(
${
EVAL_BIN_2
}
${
EVAL_SRCS_2
}
${
HEADER_FILES
}
)
#target_include_directories(${TEST_NAME} PRIVATE ${HEADER_DIR})
# Import Microsoft SEAL
find_package
(
SEAL 3.5 REQUIRED
)
# Link Microsoft SEAL
#target_link_libraries(${GENKEY_BIN} SEAL::seal)
#target_link_libraries(${ENCR_BIN} SEAL::seal)
#target_link_libraries(${DECR_BIN} SEAL::seal)
#target_link_libraries(${EVAL_BIN} SEAL::seal)
target_link_libraries
(
${
GENKEY_BIN_2
}
SEAL::seal
)
target_link_libraries
(
${
ENCR_BIN_2
}
SEAL::seal
)
target_link_libraries
(
${
DECR_BIN_2
}
SEAL::seal
)
target_link_libraries
(
${
DECR_RESULT_BIN_2
}
SEAL::seal
)
target_link_libraries
(
${
EVAL_BIN_2
}
SEAL::seal
)
set
(
CMAKE_RUNTIME_OUTPUT_DIRECTORY
${
CMAKE_SOURCE_DIR
}
/bin
)
#set_target_properties(${GENKEY_BIN}
# PROPERTIES
# RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TEST_NAME}"
# )
#set_target_properties(${ENCR_BIN}
# PROPERTIES
# RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TEST_NAME}"
# )
#set_target_properties(${DECR_BIN}
# PROPERTIES
# RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TEST_NAME}"
# )
#set_target_properties(${EVAL_BIN}
# PROPERTIES
# RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TEST_NAME}"
# )
set_target_properties
(
${
GENKEY_BIN_2
}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY
"
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1"
)
set_target_properties
(
${
ENCR_BIN_2
}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY
"
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1"
)
set_target_properties
(
${
DECR_BIN_2
}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY
"
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1"
)
set_target_properties
(
${
DECR_RESULT_BIN_2
}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY
"
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1"
)
set_target_properties
(
${
EVAL_BIN_2
}
PROPERTIES
RUNTIME_OUTPUT_DIRECTORY
"
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1"
)
file
(
COPY
${
CMAKE_CURRENT_SOURCE_DIR
}
/
${
SCR_TEST
}
DESTINATION
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1
)
file
(
COPY
${
CMAKE_CURRENT_SOURCE_DIR
}
/
${
SCR_ENC
}
DESTINATION
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1
)
file
(
COPY
${
CMAKE_CURRENT_SOURCE_DIR
}
/
${
SCR_DEC
}
DESTINATION
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1
)
file
(
COPY
${
CMAKE_CURRENT_SOURCE_DIR
}
/
${
SCR_DEC_RESULT
}
DESTINATION
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1
)
file
(
COPY
${
CMAKE_CURRENT_SOURCE_DIR
}
/
${
SCR_EVAL
}
DESTINATION
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1
)
file
(
COPY
${
CMAKE_CURRENT_SOURCE_DIR
}
/
${
SCR_GEN
}
DESTINATION
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1
)
file
(
COPY
${
CMAKE_CURRENT_SOURCE_DIR
}
/
${
SCR_TEST_v1
}
DESTINATION
${
CMAKE_RUNTIME_OUTPUT_DIRECTORY
}
/
${
TEST_NAME
}
/v1
)
\ No newline at end of file
bigpiseal3.5.1/native/examples/ANN/v1/csv_api.cpp
0 → 100644
View file @
c6c9d2f5
/*
*/
#include "csv_api.h"
/* namespaces */
using
namespace
std
;
int
load_csv_line
(
const
string
&
filename
,
vector
<
int64_t
>
&
in
,
bool
print_info
)
{
int
ret
=
1
;
if
(
in
.
size
()
>
0
)
in
.
clear
();
fstream
fd
;
fd
.
open
(
filename
,
std
::
fstream
::
in
);
if
(
!
fd
.
is_open
())
{
cerr
<<
"[csv-error] opening csv file failure"
<<
endl
;
ret
=
0
;
}
else
{
string
l
;
getline
(
fd
,
l
);
if
(
l
.
empty
())
{
cerr
<<
"[csv-error] empty csv file, please provide a valid csv file"
<<
endl
;
ret
=
0
;
}
else
{
string
w
;
stringstream
s
(
l
);
while
(
getline
(
s
,
w
,
','
))
{
int64_t
val
=
boost
::
lexical_cast
<
int64_t
>
(
w
);
in
.
push_back
(
val
);
}
fd
.
close
();
/* print the obtained vector of numbers from a csv line */
if
(
print_info
)
{
cout
<<
"[csv-info] loading vector from a csv input (line): "
<<
endl
;
for
(
int
i
=
0
;
i
<
(
int
)(
in
.
size
());
++
i
)
cout
<<
in
[
i
]
<<
" "
;
cout
<<
endl
;
}
}
}
return
ret
;
}
int
load_csv_line
(
const
string
&
filename
,
vector
<
double
>
&
in
,
bool
print_info
)
{
int
ret
=
1
;
if
(
in
.
size
()
>
0
)
in
.
clear
();
fstream
fd
;
fd
.
open
(
filename
,
std
::
fstream
::
in
);
if
(
!
fd
.
is_open
())
{
cerr
<<
"[csv-error] opening csv file failure"
<<
endl
;
ret
=
0
;
}
else
{
string
l
;
getline
(
fd
,
l
);
if
(
l
.
empty
())
{
cerr
<<
"[csv-error] empty csv file, please provide a valid csv file"
<<
endl
;
ret
=
0
;
}
else
{
string
w
;
stringstream
s
(
l
);
while
(
getline
(
s
,
w
,
','
))
{
double
val
=
boost
::
lexical_cast
<
double
>
(
w
);
in
.
push_back
(
val
);
}
fd
.
close
();
/* print the obtained vector of numbers from a csv line */
if
(
print_info
)
{
cout
<<
"[csv-info] loading vector from a csv input (line): "
<<
endl
;
for
(
int
i
=
0
;
i
<
(
int
)(
in
.
size
());
++
i
)
cout
<<
in
[
i
]
<<
" "
;
cout
<<
endl
;
}
}
}
return
ret
;
}
int
load_windows_csv_line
(
const
string
&
filename
,
vector
<
int64_t
>
&
in
,
bool
print_info
)
{
int
ret
=
1
;
if
(
in
.
size
()
>
0
)
in
.
clear
();
fstream
fd
;
fd
.
open
(
filename
,
std
::
fstream
::
in
);
if
(
!
fd
.
is_open
())
{
cerr
<<
"[csv-error] opening csv file failure"
<<
endl
;
ret
=
0
;
}
else
{
string
l
;
getline
(
fd
,
l
);
if
(
l
.
empty
())
{
cerr
<<
"[csv-error] empty csv file, please provide a valid csv file"
<<
endl
;
ret
=
0
;
}
else
{
if
(
l
[
l
.
size
()
-
1
]
==
'\r'
)
l
.
erase
(
l
.
size
()
-
1
);
string
w
;
stringstream
s
(
l
);
while
(
getline
(
s
,
w
,
','
))
{
int64_t
val
=
boost
::
lexical_cast
<
int64_t
>
(
w
);
in
.
push_back
(
val
);
}
fd
.
close
();
/* print the obtained vector of numbers from a csv line */
if
(
print_info
)
{
cout
<<
"[csv-info] loading vector from a csv input (line): "
<<
endl
;
for
(
int
i
=
0
;
i
<
(
int
)(
in
.
size
());
++
i
)
cout
<<
in
[
i
]
<<
" "
;
cout
<<
endl
;
}
}
}
return
ret
;
}
int
load_windows_csv_line
(
const
string
&
filename
,
vector
<
double
>
&
in
,
bool
print_info
)
{
int
ret
=
1
;
if
(
in
.
size
()
>
0
)
in
.
clear
();
fstream
fd
;
fd
.
open
(
filename
,
std
::
fstream
::
in
);
if
(
!
fd
.
is_open
())
{
cerr
<<
"[csv-error] opening csv file failure"
<<
endl
;
ret
=
0
;
}
else
{
string
l
;
getline
(
fd
,
l
);
if
(
l
.
empty
())
{
cerr
<<
"[csv-error] empty csv file, please provide a valid csv file"
<<
endl
;
ret
=
0
;
}
else
{
if
(
l
[
l
.
size
()
-
1
]
==
'\r'
)
l
.
erase
(
l
.
size
()
-
1
);
string
w
;
stringstream
s
(
l
);
while
(
getline
(
s
,
w
,
','
))
{
double
val
=
boost
::
lexical_cast
<
double
>
(
w
);
in
.
push_back
(
val
);
}
fd
.
close
();
/* print the obtained vector of numbers from a csv line */
if
(
print_info
)
{
cout
<<
"[csv-info] loading vector from a csv input (line): "
<<
endl
;
for
(
int
i
=
0
;
i
<
(
int
)(
in
.
size
());
++
i
)
cout
<<
in
[
i
]
<<
" "
;
cout
<<
endl
;
}
}
}
return
ret
;
}
void
string_to_number_vector
(
string
&
s
,
vector
<
int64_t
>
&
in
,
bool
print_info
)
{
if
(
in
.
size
()
>
0
)
in
.
clear
();
string
w
;
stringstream
c
(
s
);
while
(
getline
(
c
,
w
,
','
))
{
int64_t
val
=
boost
::
lexical_cast
<
int64_t
>
(
w
);
in
.
push_back
(
val
);
}
/* print the obtained vectors of numbers from a csv file */
if
(
print_info
)
{
cout
<<
"[csv-info] converting a csv line to a vector of numbers"
<<
endl
;
cout
<<
"[csv-info] input line: "
<<
s
<<
endl
;
cout
<<
"[csv-info] output vector: "
;
for
(
int
i
=
0
;
i
<
(
int
)(
in
.
size
());
++
i
)
cout
<<
in
[
i
]
<<
" "
;
cout
<<
endl
;
}
}
void
string_to_number_vector
(
string
&
s
,
vector
<
double
>
&
in
,
bool
print_info
)
{
if
(
in
.
size
()
>
0
)
in
.
clear
();
string
w
;
stringstream
c
(
s
);
while
(
getline
(
c
,
w
,
','
))
{
double
val
=
boost
::
lexical_cast
<
double
>
(
w
);
in
.
push_back
(
val
);
}
/* print the obtained vectors of numbers from a csv file */
if
(
print_info
)
{
cout
<<
"[csv-info] converting a csv line to a vector of numbers"
<<
endl
;
cout
<<
"[csv-info] input line: "
<<
s
<<
endl
;
cout
<<
"[csv-info] output vector: "
;
for
(
int
i
=
0
;
i
<
(
int
)(
in
.
size
());
++
i
)
cout
<<
in
[
i
]
<<
" "
;
cout
<<
endl
;
}
}
int
load_csv_file
(
const
string
&
filename
,
vector
<
vector
<
int64_t
>>
&
in
,
bool
print_info
)
{
int
ret
=
1
;
if
(
in
.
size
()
>
0
)
in
.
clear
();
fstream
fd
;
fd
.
open
(
filename
,
std
::
fstream
::
in
);
if
(
!
fd
.
is_open
())
{
cerr
<<
"[csv-error] opening csv file failure"
<<
endl
;
ret
=
0
;
}
else
{
while
(
!
fd
.
eof
())
{
string
l
;
getline
(
fd
,
l
);
vector
<
int64_t
>
tmp
;
string_to_number_vector
(
l
,
tmp
);
in
.
push_back
(
tmp
);
}
fd
.
close
();
/* print the different csv lines */
if
(
print_info
)
{
cout
<<
"[csv-info] loaded vectors from the lines of a csv file: "
<<
endl
;
print_matrix
(
in
);
}
}
return
ret
;
}
void
remove_carriage_return
(
std
::
string
&
line
)
{
if
(
*
line
.
rbegin
()
==
'\r'
)
{
line
.
erase
(
line
.
length
()
-
1
);
}
}
int
load_csv_file
(
const
string
&
filename
,
vector
<
vector
<
double
>>
&
in
,
bool
print_info
)
{
int
ret
=
1
;
if
(
in
.
size
()
>
0
)
in
.
clear
();
fstream
fd
;
fd
.
open
(
filename
,
std
::
fstream
::
in
);
if
(
!
fd
.
is_open
())
{
cerr
<<
"[csv-error] opening csv file failure"
<<
endl
;
ret
=
0
;
}
else
{
int
i
=
0
;
while
(
!
fd
.
eof
())
{
string
l
;
getline
(
fd
,
l
);
remove_carriage_return
(
l
);
vector
<
double
>
tmp
;
string_to_number_vector
(
l
,
tmp
);
in
.
push_back
(
tmp
);
i
++
;
}
fd
.
close
();
/* print the different csv lines */
if
(
print_info
)
{
cout
<<
"[csv-info] loaded vectors from the lines of a csv file: "
<<
endl
;
print_matrix
(
in
);
}
}
return
ret
;
}
int
read_csv_file
(
const
string
&
filename
,
vector
<
string
>
&
in
,
bool
print_info
)
{
int
ret
=
1
;
if
(
in
.
size
()
>
0
)
in
.
clear
();
fstream
fd
;
fd
.
open
(
filename
,
std
::
fstream
::
in
);
if
(
!
fd
.
is_open
())
{
cerr
<<
"[csv-error] opening csv file failure"
<<
endl
;
ret
=
0
;
}
else
{
while
(
!
fd
.
eof
())
{
string
l
;
getline
(
fd
,
l
);
in
.
push_back
(
l
);
}
fd
.
close
();
/* print the different csv lines */
if
(
print_info
)
{
cout
<<
"[csv-info] reading csv lines: "
<<
endl
;
for
(
int
i
=
0
;
i
<
(
int
)(
in
.
size
());
++
i
)
cout
<<
in
[
i
]
<<
endl
;
}
}
return
ret
;
}
int
write_to_csv_file
(
const
string
&
filename
,
vector
<
int64_t
>
&
in
)
{
int
ret
=
1
;
ofstream
fd
(
filename
);
if
(
!
fd
.
is_open
())
{
cerr
<<
"[csv-error] could not open the csv output file"
<<
endl
;
ret
=
0
;
}
else
{
for
(
int
i
=
0
;
i
<
(
int
)(
in
.
size
()
-
1
);
++
i
)
{
fd
<<
in
[
i
]
<<
","
;
}
fd
<<
in
[
in
.
size
()
-
1
]
<<
endl
;
fd
.
close
();
}
return
ret
;
}
int
write_to_csv_file
(
const
string
&
filename
,
vector
<
double
>
&
in
)
{
int
ret
=
1
;
ofstream
fd
(
filename
);
if
(
!
fd
.
is_open
())
{
cerr
<<
"[csv-error] could not open the csv output file"
<<
endl
;
ret
=
0
;
}
else
{
for
(
int
i
=
0
;
i
<
(
int
)(
in
.
size
()
-
1
);
++
i
)
{
fd
<<
in
[
i
]
<<
","
;
}
fd
<<
in
[
in
.
size
()
-
1
]
<<
endl
;
fd
.
close
();
}
return
ret
;
}
int
convert_crlf_to_lf
(
char
*
in
,
char
*
out
)
{
int
ret
=
1
;
int
c
;
FILE
*
ifp
,
*
ofp
;
if
((
ifp
=
fopen
(
in
,
"rb"
))
==
NULL
)
{
cout
<<
"[csv-error] could not open input file"
<<
endl
;
ret
=
0
;
}
else
{
if
((
ofp
=
fopen
(
out
,
"wb"
))
==
NULL
)
{
cout
<<
"[csv-error] could not open output file"
<<
endl
;
fclose
(
ifp
);
ret
=
0
;
}
else
{
while
((
c
=
getc
(
ifp
))
!=
EOF
)
{
if
(
c
==
'\r'
)
{
putc
(
'\n'
,
ofp
);
c
=
getc
(
ifp
);
if
(
c
==
EOF
)
break
;
if
(
c
==
'\n'
)
continue
;
}
putc
(
c
,
ofp
);
}
fclose
(
ifp
);
fclose
(
ofp
);
}
}
return
ret
;
}
int
convert_lf_to_crlf
(
char
*
in
,
char
*
out
)
{
int
ret
=
1
;
int
c
;
FILE
*
ifp
,
*
ofp
;
if
((
ifp
=
fopen
(
in
,
"rb"
))
==
NULL
)
{
cout
<<
"[csv-error] could not open input file
\n
"
<<
endl
;
ret
=
0
;
}
else
{
if
((
ofp
=
fopen
(
out
,
"wb"
))
==
NULL
)
{
cout
<<
"[csv-error] could not open output file
\n
"
<<
endl
;
fclose
(
ifp
);
ret
=
0
;
}
else
{
while
((
c
=
getc
(
ifp
))
!=
EOF
)
{
if
(
c
==
'\n'
)
{
putc
(
'\r'
,
ofp
);
putc
(
'\n'
,
ofp
);
c
=
getc
(
ifp
);
if
(
c
==
EOF
)
break
;
if
(
c
==
'\n'
)
continue
;
}
putc
(
c
,
ofp
);
}
fclose
(
ifp
);
fclose
(
ofp
);
}
}
return
ret
;
}
bigpiseal3.5.1/native/examples/ANN/v1/csv_api.h
0 → 100644
View file @
c6c9d2f5
/*
(C) Copyright 2022 CEA LIST. All Rights Reserved.
*/
#ifndef _CSV_API_H_
#define _CSV_API_H_
/* includes */
#include <iostream>
#include <fstream>
#include <string>
#include <cstdio>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include "print_api.h"
/* prototypes */
int
load_csv_line
(
const
std
::
string
&
filename
,
std
::
vector
<
int64_t
>&
in
,
bool
print_info
=
0
);
int
load_csv_line
(
const
std
::
string
&
filename
,
std
::
vector
<
double
>&
in
,
bool
print_info
=
0
);
int
load_windows_csv_line
(
const
std
::
string
&
filename
,
std
::
vector
<
int64_t
>&
in
,
bool
print_info
=
0
);
int
load_windows_csv_line
(
const
std
::
string
&
filename
,
std
::
vector
<
double
>&
in
,
bool
print_info
=
0
);
void
string_to_number_vector
(
std
::
string
&
s
,
std
::
vector
<
int64_t
>&
in
,
bool
print_info
=
0
);
void
string_to_number_vector
(
std
::
string
&
s
,
std
::
vector
<
double
>&
in
,
bool
print_info
=
0
);
int
load_csv_file
(
const
std
::
string
&
filename
,
std
::
vector
<
std
::
vector
<
int64_t
>>&
in
,
bool
print_info
=
0
);
int
load_csv_file
(
const
std
::
string
&
filename
,
std
::
vector
<
std
::
vector
<
double
>>&
in
,
bool
print_info
=
0
);
int
read_csv_file
(
const
std
::
string
&
filename
,
std
::
vector
<
std
::
string
>&
in
,
bool
print_info
=
0
);
int
write_to_csv_file
(
const
std
::
string
&
filename
,
std
::
vector
<
int64_t
>
&
in
);
int
write_to_csv_file
(
const
std
::
string
&
filename
,
std
::
vector
<
double
>
&
in
);
int
convert_crlf_to_lf
(
char
*
in
,
char
*
out
);
int
convert_lf_to_crlf
(
char
*
in
,
char
*
out
);
#endif
Prev
1
…
3
4
5
6
7
8
9
10
11
…
14
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment