mirror of
https://github.com/zama-ai/concrete.git
synced 2026-02-09 03:55:04 -05:00
docs: update doc to the new API
This commit is contained in:
@@ -25,7 +25,7 @@ inputset = [(0, 0), (0, 1), (1, 0), (1, 1), (2, 0), (2, 1), (3, 0), (3, 1)]
|
||||
circuit = compiler.compile_on_inputset(inputset)
|
||||
|
||||
# Make homomorphic inference
|
||||
circuit.run(1, 0)
|
||||
circuit.encrypt_run_decrypt(1, 0)
|
||||
```
|
||||
|
||||
## Overview of the numpy compilation process
|
||||
|
||||
@@ -78,17 +78,17 @@ Here is the graph from the previous code block drawn with `draw_graph`:
|
||||
|
||||
## Performing homomorphic evaluation
|
||||
|
||||
You can use `.run(...)` method of `FHECircuit` returned by `hnp.compile_numpy_function(...)` to perform fully homomorphic evaluation. Here are some examples:
|
||||
You can use `.encrypt_run_decrypt(...)` method of `FHECircuit` returned by `hnp.compile_numpy_function(...)` to perform fully homomorphic evaluation. Here are some examples:
|
||||
|
||||
<!--pytest-codeblocks:cont-->
|
||||
```python
|
||||
circuit.run(3, 4)
|
||||
circuit.encrypt_run_decrypt(3, 4)
|
||||
# 7
|
||||
circuit.run(1, 2)
|
||||
circuit.encrypt_run_decrypt(1, 2)
|
||||
# 3
|
||||
circuit.run(7, 7)
|
||||
circuit.encrypt_run_decrypt(7, 7)
|
||||
# 14
|
||||
circuit.run(0, 0)
|
||||
circuit.encrypt_run_decrypt(0, 0)
|
||||
# 0
|
||||
```
|
||||
|
||||
@@ -97,11 +97,20 @@ Be careful about the inputs, though.
|
||||
If you were to run with values outside the range of the inputset, the result might not be correct.
|
||||
```
|
||||
|
||||
Today, we cannot simulate a client / server API in python, but it is for very soon. Then, we will have:
|
||||
- a `keygen` API, which is used to generate both public and private keys
|
||||
- an `encrypt` API, which happens on the user's device, and is using private keys
|
||||
- a `run_inference` API, which happens on the untrusted server and only uses public material
|
||||
- a `encrypt` API, which happens on the user's device to get final clear result, and is using private keys
|
||||
While `.encrypt_run_decrypt(...)` is a good start for prototyping examples, more advanced usages require control over the different steps that are happening behind the scene, mainly key generation, encryption, execution, and decryption. The different steps can of course be called separately as in the example below:
|
||||
|
||||
<!--pytest-codeblocks:cont-->
|
||||
```python
|
||||
# generate keys required for encrypted computation
|
||||
circuit.keygen()
|
||||
# this will encrypt arguments that require encryption and pack all arguments
|
||||
# as well as public materials (public keys)
|
||||
public_args = circuit.encrypt(3, 4)
|
||||
# this will run the encrypted computation using public materials and inputs provided
|
||||
encrypted_result = circuit.run(public_args)
|
||||
# the execution returns the encrypted result which can later be decrypted
|
||||
decrypted_result = circuit.decrypt(encrypted_result)
|
||||
```
|
||||
|
||||
## Further reading
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ circuit = compiler.compile_on_inputset(inputset)
|
||||
test_input = np.array([4, 2, 6], dtype=np.uint8)
|
||||
expected_output = 2
|
||||
|
||||
assert np.array_equal(circuit.run(test_input), expected_output)
|
||||
assert np.array_equal(circuit.encrypt_run_decrypt(test_input), expected_output)
|
||||
```
|
||||
|
||||
You can use negative indexing.
|
||||
@@ -43,7 +43,7 @@ circuit = compiler.compile_on_inputset(inputset)
|
||||
test_input = np.array([4, 2, 6], dtype=np.uint8)
|
||||
expected_output = 6
|
||||
|
||||
assert np.array_equal(circuit.run(test_input), expected_output)
|
||||
assert np.array_equal(circuit.encrypt_run_decrypt(test_input), expected_output)
|
||||
```
|
||||
|
||||
You can use multidimensional indexing as well.
|
||||
@@ -63,7 +63,7 @@ circuit = compiler.compile_on_inputset(inputset)
|
||||
test_input = np.array([[4, 2], [1, 5], [7, 6]], dtype=np.uint8)
|
||||
expected_output = 6
|
||||
|
||||
assert np.array_equal(circuit.run(test_input), expected_output)
|
||||
assert np.array_equal(circuit.encrypt_run_decrypt(test_input), expected_output)
|
||||
```
|
||||
|
||||
### Extracting a slice
|
||||
@@ -83,7 +83,7 @@ circuit = compiler.compile_on_inputset(inputset)
|
||||
test_input = np.array([4, 2, 6, 1, 7], dtype=np.uint8)
|
||||
expected_output = np.array([2, 6, 1], dtype=np.uint8)
|
||||
|
||||
assert np.array_equal(circuit.run(test_input), expected_output)
|
||||
assert np.array_equal(circuit.encrypt_run_decrypt(test_input), expected_output)
|
||||
```
|
||||
|
||||
You can use multidimensional slicing as well.
|
||||
|
||||
@@ -23,10 +23,10 @@ results in
|
||||
|
||||
<!--pytest-codeblocks:skip-->
|
||||
```python
|
||||
circuit.run(0) == 2
|
||||
circuit.run(1) == 1
|
||||
circuit.run(2) == 3
|
||||
circuit.run(3) == 0
|
||||
circuit.encrypt_run_decrypt(0) == 2
|
||||
circuit.encrypt_run_decrypt(1) == 1
|
||||
circuit.encrypt_run_decrypt(2) == 3
|
||||
circuit.encrypt_run_decrypt(3) == 0
|
||||
```
|
||||
|
||||
Moreover, direct lookup tables can be used with tensors where the same table lookup is applied to each value in the tensor, so
|
||||
@@ -38,7 +38,7 @@ results in
|
||||
<!--pytest-codeblocks:skip-->
|
||||
```python
|
||||
input = np.array([[0, 1, 3], [2, 3, 1]], dtype=np.uint8)
|
||||
circuit.run(input) == [[2, 1, 0], [3, 0, 1]]
|
||||
circuit.encrypt_run_decrypt(input) == [[2, 1, 0], [3, 0, 1]]
|
||||
```
|
||||
|
||||
## Direct Multi Table Lookup
|
||||
@@ -71,7 +71,7 @@ results in
|
||||
<!--pytest-codeblocks:skip-->
|
||||
```python
|
||||
input = np.array([[2, 3], [1, 2], [3, 0]], dtype=np.uint8)
|
||||
circuit.run(input) == [[4, 27], [1, 8], [9, 0]]
|
||||
circuit.encrypt_run_decrypt(input) == [[4, 27], [1, 8], [9, 0]]
|
||||
```
|
||||
|
||||
Basically, we applied `squared` table to the first column and `cubed` to the second one.
|
||||
@@ -96,14 +96,14 @@ results in
|
||||
|
||||
<!--pytest-codeblocks:skip-->
|
||||
```python
|
||||
circuit.run(0) == 77
|
||||
circuit.run(1) == 35
|
||||
circuit.run(2) == 32
|
||||
circuit.run(3) == 70
|
||||
circuit.run(4) == 115
|
||||
circuit.run(5) == 125
|
||||
circuit.run(6) == 91
|
||||
circuit.run(7) == 45
|
||||
circuit.encrypt_run_decrypt(0) == 77
|
||||
circuit.encrypt_run_decrypt(1) == 35
|
||||
circuit.encrypt_run_decrypt(2) == 32
|
||||
circuit.encrypt_run_decrypt(3) == 70
|
||||
circuit.encrypt_run_decrypt(4) == 115
|
||||
circuit.encrypt_run_decrypt(5) == 125
|
||||
circuit.encrypt_run_decrypt(6) == 91
|
||||
circuit.encrypt_run_decrypt(7) == 45
|
||||
```
|
||||
|
||||
Initially, the function is converted to this operation graph
|
||||
|
||||
@@ -540,7 +540,7 @@
|
||||
" \n",
|
||||
" # We setup an example tensor that will be encrypted and passed on to the current operation\n",
|
||||
" sample = np.random.randint(3, 11, size=(3, 2), dtype=np.uint8)\n",
|
||||
" result = circuit.run(sample)\n",
|
||||
" result = circuit.encrypt_run_decrypt(sample)\n",
|
||||
" \n",
|
||||
" print(\"#######################################################################################\")\n",
|
||||
" print()\n",
|
||||
|
||||
@@ -15,11 +15,11 @@ def f(x):
|
||||
compiler = hnp.NPFHECompiler(f, {"x": "encrypted"})
|
||||
circuit = compiler.compile_on_inputset(range(64))
|
||||
|
||||
print(circuit.run(3) == f(3))
|
||||
print(circuit.run(0) == f(0))
|
||||
print(circuit.run(1) == f(1))
|
||||
print(circuit.run(10) == f(10))
|
||||
print(circuit.run(60) == f(60))
|
||||
print(circuit.encrypt_run_decrypt(3) == f(3))
|
||||
print(circuit.encrypt_run_decrypt(0) == f(0))
|
||||
print(circuit.encrypt_run_decrypt(1) == f(1))
|
||||
print(circuit.encrypt_run_decrypt(10) == f(10))
|
||||
print(circuit.encrypt_run_decrypt(60) == f(60))
|
||||
|
||||
print("All good!")
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user