Slicing
Recall from the Tutorial that in Cicada, secrets are always arrays that could have zero, one, or many dimensions. Consequently secret shares are also multidimensional arrays, supporting similar array manipulation functions. Chief among these is slicing, which returns a secret share referencing a subset of the original array.
For example, you could compute the sum of a range of values from a 1D array:
[1]:
import logging
import numpy
from cicada.active import ActiveProtocolSuite
from cicada.communicator import SocketCommunicator
from cicada.logger import Logger
logging.basicConfig(level=logging.INFO)
def main(communicator):
log = Logger(logging.getLogger(), communicator)
protocol = ActiveProtocolSuite(communicator, threshold=2)
secret = numpy.arange(10) if communicator.rank == 0 else None
share = protocol.share(src=0, secret=secret, shape=(10,))
# Sum every value in the array.
log.info(f"Player {communicator.rank} sum: {protocol.reveal(protocol.sum(share))}")
# Sum the 2nd, 3rd, and 4th values in the array.
log.info(f"Player {communicator.rank} subset sum: {protocol.reveal(protocol.sum(share[2:5]))}")
SocketCommunicator.run(world_size=3, fn=main, show_traceback=True)
INFO:root:Player 0 sum: 45.0
INFO:root:Player 1 sum: 45.0
INFO:root:Player 2 sum: 45.0
INFO:root:Player 0 subset sum: 9.0
INFO:root:Player 1 subset sum: 9.0
INFO:root:Player 2 subset sum: 9.0
[1]:
[None, None, None]
In fact Cicada supports all of the indexing operations supported by Indexing on ndarrays. For example, you could sum just the odd-numbered elements in an array:
[2]:
def main(communicator):
log = Logger(logging.getLogger(), communicator)
protocol = ActiveProtocolSuite(communicator, threshold=2)
secret = numpy.arange(10) if communicator.rank == 0 else None
share = protocol.share(src=0, secret=secret, shape=(10,))
# Sum every value in the array.
log.info(f"Player {communicator.rank} sum: {protocol.reveal(protocol.sum(share))}")
# Sum the odd numbered elements in the array.
log.info(f"Player {communicator.rank} subset sum: {protocol.reveal(protocol.sum(share[1::2]))}")
SocketCommunicator.run(world_size=3, fn=main, show_traceback=True)
INFO:root:Player 0 sum: 45.0
INFO:root:Player 1 sum: 45.0
INFO:root:Player 2 sum: 45.0
INFO:root:Player 0 subset sum: 25.0
INFO:root:Player 1 subset sum: 25.0
INFO:root:Player 2 subset sum: 25.0
[2]:
[None, None, None]