<jupyter_start><jupyter_text>Part 9 - Intro to Encrypted ProgramsYou believe or you no believe, he dey possible to compute with encrypted data. Make I talk am another way sey he dey possible to run program where **ALL of the variables** in the program dey **encrypted**! For this tutoria we go learn basic tools of encrypted computation. In particular, we go focus on one popular approach called Secure Multi-Party Computation. We go build encrypted calculator wey fit perform calculations on encrypted numbers for this lesson. Person wey write am:- <NAME> - Twitter: [@iamtrask](https://twitter.com/iamtrask)- <NAME> - GitHub: [@LaRiffle](https://github.com/LaRiffle)Reference wey you fit use: - <NAME> - [Blog](https://mortendahl.github.io) - Twitter: [@mortendahlcs](https://twitter.com/mortendahlcs)Person wey translate am:- <NAME> - Twitter: [@techie991](https://twitter.com/techie991)  Step 1: Encryption Using Secure Multi-Party ComputationSMPC na strange form of "encryptioon". Instead make we use public/private key to encrypt a variable, we go split each value into multiple `shares`, each of dem operates like a private key. Typically, we go distribute these `shares` amongst 2 or more _owners_. Thus, to decrypt the variable, all owners must agree sey make we decryt am. In essence, everyone go get private key. Encrypt()So, let's say we wan "encrypt" a variable `x`, we fit do am in the following way.So, let's say we wanted to "encrypt" a variable `x`, we could do so in the following way. > Encryption no dey use floats or real numbers but he dey happen in a mathematical space called [integer quotient ring](http://mathworld.wolfram.com/QuotientRing.html) wey be the integer wey dey between `0` and `Q-1`, where `Q` na prime and "big enough" so that the space can contain all the numbers that we use in our experiments. In practice, given a value `x` integer, we do `x % Q` to fit in the ring. (That's why we dey avoid to use number `x' > Q`).<jupyter_code>Q = 1234567891011
x = 25
import random

def encrypt(x):
    share_a = random.randint(-Q,Q)
    share_b = random.randint(-Q,Q)
    share_c = (x - share_a - share_b) % Q
    return (share_a, share_b,  share_c)
encrypt(x)<jupyter_output><empty_output><jupyter_text>As you dey see am, we don split our variable `x` into 3 different shares, wey we go send to 3 different owners. Decrypt()If you wan decrypt these 3 shares, we go just sum them togeda and then we go take the modulus of the result (mod Q)<jupyter_code>def decrypt(*shares):
    return sum(shares) % Q
a,b,c = encrypt(25)
decrypt(a, b, c)<jupyter_output><empty_output><jupyter_text>Importantly, notice sey if we try to decrypt with only two shares, the decryption no dey work!<jupyter_code>decrypt(a, b)<jupyter_output><empty_output><jupyter_text>We go need all of the owners to participate if we wan decrypt value. Na like this `shares` dey act like private keys, everytin must to dey before we go fit decrypt a value.  Step 2: Basic Arithmetic Using SMPCThe extraordinary property of Secure Multi-Party Computation na him ability to perform computation **while the variables are still encrypted**. Make we demonstrate simple addition as he dey below.<jupyter_code>x = encrypt(25)
y = encrypt(5)
def add(x, y):
    z = list()
    # the first worker adds their shares together
    z.append((x[0] + y[0]) % Q)
    
    # the second worker adds their shares together
    z.append((x[1] + y[1]) % Q)
    
    # the third worker adds their shares together
    z.append((x[2] + y[2]) % Q)
    
    return z
decrypt(*add(x,y))<jupyter_output><empty_output><jupyter_text>Success!!!And there you get am! If each worker (separately) add their shares togeda, the resulting shares go decrypt to the correct vallues (25 + 5 == 30).As he dey so,  SMPC protocols exist go allow make this encrypted computation fit do the following operations:- addition (which we've just seen)- multiplication- comparisonIf we use these basic underlying primitives, we go fit perform arbitrary computation!!!In the next section, we go learn how we fit use PySyft library to perform these operations!  Step 3: SMPC Using PySyftIn the previous sections, we talk about some intuition wey dey around SMPC if he go work. However, we no wan use hand write everi primitive operations ourselves when we fit write our encrypted programs. So, for this section we go studi the basics of how we go do encrypted computation using PySyft. Particularly, we go focus on how to do 3 primitives wey we mention before: addition, multiplication, and comparison.First, we go first create few Virtual Worker (wey you don dey familiar as you don study our previous tutorials).<jupyter_code>import torch
import syft as sy
hook = sy.TorchHook(torch)

bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
bill = sy.VirtualWorker(hook, id="bill")<jupyter_output><empty_output><jupyter_text>Basic Encryption/DecryptionEncryption na simple way of taking PySyft tensor and calling .share(). Decryption na simple way of calling .get() on shared variable.<jupyter_code>x = torch.tensor([25])
x
encrypted_x = x.share(bob, alice, bill)
encrypted_x.get()<jupyter_output><empty_output><jupyter_text>Introspecting the Encrypted ValuesIf you look Bob, Alice and Bill's workers wella, we go see the shares we done create!<jupyter_code>bob._objects
x = torch.tensor([25]).share(bob, alice, bill)
# Bob's share
bobs_share = list(bob._objects.values())[0]
bobs_share
# Alice's share
alices_share = list(alice._objects.values())[0]
alices_share
# Bill's share
bills_share = list(bill._objects.values())[0]
bills_share<jupyter_output><empty_output><jupyter_text>If we want, we fit decrypt these values if we use the SAME approach we don talk about before now!!!<jupyter_code>Q = x.child.field

(bobs_share + alices_share + bills_share) % Q<jupyter_output><empty_output><jupyter_text>Look am wella, as we call `.share()`  he just split value into 3 shares ans he come send one share go each parties!  Encrypted ArithmeticWe fit perform arithmetic on the underlying values! API dey constructed on to sey make we perform arithmetic liek the one wey we do with normal PyTorch tensors.<jupyter_code>x = torch.tensor([25]).share(bob,alice)
y = torch.tensor([5]).share(bob,alice)
z = x + y
z.get()
z = x - y
z.get()<jupyter_output><empty_output><jupyter_text>Encrypted MultiplicationFor multiplication we go need an additional party who go dey responsible to dey consistently generate random numbers (he no go collude with any other parties). We dey call this person "crypto provider". For all intensive purposes, the crypto provider na just additional VirtualWorker, but he go dey important to acknowledge sey crypto provider no be "owner" onto sey he/she no get him own shares but na someone wey we need to trust make he no go collude with ani existing shareholders.<jupyter_code>crypto_provider = sy.VirtualWorker(hook, id="crypto_provider")
x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)
# multiplication

z = x * y
z.get()<jupyter_output><empty_output><jupyter_text>You fit do matrix multiplication<jupyter_code>x = torch.tensor([[1, 2],[3,4]]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([[2, 0],[0,2]]).share(bob,alice, crypto_provider=crypto_provider)
# matrix multiplication

z = x.mm(y)
z.get()<jupyter_output><empty_output><jupyter_text>Encrypted comparisonHe dey possible to private compare private values. We go rely on SecureNN protocol, we fit find the details [here](https://eprint.iacr.org/2018/442.pdf). The result of the comparison na private shared tensor.<jupyter_code>x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)
z = x > y
z.get()
z = x <= y
z.get()
z = x == y
z.get()
z = x == y + 20
z.get()<jupyter_output><empty_output><jupyter_text>You fit perform max operations<jupyter_code>x = torch.tensor([2, 3, 4, 1]).share(bob,alice, crypto_provider=crypto_provider)
x.max().get()
x = torch.tensor([[2, 3], [4, 1]]).share(bob,alice, crypto_provider=crypto_provider)
max_values, max_ids = x.max(dim=0)
max_values.get()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09754326515793295, 'fraction_numerical': 0.011012949292024688, 'mean_word_length': 5.260606060606061, 'pattern_counts': {'":': 0, '<': 58, '<?xml version=': 0, '>': 60, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '27937', 'license': 'Apache-2.0', 'n_tokens_mistral': 2676, 'n_tokens_neox': 2529, 'n_words': 1008, 'path': 'examples/tutorials/translations/Pidgin/Part 09 - Intro to Encrypted Programs-Pidgin.ipynb', 'repo_name': 'NicoSerranoP/PySyft', 'size': '8290'}