Repository: OpenMined/PySyft

Issue:
Benchmark sigmoid function approximations methods
## What?
Benchmark the ```sigmoid``` function from the ```FPT``` (Fixed Precision Tensor) using the different approximations implemented: ```exp```, ```chebyshev```, ```maclaurin```

The final output should be a graph or graphs (png images) where:
* X-axis - the used precision represents
* Y-axis - the time to compute the approximation

We can have multiple lines, each one representing an approximation method.

Some more possibilities:
* Y-axis (or Y2-Axis) - delta (difference) between the values - our method vs the one implemented in ```torch```
* X-axis - number of iterations used for the Chebyshev or the number of terms

See the [Epic](https://github.com/OpenMined/PySyft/issues/3997) to check more details regarding where to place the graphs.


Discussion:
Hi @gmuraru I would like to work in this!
Done :D

Resolving patch:
diff --git a/benchmarks/frameworks/torch/mpc/scripts/benchmark_sample_data.py b/benchmarks/frameworks/torch/mpc/scripts/benchmark_sample_data.py
new file mode 100644
--- /dev/null
+++ b/benchmarks/frameworks/torch/mpc/scripts/benchmark_sample_data.py
@@ -0,0 +1,4 @@
+# this is sample data for benchmark of sigmoid function
+
+# data format ('method_name', precision value)
+benchmark_data_sigmoid = [("chebyshev", 4), ("maclaurin", 4), ("exp", 4)]
diff --git a/benchmarks/frameworks/torch/mpc/scripts/benchmark_sigmoid.py b/benchmarks/frameworks/torch/mpc/scripts/benchmark_sigmoid.py
new file mode 100644
--- /dev/null
+++ b/benchmarks/frameworks/torch/mpc/scripts/benchmark_sigmoid.py
@@ -0,0 +1,100 @@
+import torch
+import timeit
+import matplotlib.pyplot as plt
+
+from benchmarks.frameworks.torch.mpc.scripts.workers_initialization import workers, hook
+from benchmarks.frameworks.torch.mpc.scripts.benchmark_sample_data import benchmark_data_sigmoid
+
+
+def benchmark_sigmoid(method, prec_frac, workers):
+    """
+    This function approximates the sigmoid function using a given method.
+
+    Args:
+        method (str): the name of the method for approximation
+        prec_frac (int): precision value
+        workers (dict): workers used for sharing data
+
+    Returns:
+        diff (int): the difference between the syft and torch approximated value
+
+    """
+    alice, bob, james = workers["alice"], workers["bob"], workers["james"]
+
+    t = torch.tensor([1.23212])
+    t_sh = t.fix_precision(precision_fractional=prec_frac).share(alice, bob, crypto_provider=james)
+    r_sh = t_sh.sigmoid(method=method)
+    r = r_sh.get().float_prec()
+    t = t.sigmoid()
+    # Calculation of the difference between FPT and normal sigmoid (error)
+    diff = (r - t).abs().max()
+    return diff.item()
+
+
+def sigmoid_approximation_plot(benchmark_data_sigmoid):
+    """
+    This function plots the graph for various sigmoidal approximation benchmarks namely
+    'chebyshev', 'maclaurin', 'exp'.
+
+    Args:
+        benchmark_data_sigmoid (list): the sample data to approximate
+
+    Returns:
+        sigmoid_function_approximations_benchmark (png): plotted graph in graph directory
+    """
+
+    # initializing workers
+    worker = workers(hook())
+
+    # initializing graph plot
+    fig, ax1 = plt.subplots()
+    ax2 = ax1.twinx()
+
+    # list for handling graph data
+    x_data = []
+    y_data = []
+    y2_data = []
+
+    for data in benchmark_data_sigmoid:
+
+        # getting value from benchmark_data_sigmoid
+        method, prec_frac = data
+
+        for precision_value in range(1, (prec_frac + 1)):
+
+            # temporary list for calculating average execution time and error
+            temp_time_taken = []
+            temp_error = []
+
+            for i in range(10):
+                start_time = timeit.default_timer()
+                error = benchmark_sigmoid(method, precision_value, worker)
+                time_taken = timeit.default_timer() - start_time
+                temp_time_taken.append(time_taken)
+                temp_error.append(error)
+
+            final_time_taken = sum(temp_time_taken) / len(temp_time_taken)
+            final_time_taken *= 1000
+            final_error = sum(temp_error) / len(temp_error)
+            x_data.append(precision_value)
+            y_data.append(final_time_taken)
+            y2_data.append(final_error)
+
+        ax1.plot(x_data, y_data, label=method, linestyle="-")
+        ax2.plot(x_data, y2_data, label=method, linestyle="--")
+        x_data.clear()
+        y_data.clear()
+        y2_data.clear()
+
+    # plotting of the data
+    ax1.set_xlabel("Precision Value")
+    ax1.set_ylabel("Execution Time (ms)")
+    ax2.set_ylabel("Error")
+    ax1.legend(bbox_to_anchor=(1, 1.3), loc="upper right", title="Method", fontsize="small")
+    ax2.legend(bbox_to_anchor=(0, 1.3), loc="upper left", title="Error", fontsize="small")
+    plt.tight_layout()
+    plt.savefig("../graphs/sigmoid_function_approximations_benchmark.png")
+
+
+# calling sigmoid_approximation_plot function
+sigmoid_approximation_plot(benchmark_data_sigmoid)
diff --git a/benchmarks/frameworks/torch/mpc/scripts/workers_initialization.py b/benchmarks/frameworks/torch/mpc/scripts/workers_initialization.py
new file mode 100644
--- /dev/null
+++ b/benchmarks/frameworks/torch/mpc/scripts/workers_initialization.py
@@ -0,0 +1,35 @@
+import torch
+import syft
+from syft import TorchHook
+from syft.generic.frameworks.hook import hook_args
+
+
+def hook():
+    hook = TorchHook(torch)
+    return hook
+
+
+def workers(hook):
+    """
+    This function defines virtual workers to be used in benchmarking functions.
+    """
+
+    # Reset the hook and the local worker
+    syft.local_worker.clear_objects()
+    hook_args.hook_method_args_functions = {}
+    hook_args.hook_method_response_functions = {}
+    hook_args.register_response_functions = {}
+    hook_args.get_tensor_type_functions = {}
+
+    # Define virtual workers
+    alice = syft.VirtualWorker(id="alice", hook=hook, is_client_worker=False)
+    bob = syft.VirtualWorker(id="bob", hook=hook, is_client_worker=False)
+    james = syft.VirtualWorker(id="james", hook=hook, is_client_worker=False)
+
+    workers = {
+        "me": hook.local_worker,
+        "alice": alice,
+        "bob": bob,
+        "james": james,
+    }
+    return workers


Test patch: