{ "cells": [ { "cell_type": "raw", "id": "b8c496d8", "metadata": { "raw_mimetype": "text/restructuredtext" }, "source": [ ".. _bit-decomposition:\n", "\n", "Bit Decomposition\n", "=================\n", "\n", "Here we demonstrate Cicada's ability to convert a secret shared value into a set of secret shared bits that comprise that value. Note that this operation is costly in terms of both communication and time.\n", "\n", "The result will have one more dimension than the input secret: for example, a scalar (zero-dimensional) input will yield a vector (one-dimensional) output, a vector input will return a matrix output, and-so-on. The size of the additional dimension will equal the number of bits used to store the encoded secret, which for :any:`AdditiveProtocolSuite` defaults to 64. The returned bit values are stored using the additional dimension, in big-endian order so that the most-significant bit is located at index 0 and-so-on. \n", "\n", "In the following example we initialize :any:`AdditiveProtocolSuite` with a custom field order (251) and an encoding with fewer bits of precision (4) so that the encoded representation requires just 8 bits, for readability. Note too that we use the :any:`Bits` encoding when we reveal the results, since the bits are represented by the field values :math:`0` and :math:`1`, and the default :any:`FixedPoint` encoding would produce unexpected results." ] }, { "cell_type": "code", "execution_count": 1, "id": "302db7e7", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:root:Player 0 secret: 3\n", "INFO:root:Player 1 decomposition shape: (8,)\n", "INFO:root:Player 2 decomposition: [0 0 1 1 0 0 0 0]\n" ] } ], "source": [ "import logging\n", "\n", "import numpy\n", "\n", "from cicada.additive import AdditiveProtocolSuite\n", "from cicada.communicator import SocketCommunicator\n", "from cicada.encoding import FixedPoint, Bits\n", "from cicada.logger import Logger\n", "\n", "logging.basicConfig(level=logging.INFO)\n", "\n", "def main(communicator):\n", " log = Logger(logging.getLogger(), communicator)\n", " protocol = AdditiveProtocolSuite(communicator, order=251, encoding=FixedPoint(precision=4))\n", "\n", " secret = numpy.array(3) if communicator.rank == 0 else None\n", " log.info(f\"Player {communicator.rank} secret: {secret}\", src=0)\n", "\n", " secret_share = protocol.share(src=0, secret=secret, shape=())\n", " \n", " decomposition_share = protocol.bit_decompose(secret_share)\n", " decomposition = protocol.reveal(decomposition_share, encoding=Bits())\n", " log.info(f\"Player {communicator.rank} decomposition shape: {decomposition.shape}\", src=1)\n", " log.info(f\"Player {communicator.rank} decomposition: {decomposition}\", src=2)\n", "\n", "SocketCommunicator.run(world_size=3, fn=main);" ] }, { "cell_type": "markdown", "id": "91ca0742", "metadata": {}, "source": [ "Note that the decomposition contains 8 bits with the integer value \"3\" stored in the first four as $0 0 1 1$, and the fractional value \".0\" stored in the last four as $0 0 0 0$.\n", "\n", "Let's re-run the computation with the fractional value $3.5$:" ] }, { "cell_type": "code", "execution_count": 2, "id": "070cb63e", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "INFO:root:Player 0 secret: 3.5\n", "INFO:root:Player 1 decomposition shape: (8,)\n", "INFO:root:Player 2 decomposition: [0 0 1 1 1 0 0 0]\n" ] } ], "source": [ "def main(communicator):\n", " log = Logger(logging.getLogger(), communicator)\n", " protocol = AdditiveProtocolSuite(communicator, order=251, encoding=FixedPoint(precision=4))\n", "\n", " secret = numpy.array(3.5) if communicator.rank == 0 else None\n", " log.info(f\"Player {communicator.rank} secret: {secret}\", src=0)\n", "\n", " secret_share = protocol.share(src=0, secret=secret, shape=())\n", " \n", " decomposition_share = protocol.bit_decompose(secret_share)\n", " decomposition = protocol.reveal(decomposition_share, encoding=Bits())\n", " log.info(f\"Player {communicator.rank} decomposition shape: {decomposition.shape}\", src=1)\n", " log.info(f\"Player {communicator.rank} decomposition: {decomposition}\", src=2)\n", "\n", "SocketCommunicator.run(world_size=3, fn=main);" ] }, { "cell_type": "markdown", "id": "b2fa8479", "metadata": {}, "source": [ "Now we see that the integer portion of our value is still stored in the first four bits as $0011$ while the fractional portion \"0.5\" is stored in the last four as $1000$." ] } ], "metadata": { "celltoolbar": "Raw Cell Format", "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.2" } }, "nbformat": 4, "nbformat_minor": 5 }