@@ -1539,24 +1539,165 @@ def performance_test_mt(th_index, lock, pool):
1539
1539
th_vector [i ].join ()
1540
1540
1541
1541
1542
+
1543
+ def save_example ():
1544
+
1545
+ print_example_banner ("Example: Basics I" );
1546
+
1547
+
1548
+ parms = EncryptionParameters ()
1549
+
1550
+ # We first set the polynomial modulus. This must be a power-of-2 cyclotomic
1551
+ # polynomial, i.e. a polynomial of the form "1x^(power-of-2) + 1". The polynomial
1552
+ # modulus should be thought of mainly affecting the security level of the scheme;
1553
+ # larger polynomial modulus makes the scheme more secure. At the same time, it
1554
+ # makes ciphertext sizes larger, and consequently all operations slower.
1555
+ # Recommended degrees for poly_modulus are 1024, 2048, 4096, 8192, 16384, 32768,
1556
+ # but it is also possible to go beyond this. Since we perform only a very small
1557
+ # computation in this example, it suffices to use a very small polynomial modulus
1558
+ parms .set_poly_modulus ("1x^2048 + 1" )
1559
+
1560
+
1561
+ parms .set_coeff_modulus (seal .coeff_modulus_128 (2048 ))
1562
+
1563
+ # The plaintext modulus can be any positive integer, even though here we take
1564
+ # it to be a power of two. In fact, in many cases one might instead want it to
1565
+ # be a prime number; we will see this in example_batching(). The plaintext
1566
+ # modulus determines the size of the plaintext data type, but it also affects
1567
+ # the noise budget in a freshly encrypted ciphertext, and the consumption of
1568
+ # the noise budget in homomorphic multiplication. Thus, it is essential to try
1569
+ # to keep the plaintext data type as small as possible for good performance.
1570
+ # The noise budget in a freshly encrypted ciphertext is
1571
+
1572
+ # ~ log2(coeff_modulus/plain_modulus) (bits)
1573
+
1574
+ # and the noise budget consumption in a homomorphic multiplication is of the
1575
+ # form log2(plain_modulus) + (other terms).
1576
+ parms .set_plain_modulus (1 << 8 )
1577
+
1578
+ # Now that all parameters are set, we are ready to construct a SEALContext
1579
+ # object. This is a heavy class that checks the validity and properties of
1580
+ # the parameters we just set, and performs and stores several important
1581
+ # pre-computations.
1582
+ context = SEALContext (parms )
1583
+
1584
+ # Print the parameters that we have chosen
1585
+ print_parameters (context );
1586
+
1587
+
1588
+ encoder = IntegerEncoder (context .plain_modulus ())
1589
+
1590
+ # We are now ready to generate the secret and public keys. For this purpose we need
1591
+ # an instance of the KeyGenerator class. Constructing a KeyGenerator automatically
1592
+ # generates the public and secret key, which can then be read to local variables.
1593
+ # To create a fresh pair of keys one can call KeyGenerator::generate() at any time.
1594
+ keygen = KeyGenerator (context )
1595
+ public_key = keygen .public_key ()
1596
+ secret_key = keygen .secret_key ()
1597
+
1598
+ # To be able to encrypt, we need to construct an instance of Encryptor. Note that
1599
+ # the Encryptor only requires the public key.
1600
+ encryptor = Encryptor (context , public_key )
1601
+
1602
+ # Computations on the ciphertexts are performed with the Evaluator class.
1603
+ evaluator = Evaluator (context )
1604
+
1605
+ # We will of course want to decrypt our results to verify that everything worked,
1606
+ # so we need to also construct an instance of Decryptor. Note that the Decryptor
1607
+ # requires the secret key.
1608
+ decryptor = Decryptor (context , secret_key )
1609
+
1610
+ # We start by encoding two integers as plaintext polynomials.
1611
+ value1 = 5 ;
1612
+ plain1 = encoder .encode (value1 );
1613
+ print ("Encoded " + (str )(value1 ) + " as polynomial " + plain1 .to_string () + " (plain1)" )
1614
+
1615
+ value2 = - 7 ;
1616
+ plain2 = encoder .encode (value2 );
1617
+ print ("Encoded " + (str )(value2 ) + " as polynomial " + plain2 .to_string () + " (plain2)" )
1618
+
1619
+ # Encrypting the values is easy.
1620
+ encrypted1 = Ciphertext ()
1621
+ encrypted2 = Ciphertext ()
1622
+ print ("Encrypting plain1: " )
1623
+ encryptor .encrypt (plain1 , encrypted1 )
1624
+ print ("Done (encrypted1)" )
1625
+
1626
+ encrypted1 .save ()
1627
+
1628
+ print ("Encrypting plain2: " )
1629
+ encryptor .encrypt (plain2 , encrypted2 )
1630
+ print ("Done (encrypted2)" )
1631
+
1632
+ # To illustrate the concept of noise budget, we print the budgets in the fresh
1633
+ # encryptions.
1634
+ print ("Noise budget in encrypted1: " + (str )(decryptor .invariant_noise_budget (encrypted1 )) + " bits" )
1635
+ print ("Noise budget in encrypted2: " + (str )(decryptor .invariant_noise_budget (encrypted2 )) + " bits" )
1636
+
1637
+ # As a simple example, we compute (-encrypted1 + encrypted2) * encrypted2.
1638
+
1639
+ # Negation is a unary operation.
1640
+ evaluator .negate (encrypted1 )
1641
+
1642
+ # Negation does not consume any noise budget.
1643
+ print ("Noise budget in -encrypted1: " + (str )(decryptor .invariant_noise_budget (encrypted1 )) + " bits" )
1644
+
1645
+ # Addition can be done in-place (overwriting the first argument with the result,
1646
+ # or alternatively a three-argument overload with a separate destination variable
1647
+ # can be used. The in-place variants are always more efficient. Here we overwrite
1648
+ # encrypted1 with the sum.
1649
+ evaluator .add (encrypted1 , encrypted2 )
1650
+
1651
+ # It is instructive to think that addition sets the noise budget to the minimum
1652
+ # of the input noise budgets. In this case both inputs had roughly the same
1653
+ # budget going on, and the output (in encrypted1) has just slightly lower budget.
1654
+ # Depending on probabilistic effects, the noise growth consumption may or may
1655
+ # not be visible when measured in whole bits.
1656
+ print ("Noise budget in -encrypted1 + encrypted2: " + (str )(decryptor .invariant_noise_budget (encrypted1 )) + " bits" )
1657
+
1658
+ # Finally multiply with encrypted2. Again, we use the in-place version of the
1659
+ # function, overwriting encrypted1 with the product.
1660
+ evaluator .multiply (encrypted1 , encrypted2 )
1661
+
1662
+ # Multiplication consumes a lot of noise budget. This is clearly seen in the
1663
+ # print-out. The user can change the plain_modulus to see its effect on the
1664
+ # rate of noise budget consumption.
1665
+ print ("Noise budget in (-encrypted1 + encrypted2) * encrypted2: " + (str )(decryptor .invariant_noise_budget (encrypted1 )) + " bits" )
1666
+
1667
+ # Now we decrypt and decode our result.
1668
+ plain_result = Plaintext ()
1669
+ print ("Decrypting result: " )
1670
+ decryptor .decrypt (encrypted1 , plain_result )
1671
+ print ("Done" )
1672
+
1673
+ # Print the result plaintext polynomial.
1674
+ print ("Plaintext polynomial: " + plain_result .to_string ())
1675
+
1676
+ # Decode to obtain an integer result.
1677
+ print ("Decoded integer: " + (str )(encoder .decode_int32 (plain_result )))
1678
+
1679
+
1680
+
1542
1681
def main ():
1543
- # Example: Basics I
1544
- example_basics_i ()
1545
- # Example: Basics II
1546
- example_basics_ii ()
1547
- # Example: Weighted Average
1548
- example_weighted_average ()
1549
- # Example: Batching using CRT
1550
- example_batching ()
1551
- # Example: Parameter Selection
1552
- example_parameter_selection ()
1553
- # Example: Performance (Single Thread)
1554
- example_performance_st ()
1555
- # Example: Performance (Multi Thread)
1556
- th_count = ((int )(input ('Thread count: ' )))
1557
- if th_count > 0 : example_performance_mt (th_count )
1558
- else : print ('Invalid thread count.' )
1559
- # Wait for ENTER before closing screen.
1682
+ # # Example: Basics I
1683
+ # example_basics_i()
1684
+ # # Example: Basics II
1685
+ # example_basics_ii()
1686
+ # # Example: Weighted Average
1687
+ # example_weighted_average()
1688
+ # # Example: Batching using CRT
1689
+ # example_batching()
1690
+ # # Example: Parameter Selection
1691
+ # example_parameter_selection()
1692
+ # # Example: Performance (Single Thread)
1693
+ # example_performance_st()
1694
+ # # Example: Performance (Multi Thread)
1695
+ # th_count = ((int)(input('Thread count: ')))
1696
+ # if th_count > 0: example_performance_mt(th_count)
1697
+ # else: print('Invalid thread count.')
1698
+ # # Wait for ENTER before closing screen.
1699
+
1700
+ save_example ()
1560
1701
input ('Press ENTER to exit' )
1561
1702
1562
1703
def print_example_banner (title , ch = '*' , length = 78 ):
0 commit comments