Optimizers

Optimizer is an interface that enables the use of declearn 's optimizers for Federated Learning inside Fed-BioMed

Classes

BaseOptimizer

BaseOptimizer(model, optimizer)

Bases: Generic[OT]

Abstract base class for Optimizer and Model wrappers.

Parameters:

Name Type Description Default
model Model

model to train, interfaced via a framework-specific Model.

required
optimizer OT

optimizer that will be used for optimizing the model.

required

Raises:

Type Description
FedbiomedOptimizerError

Raised if model is not an instance of _model_cls (which may be a subset of the generic Model type).

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def __init__(self, model: Model, optimizer: OT):
    """Constuctor of the optimizer wrapper that sets a reference to model and optimizer.

    Args:
        model: model to train, interfaced via a framework-specific Model.
        optimizer: optimizer that will be used for optimizing the model.

    Raises:
        FedbiomedOptimizerError:
            Raised if model is not an instance of `_model_cls` (which may
            be a subset of the generic Model type).
    """
    if not isinstance(model, self._model_cls):
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB626.value}, in `model` argument, expected an instance "
            f"of {self._model_cls} but got an object of type {type(model)}."
        )
    self._model: Model = model
    self.optimizer: OT = optimizer

Attributes

optimizer instance-attribute
optimizer = optimizer

Functions

count_nb_auxvar
count_nb_auxvar()

Counts number of auxiliary variables needed for the given optimizer

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def count_nb_auxvar(self) -> int:
    """Counts number of auxiliary variables needed for the given optimizer"""
    return 0
init_training
init_training()

Sets up training and misceallenous parameters so the model is ready for training

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def init_training(self):
    """Sets up training and misceallenous parameters so the model is ready for training
    """
    self._model.init_training()
load_state
load_state(optim_state, load_from_state=False)

Reconfigures optimizer from a given state.

This is the default method for optimizers that don't support state. Does nothing.

Parameters:

Name Type Description Default
optim_state Dict

not used

required
load_from_state optional

not used

False

Returns:

Type Description
Union[BaseOptimizer, None]

None

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def load_state(self, optim_state: Dict, load_from_state: bool = False) -> Union['BaseOptimizer', None]:
    """Reconfigures optimizer from a given state.

    This is the default method for optimizers that don't support state. Does nothing.

    Args:
        optim_state: not used
        load_from_state (optional): not used

    Returns:
        None
    """
    logger.warning("load_state method of optimizer not implemented, cannot load optimizer status")
    return None
save_state
save_state()

Gets optimizer state.

This is the default method for optimizers that don't support state. Does nothing.

Returns:

Type Description
Union[Dict, None]

None

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def save_state(self) -> Union[Dict, None]:
    """Gets optimizer state.

    This is the default method for optimizers that don't support state. Does nothing.

    Returns:
        None
    """
    logger.warning("save_state method of optimizer not implemented, cannot save optimizer status")
    return None
send_to_device
send_to_device(device, idx=None)

GPU support

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def send_to_device(self, device: str, idx: Optional[int] = None):
    """GPU support"""
step abstractmethod
step()

Performs an optimisation step and updates model weights.

Source code in fedbiomed/common/optimizers/generic_optimizers.py
@abstractmethod
def step(self):
    """Performs an optimisation step and updates model weights.
    """

DeclearnOptimizer

DeclearnOptimizer(model, optimizer)

Bases: BaseOptimizer

Base Optimizer subclass to use a declearn-backed Optimizer.

Parameters:

Name Type Description Default
model Model

Model that wraps the actual model

required
optimizer Union[Optimizer, Optimizer]

declearn optimizer, or fedbiomed optimizer (that wraps declearn optimizer)

required
Source code in fedbiomed/common/optimizers/generic_optimizers.py
def __init__(self, model: Model, optimizer: Union[FedOptimizer, declearn.optimizer.Optimizer]):
    """Constructor of Optimizer wrapper for declearn's optimizers

    Args:
        model: Model that wraps the actual model
        optimizer: declearn optimizer,
            or fedbiomed optimizer (that wraps declearn optimizer)
    """
    logger.debug("Using declearn optimizer")
    if isinstance(optimizer, declearn.optimizer.Optimizer):
        # convert declearn optimizer into a fedbiomed optimizer wrapper
        optimizer = FedOptimizer.from_declearn_optimizer(optimizer)
    elif not isinstance(optimizer, FedOptimizer):
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB626.value}: expected a declearn optimizer,"
            f" but got an object with type {type(optimizer)}."
        )
    super().__init__(model, optimizer)

Attributes

optimizer class-attribute instance-attribute
optimizer = None

Functions

count_nb_auxvar
count_nb_auxvar()
Source code in fedbiomed/common/optimizers/generic_optimizers.py
def count_nb_auxvar(self) -> int:
    return len(self.optimizer.get_aux_names())
get_aux
get_aux()
Source code in fedbiomed/common/optimizers/generic_optimizers.py
def get_aux(self) -> Optional[Dict[str, AuxVar]]:
    aux = self.optimizer.get_aux()
    return aux
init_training
init_training()
Source code in fedbiomed/common/optimizers/generic_optimizers.py
def init_training(self):
    super().init_training()
    self.optimizer.init_round()
load_state
load_state(optim_state, load_from_state=False)

Reconfigures optimizer from a given state (contained in optim_state argument). Usage:

>>> import torch.nn as nn
>>> from fedbiomed.common.optimizers import Optimizer
>>> from fedbiomed.common.models import TorchModel
>>> model = TorchModel(nn.Linear(4, 2))
>>> optimizer = Optimizer(lr=.1)
>>> optim = DeclearnOptimizer(model, optimizer)

>>> optim.load_state(state)  # provided state contains the state one wants to load the optimizer with
If load_from_state argument is True, it completes the current optimizer state with optim_state argument

>>> import torch.nn as nn
>>> from fedbiomed.common.optimizers import Optimizer
>>> from fedbiomed.common.optimizers.declearn import MomentumModule, AdamModule
>>> from fedbiomed.common.models import TorchModel
>>> model = TorchModel(nn.Linear(4, 2))
>>> optimizer = Optimizer(lr=.1, modules=[MomentumModule(), AdamModule()])
>>> optim_1 = DeclearnOptimizer(model, optimizer)

>>> optimizer = Optimizer(lr=.1, modules=[AdamModule(), MomentumModule()])
>>> optim_2 = DeclearnOptimizer(model, optimizer)
>>> optim_2.load_state(optim_1.save_state())
>>> optim_2.save_state()['states']
{'modules': [('momentum', {'velocity': 0.0}),
        ('adam',
        {'steps': 0,
            'vmax': None,
            'momentum': {'state': 0.0},
            'velocity': {'state': 0.0}})]}
Modules of DeclearnOptimizer will be reloaded provided that Module is the same and occupying the same index. Eg if the state contains following modules: modules=[AdamModule(), AdagradModule(), MomemtumModule()] And the Optimizer contained in the TrainingPlan has the following modules: modules=[AdamModule(), MomemtumModule()] Then only AdamModule module will be reloaded, MomentumModule will be set with default argument (they don't share the same index in the modules list).

Parameters:

Name Type Description Default
optim_state Dict[str, Any]

state of the Optimizer to be loaded. It will change the current state of the optimizer with the one loaded

required
load_from_state optional

strategy for loading states: whether to load from saved states (True) or from breakpoint (False). If set to True, loading is done partially in the sense that if some of the OptimModules is different in the optim_state and the original state of the optimizer, it loads only the OptiModule(s) from the latest state that both state has in common. Defaults to False.

False

Raises:

Type Description
FedbiomedOptimizerError

raised if state is not of dict type.

Returns:

Type Description
DeclearnOptimizer

Optimizer wrapper reloaded from optim_state argument.

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def load_state(self, optim_state: Dict[str, Any], load_from_state: bool = False) -> 'DeclearnOptimizer':
    """Reconfigures optimizer from a given state (contained in `optim_state` argument).
    Usage:
    ```python
    >>> import torch.nn as nn
    >>> from fedbiomed.common.optimizers import Optimizer
    >>> from fedbiomed.common.models import TorchModel
    >>> model = TorchModel(nn.Linear(4, 2))
    >>> optimizer = Optimizer(lr=.1)
    >>> optim = DeclearnOptimizer(model, optimizer)

    >>> optim.load_state(state)  # provided state contains the state one wants to load the optimizer with
    ```
    If `load_from_state` argument is True, it completes the current optimizer state with `optim_state` argument

    ```python
    >>> import torch.nn as nn
    >>> from fedbiomed.common.optimizers import Optimizer
    >>> from fedbiomed.common.optimizers.declearn import MomentumModule, AdamModule
    >>> from fedbiomed.common.models import TorchModel
    >>> model = TorchModel(nn.Linear(4, 2))
    >>> optimizer = Optimizer(lr=.1, modules=[MomentumModule(), AdamModule()])
    >>> optim_1 = DeclearnOptimizer(model, optimizer)

    >>> optimizer = Optimizer(lr=.1, modules=[AdamModule(), MomentumModule()])
    >>> optim_2 = DeclearnOptimizer(model, optimizer)
    >>> optim_2.load_state(optim_1.save_state())
    >>> optim_2.save_state()['states']
    {'modules': [('momentum', {'velocity': 0.0}),
            ('adam',
            {'steps': 0,
                'vmax': None,
                'momentum': {'state': 0.0},
                'velocity': {'state': 0.0}})]}
    ```
    Modules of DeclearnOptimizer will be reloaded provided that Module is the same and occupying the same index.
    Eg if the state contains following modules:
    ```modules=[AdamModule(), AdagradModule(), MomemtumModule()]```
     And the Optimizer contained in the TrainingPlan has the following modules:
    ```modules=[AdamModule(), MomemtumModule()]```
    Then only `AdamModule` module will be reloaded, `MomentumModule` will be set with default argument (they don't
    share the same index in the modules list).

    Args:
        optim_state: state of the Optimizer to be loaded. It will change the current state of the optimizer
            with the one loaded
        load_from_state (optional): strategy for loading states: whether to load from saved states (True) or
            from breakpoint (False).
            If set to True, loading is done partially in the sense that if some of the OptimModules is different in
            the optim_state and the original state of the optimizer, it loads only the OptiModule(s) from the
            latest state that both state has in common. Defaults to False.

    Raises:
        FedbiomedOptimizerError: raised if state is not of dict type.

    Returns:
        Optimizer wrapper reloaded from `optim_state` argument.
    """
    # state: breakpoint content for optimizer
    if not isinstance(optim_state, Dict):
        raise FedbiomedOptimizerError(f"{ErrorNumbers.FB626.value}, incorrect type of argument `optim_state`: "
                                      f"expecting a dict, but got {type(optim_state)}")

    if load_from_state:
        # first get Optimizer detailed in the TrainingPlan.

        init_optim_state = self.optimizer.get_state()  # we have to get states since it is the only way we can
        # gather modules (other methods of `Optimizer are private`)

        optim_state_copy = copy.deepcopy(optim_state)
        optim_state.update(init_optim_state)  # optim_state will be updated with current optimizer state
        # check if opimizer state has changed from last optimizer to the current one
        # if it has changed, find common modules and update common states
        for component in ( 'modules', 'regularizers',):
            components_to_keep: List[Tuple[str, int]] = []  # we store here common Module between current Optimizer
            # and the ones in the `optim_state` tuple (common Module name, index in List)

            if not init_optim_state['states'].get(component) or not optim_state_copy['states'].get(component):
                continue
            self._collect_common_optimodules(
                init_optim_state,
                optim_state_copy,
                component,
                components_to_keep
            )

            for mod in components_to_keep:
                for mod_state in optim_state_copy['states'][component]:
                    if mod[0] == mod_state[0]:
                        # if we do find same module in the current optimizer than the previous one,
                        # we load the previous optimizer module state into the current one
                        optim_state['states'][component][mod[1]] = mod_state

        logger.info("Loading optimizer state from saved state")

    reloaded_optim = FedOptimizer.load_state(optim_state)
    self.optimizer = reloaded_optim

    return self
optimizer_processing
optimizer_processing()

Provides a context manager able to do some actions before and after setting up an Optimizer, mainly disabling scikit-learn internal optimizer.

Also, checks if model_args dictionary contains training parameters that won't be used or have any effect on the training, because of disabling the scikit-learn optimizer ( such as initial learning rate, learnig rate scheduler, ...). If disabling the internal optimizer leads to such changes, displays a warning.

Returns:

Name Type Description
SklearnOptimizerProcessing SklearnOptimizerProcessing

context manager providing extra logic

Usage:

    >>> dlo = DeclearnSklearnOptimizer(model, optimizer)
    >>> with dlo.optimizer_processing():
            model.train(inputs,targets)

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def optimizer_processing(self) -> SklearnOptimizerProcessing:
    """Provides a context manager able to do some actions before and after setting up an Optimizer, mainly
    disabling scikit-learn internal optimizer.

    Also, checks if `model_args` dictionary contains training parameters that
    won't be used or have any effect on the training, because of disabling the scikit-learn optimizer (
    such as initial learning rate, learnig rate scheduler, ...). If disabling the internal optimizer leads
    to such changes, displays a warning.

    Returns:
        SklearnOptimizerProcessing: context manager providing extra logic

    Usage:
    ```python
        >>> dlo = DeclearnSklearnOptimizer(model, optimizer)
        >>> with dlo.optimizer_processing():
                model.train(inputs,targets)
    ```
    """
    if isinstance(self._model, SkLearnModel):
        return SklearnOptimizerProcessing(self._model, disable_internal_optimizer=True)
    else:
        raise FedbiomedOptimizerError(f"{ErrorNumbers.FB626.value}: Method optimizer_processing should be used "
                                      f"only with SkLearnModel, but model is {self._model}")
save_state
save_state()

Gets optimizer state.

Returns:

Type Description
Dict

optimizer state

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def save_state(self) -> Dict:
    """Gets optimizer state.

    Returns:
        optimizer state
    """
    optim_state = self.optimizer.get_state()
    return optim_state
send_to_device
send_to_device(device, idx=None)
Source code in fedbiomed/common/optimizers/generic_optimizers.py
def send_to_device(self, device: str, idx: int | None = None):
    self.optimizer.send_to_device(device, idx)
set_aux
set_aux(aux)
Source code in fedbiomed/common/optimizers/generic_optimizers.py
def set_aux(self, aux: Dict[str, AuxVar]):
    # FIXME: for imported tensors in PyTorch sent as auxiliary variables,
    # we should push it on the appropriate device (ie cpu/gpu)
    # TODO-PAUL: call the proper declearn routines
    self.optimizer.set_aux(aux)
step
step()

Performs one optimization step

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def step(self):
    """Performs one optimization step"""
    # NOTA: for sklearn, gradients retrieved are unscaled because we are using learning rate equal to 1.
    # Therefore, it is necessary to disable the sklearn internal optimizer beforehand
    # otherwise, computation will be incorrect
    grad = declearn.model.api.Vector.build(self._model.get_gradients())
    weights = declearn.model.api.Vector.build(self._model.get_weights(
        only_trainable=False,
        exclude_buffers=True
    ))
    updates = self.optimizer.step(grad, weights)
    self._model.apply_updates(updates.coefs)
zero_grad
zero_grad()

Zeroes gradients of the Pytorch model. Basically calls the zero_grad method of the model.

Raises:

Type Description
FedbiomedOptimizerError

triggered if model has no method called zero_grad

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def zero_grad(self):
    """Zeroes gradients of the Pytorch model. Basically calls the `zero_grad`
    method of the model.

    Raises:
        FedbiomedOptimizerError: triggered if model has no method called `zero_grad`
    """
    # warning: specific for pytorch
    if not isinstance(self._model, TorchModel):
        raise FedbiomedOptimizerError(f"{ErrorNumbers.FB626.value}. This method can only be used for TorchModel, "
                                      f"but got {self._model}")
    self._model.model.zero_grad()

EncryptedAuxVar

EncryptedAuxVar(encrypted, enc_specs, cleartext, clear_cls)

Container for encrypted optimizer auxiliary variables.

This ad hoc data structure is designed to enable performing secure aggregation over auxiliary variables of declearn-backed optimizers.

It is designed to be used in four steps:

  • Encrypt the outputs of a declearn optimizer's collect_aux_var call, using flatten_auxvar_for_secagg and a SecaggCrypter, then wrap the results into an EncryptedAuxVar
  • Convert the EncryptedAuxVar to and from a serializable dict, enabling to transmit it across network communications (from nodes to researcher).
  • Aggregate node-wise encrypted values by summing nodes' EncryptedAuxVar instances (or calling directly their aggregate method).
  • Decrypt the resulting instance's encrypted values with a SecaggCrypter and use unflatten_auxvar_after_secagg on the decrypted values and the rest of the instance's attributes to recover auxiliary variables that can be passed to the researcher's optimizer's process_aux_var method.

Parameters:

Name Type Description Default
encrypted List[List[int]]

List of node-wise flattened, encrypted values.

required
enc_specs List[ValueSpec]

List of module-wise specifications describing the flattened values' initial names, types and shapes.

required
cleartext List[Optional[Dict[str, Any]]]

List of module-wise optional cleartext values, that need sharing and aggregation but not encryption.

required
clear_cls List[Tuple[str, Type[AuxVar]]]

List of module-wise tuples storing module name and source AuxVar subtype.

required
Source code in fedbiomed/common/optimizers/_secagg.py
def __init__(
    self,
    encrypted: List[List[int]],
    enc_specs: List[ValueSpec],
    cleartext: List[Optional[Dict[str, Any]]],
    clear_cls: List[Tuple[str, Type[AuxVar]]],
) -> None:
    """Instantiate a container from already encrypted information.

    Args:
        encrypted: List of node-wise flattened, encrypted values.
        enc_specs: List of module-wise specifications describing the
            flattened values' initial names, types and shapes.
        cleartext: List of module-wise optional cleartext values, that
            need sharing and aggregation but not encryption.
        clear_cls: List of module-wise tuples storing module name and
            source `AuxVar` subtype.
    """
    self.encrypted = encrypted
    self.enc_specs = enc_specs
    self.cleartext = cleartext
    self.clear_cls = clear_cls

Attributes

clear_cls instance-attribute
clear_cls = clear_cls
cleartext instance-attribute
cleartext = cleartext
enc_specs instance-attribute
enc_specs = enc_specs
encrypted instance-attribute
encrypted = encrypted

Functions

concatenate
concatenate(other)

Concatenates a pair of EncryptedAuxVar into a single instance.

Parameters:

Name Type Description Default
other Self

EncryptedAuxVar instance to be aggregated with this one.

required

Returns:

Type Description
Self

EncryptedAuxVar instance resulting from the aggregation (with

Self

concatenated encrypted values, and aggregated cleartext ones).

Raises:

Type Description
TypeError

if other is not an EncryptedAuxVar instance.

ValueError

if other has distinct specs from this one.

Source code in fedbiomed/common/optimizers/_secagg.py
def concatenate(
    self,
    other: Self,
) -> Self:
    """Concatenates a pair of EncryptedAuxVar into a single instance.

    Args:
        other: `EncryptedAuxVar` instance to be aggregated with this one.

    Returns:
        `EncryptedAuxVar` instance resulting from the aggregation (with
        concatenated encrypted values, and aggregated cleartext ones).

    Raises:
        TypeError: if `other` is not an `EncryptedAuxVar` instance.
        ValueError: if `other` has distinct specs from this one.
    """
    # Raise if instances do not match.
    if not isinstance(other, self.__class__):
        raise TypeError(
            f"'{self.__class__.__name__}.aggregate' expects an input "
            f"with the same type, but received '{type(other)}'."
        )
    if self.enc_specs != other.enc_specs:
        raise ValueError(
            f"Cannot sum '{self.__class__.__name__}' instances with"
            " distinct specs for encrypted values."
        )
    if self.clear_cls != other.clear_cls:
        raise ValueError(
            f"Cannot sum '{self.__class__.__name__}' instances with"
            " distinct specs for base AuxVar classes."
        )
    # Concatenate lists of encrypted values for future sum-decryption.
    encrypted = self.encrypted + other.encrypted
    # Perform aggregation of cleartext values, using type-specific rules.
    cleartext = [
        self._aggregate_cleartext(
            aux_cls, self.cleartext[i], other.cleartext[i]
        )
        for i, (_, aux_cls) in enumerate(self.clear_cls)
    ]
    # Wrap up results in an EncryptedAuxVar instance and return it.
    return self.__class__(
        encrypted=encrypted,
        enc_specs=self.enc_specs,
        cleartext=cleartext,
        clear_cls=self.clear_cls,
    )
concatenate_from_dict classmethod
concatenate_from_dict(data)
Source code in fedbiomed/common/optimizers/_secagg.py
@classmethod
def concatenate_from_dict(cls, data: Dict[str, Self]) -> Self:
    auxvar = list(data.values())
    # this converts List[List[List[int]]] -> List[List[int]]
    obj = sum(auxvar[1:], start=auxvar[0])
    return cls(obj.encrypted, obj.enc_specs, obj.cleartext, obj.clear_cls)
from_dict classmethod
from_dict(data)

Instantiate from a dict representation.

Parameters:

Name Type Description Default
data Dict[str, Any]

Dict representation, as emitted by this class's to_dict.

required

Raises:

Type Description
TypeError

If any required key is missing or improper.

Source code in fedbiomed/common/optimizers/_secagg.py
@classmethod
def from_dict(
    cls,
    data: Dict[str, Any],
) -> Self:
    """Instantiate from a dict representation.

    Args:
        data: Dict representation, as emitted by this class's `to_dict`.

    Raises:
        TypeError: If any required key is missing or improper.
    """
    try:
        # Recover wrapped AuxVar classes from the type registry.
        clear_cls = [
            (name, access_registered(*info))
            for name, info in data["clear_cls"]
        ]
        # Ensure tuples are preserved (as serialization converts to list).
        enc_specs = [
            [tuple(value_specs) for value_specs in module_specs]
            for module_specs in data["enc_specs"]
        ]
        # Try instantiating from the input data.
        return cls(
            encrypted=data["encrypted"],
            enc_specs=enc_specs,  # type: ignore
            cleartext=data["cleartext"],
            clear_cls=clear_cls,
        )
    except Exception as exc:
        raise TypeError(
            f"Cannot instantiate '{cls.__name__}' from input dict: "
            f"raised '{repr(exc)}'."
        ) from exc
get_mapping_encrypted_aux_var
get_mapping_encrypted_aux_var()
Source code in fedbiomed/common/optimizers/_secagg.py
def get_mapping_encrypted_aux_var(self) -> Dict[str, List[int]]:
    nodes_id = list(self.cleartext[0]['clients'])
    return {n: p for n,p in zip(nodes_id, self.encrypted)}
get_num_expected_params
get_num_expected_params()

Return the number of flat values that should be decrypted.

Source code in fedbiomed/common/optimizers/_secagg.py
def get_num_expected_params(
    self,
) -> int:
    """Return the number of flat values that should be decrypted."""
    return sum(
        size
        for module_specs in self.enc_specs
        for _, size, _ in module_specs
    )
to_dict
to_dict()

Return a dict representation of this instance.

Returns:

Type Description
Dict[str, Any]

Dict representation of this instance.

Source code in fedbiomed/common/optimizers/_secagg.py
def to_dict(
    self,
) -> Dict[str, Any]:
    """Return a dict representation of this instance.

    Returns:
        Dict representation of this instance.
    """
    aux_cls_info = [
        (name, access_registration_info(aux_cls))
        for name, aux_cls in self.clear_cls
    ]
    return {
        "encrypted": self.encrypted,
        "enc_specs": self.enc_specs,
        "cleartext": self.cleartext,
        "clear_cls": aux_cls_info,
    }

NativeSkLearnOptimizer

NativeSkLearnOptimizer(model, optimizer=None)

Bases: BaseOptimizer

Optimizer wrapper for scikit-learn native models.

Parameters:

Name Type Description Default
model SkLearnModel

SkLearnModel model that builds a scikit-learn model.

required
optimizer Optional[None]

unused. Defaults to None.

None
Source code in fedbiomed/common/optimizers/generic_optimizers.py
def __init__(self, model: SkLearnModel, optimizer: Optional[None] = None):
    """Constructor of the Optimizer wrapper for scikit-learn native models.

    Args:
        model: SkLearnModel model that builds a scikit-learn model.
        optimizer: unused. Defaults to None.
    """

    if optimizer is not None:
        logger.info(f"Passed Optimizer {optimizer} won't be used (using only native scikit learn optimization)")
    super().__init__(model, None)
    logger.debug("Using native Sklearn Optimizer")

Functions

optimizer_processing
optimizer_processing()
Source code in fedbiomed/common/optimizers/generic_optimizers.py
def optimizer_processing(self) -> SklearnOptimizerProcessing:
    return SklearnOptimizerProcessing(self._model, disable_internal_optimizer=False)
step
step()

Performs an optimization step and updates model weights.

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def step(self):
    """Performs an optimization step and updates model weights."""
    gradients = self._model.get_gradients()
    updates = {k: -v for k, v in gradients.items()}
    self._model.apply_updates(updates)

NativeTorchOptimizer

NativeTorchOptimizer(model, optimizer)

Bases: BaseOptimizer

Optimizer wrapper for pytorch native optimizers and models.

Parameters:

Name Type Description Default
model TorchModel

fedbiomed model wrapper that warps the pytorch model

required
optimizer Optimizer

pytorch native optimizers (inhereting from torch.optim.Optimizer)

required

Raises:

Type Description
FedbiomedOptimizerError

raised if optimizer is not a pytorch native optimizer ie a torch.optim.Optimizer object.

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def __init__(self, model: TorchModel, optimizer: torch.optim.Optimizer):
    """Constructor of the optimizer wrapper

    Args:
        model: fedbiomed model wrapper that warps the pytorch model
        optimizer: pytorch native optimizers (inhereting from `torch.optim.Optimizer`)

    Raises:
        FedbiomedOptimizerError: raised if optimizer is not a pytorch native optimizer ie a `torch.optim.Optimizer`
            object.
    """
    if not isinstance(optimizer, torch.optim.Optimizer):
        raise FedbiomedOptimizerError(f"{ErrorNumbers.FB626.value} Expected a native pytorch `torch.optim` "
                                      f"optimizer, but got {type(optimizer)}")
    super().__init__(model, optimizer)
    logger.debug("using native torch optimizer")

Functions

get_learning_rate
get_learning_rate()

Gets learning rates from param groups in Pytorch optimizer.

For each optimizer param group, it iterates over all parameters in that parameter group and searches for the " corresponding parameter of the model by iterating over all model parameters. If it finds a correspondence, it saves the learning rate value. This function assumes that the parameters in the optimizer and the model have the same reference.

Warning

This function gathers the base learning rate applied to the model weights, including alterations due to any LR scheduler. However, it does not catch any adaptive component, e.g. due to RMSProp, Adam or such.

Returns:

Type Description
Dict[str, float]

List[float]: list of single learning rate or multiple learning rates (as many as the number of the layers contained in the model)

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def get_learning_rate(self) -> Dict[str, float]:
    """Gets learning rates from param groups in Pytorch optimizer.

    For each optimizer param group, it iterates over all parameters in that parameter group and searches for the "
    corresponding parameter of the model by iterating over all model parameters. If it finds a correspondence,
    it saves the learning rate value. This function assumes that the parameters in the optimizer and the model
    have the same reference.


    !!! warning
        This function gathers the base learning rate applied to the model weights,
        including alterations due to any LR scheduler. However, it does not catch
        any adaptive component, e.g. due to RMSProp, Adam or such.

    Returns:
        List[float]: list of single learning rate or multiple learning rates
            (as many as the number of the layers contained in the model)
    """
    logger.warning(
        "`get_learning_rate` is deprecated and will be removed in future Fed-BioMed releases",
        broadcast=True)

    mapping_lr_layer_name: Dict[str, float] = {}

    for param_group in self.optimizer.param_groups:
        for layer_params in param_group['params']:
            for layer_name, tensor in self._model.model.named_parameters():
                if layer_params is tensor:
                    mapping_lr_layer_name[layer_name] = param_group['lr']
    return mapping_lr_layer_name
step
step()

Performs an optimization step and updates model weights

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def step(self):
    """Performs an optimization step and updates model weights
    """
    self.optimizer.step()
zero_grad
zero_grad()

Zeroes gradients of the Pytorch model. Basically calls the zero_grad method of the optimizer.

Source code in fedbiomed/common/optimizers/generic_optimizers.py
def zero_grad(self):
    """Zeroes gradients of the Pytorch model. Basically calls the `zero_grad`
    method of the optimizer.
    """
    self.optimizer.zero_grad()

Optimizer

Optimizer(lr, decay=0.0, modules=None, regularizers=None)

Optimizer class with a declearn-backed modular SGD-core algorithm.

Parameters:

Name Type Description Default
lr float

Base learning rate (i.e. step size) applied to gradients-based updates upon applying them to a model's weights.

required
decay float

Optional weight decay parameter, used to parameterize a decoupled weight decay regularization term (see [1]) added to the updates right before the learning rate is applied and model weights are effectively updated.

0.0
modules Optional[Sequence[Union[OptiModule, str, Tuple[str, Dict[str, Any]]]]]

Optional list of plug-in modules implementing gradients' alteration into model weights' udpates. Modules will be applied to gradients following this list's ordering. See declearn.optimizer.modules.OptiModule for details. See Notes section below for details on the "specs" format.

None
regularizers Optional[Sequence[Union[Regularizer, str, Tuple[str, Dict[str, Any]]]]]

Optional list of plug-in loss regularizers. Regularizers will be applied to gradients following this list's order, prior to any other alteration (see modules above). See declearn.optimizer.regularizers.Regularizer for details. See Notes section below for details on the "specs" format.

None

Note

Regularizer and OptiModule to be used by this optimizer, specified using the regularizers and modules parameters, may be passed as ready-for-use instances, or be instantiated from specs, consisting either of a single string (the name attribute of the class to build) or a tuple grouping this name and a config dict (to specify some hyper-parameters).

References

[1] Loshchilov & Hutter, 2019. Decoupled Weight Decay Regularization. https://arxiv.org/abs/1711.05101

Source code in fedbiomed/common/optimizers/optimizer.py
def __init__(
    self,
    lr: float,
    decay: float = 0.0,
    modules: Optional[
        Sequence[Union[OptiModule, str, Tuple[str, Dict[str, Any]]]]
    ] = None,
    regularizers: Optional[
        Sequence[Union[Regularizer, str, Tuple[str, Dict[str, Any]]]]
    ] = None,
) -> None:
    """Instantiate the declearn-issued gradient-descent optimizer.

    Args:
        lr: Base learning rate (i.e. step size) applied to gradients-based
            updates upon applying them to a model's weights.
        decay: Optional weight decay parameter, used to parameterize a
            decoupled weight decay regularization term (see [1]) added to
            the updates right before the learning rate is applied and model
            weights are effectively updated.
        modules: Optional list of plug-in modules implementing gradients'
            alteration into model weights' udpates. Modules will be applied
            to gradients following this list's ordering.
            See `declearn.optimizer.modules.OptiModule` for details.
            See Notes section below for details on the "specs" format.
        regularizers: Optional list of plug-in loss regularizers.
            Regularizers will be applied to gradients following this list's
            order, prior to any other alteration (see `modules` above).
            See `declearn.optimizer.regularizers.Regularizer` for details.
            See Notes section below for details on the "specs" format.

    !!! info "Note"
        `Regularizer` and `OptiModule` to be used by this optimizer,
        specified using the `regularizers` and `modules` parameters,
        may be passed as ready-for-use instances, or be instantiated
        from specs, consisting either of a single string (the `name`
        attribute of the class to build) or a tuple grouping this
        name and a config dict (to specify some hyper-parameters).

    !!! info "References"
        [1] Loshchilov & Hutter, 2019.
            Decoupled Weight Decay Regularization.
            https://arxiv.org/abs/1711.05101
    """
    try:
        self._optimizer = DeclearnOptimizer(
            lrate=lr,
            w_decay=decay,
            modules=modules,
            regularizers=regularizers,
        )
    except (KeyError, TypeError) as exc:
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB621.value}: declearn Optimizer instantiation"
            f" raised the following exception: {repr(exc)}"
        ) from exc

Functions

from_declearn_optimizer classmethod
from_declearn_optimizer(declearn_optimizer)

Wrap a declearn Optimizer into a fed-biomed one.

Parameters:

Name Type Description Default
declearn_optimizer Optimizer

[declearn.optimizer.Optimizer][] instance that needs to be wrapped.

required

Returns:

Type Description
Self

Fed-BioMed Optimizer instance wrapping a copy of the input declearn optimizer.

Source code in fedbiomed/common/optimizers/optimizer.py
@classmethod
def from_declearn_optimizer(
    cls,
    declearn_optimizer: DeclearnOptimizer,
) -> Self:
    """Wrap a declearn Optimizer into a fed-biomed one.

    Args:
        declearn_optimizer: [declearn.optimizer.Optimizer][] instance that
            needs to be wrapped.

    Returns:
        Fed-BioMed `Optimizer` instance wrapping a copy of the input
            declearn optimizer.
    """
    config = declearn_optimizer.get_config()
    optim = cls(
        lr=config["lrate"],
        decay=config["w_decay"],
        modules=config["modules"],
        regularizers=config["regularizers"],
    )
    optim._optimizer.set_state(declearn_optimizer.get_state())
    return optim
get_aux
get_aux()

Return auxiliary variables that need to be shared across network.

Returns:

Type Description
Dict[str, AuxVar]

Aux-var dict that associates module.collect_aux_var() values to module.name keys for each and every module plugged in this Optimizer that has some auxiliary variables to share.

Note

"Auxiliary variables" are information that needs to be shared between the nodes and the researcher between training rounds, to synchronize some optimizer plug-ins that work by pair. Their production via this method can have internal side effects; get_aux should therefore be called sparingly.

Source code in fedbiomed/common/optimizers/optimizer.py
def get_aux(self) -> Dict[str, AuxVar]:
    """Return auxiliary variables that need to be shared across network.

    Returns:
        Aux-var dict that associates `module.collect_aux_var()` values to
            `module.name` keys for each and every module plugged in this
            Optimizer that has some auxiliary variables to share.

    !!! info "Note"
        "Auxiliary variables" are information that needs to be shared
        between the nodes and the researcher between training rounds, to
        synchronize some optimizer plug-ins that work by pair. Their
        production via this method can have internal side effects;
        `get_aux` should therefore be called sparingly.
    """
    try:
        return self._optimizer.collect_aux_var()
    except Exception as exc:
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB621.value}: error in 'get_aux': {exc}"
        ) from exc
get_aux_names
get_aux_names()

Gathers list of names of modules requiring auxiliary variables

Source code in fedbiomed/common/optimizers/optimizer.py
def get_aux_names(self) -> List[str]:
    """Gathers list of names of modules requiring auxiliary variables"""
    aux_names = []

    for module in self._optimizer.modules:
        if module.aux_name is not None:
            aux_names.append(module.aux_name)
    return aux_names
get_state
get_state()

Return the configuration and current states of this Optimizer.

This method is to be used for creating breakpoints.

Returns:

Type Description
Dict[str, Any]

State-and-config dict that may be saved as part of a breakpoint file, and used to re-create this Optimizer using the Optimizer.load_state classmethod constructor.

Source code in fedbiomed/common/optimizers/optimizer.py
def get_state(self) -> Dict[str, Any]:
    """Return the configuration and current states of this Optimizer.

    This method is to be used for creating breakpoints.

    Returns:
        State-and-config dict that may be saved as part of a breakpoint
            file, and used to re-create this Optimizer using the
            `Optimizer.load_state` classmethod constructor.
    """
    try:
        config = self._optimizer.get_config()
        states = self._optimizer.get_state()
        return {"config": config, "states": states}
    except Exception as exc:
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB621.value}: error in 'get_state': {exc}"
        ) from exc
init_round
init_round()

Trigger start-of-training-round behavior of wrapped regularizers.

Source code in fedbiomed/common/optimizers/optimizer.py
def init_round(self) -> None:
    """Trigger start-of-training-round behavior of wrapped regularizers."""
    try:
        self._optimizer.start_round()
    except Exception as exc:
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB621.value}: error in 'init_round': {exc}"
        ) from exc
load_state classmethod
load_state(state)

Instantiate an Optimizer from its breakpoint state dict.

Parameters:

Name Type Description Default
state Dict[str, Any]

state-and-config dict created using the get_state method.

required

Returns:

Type Description
Self

Optimizer instance re-created from the state dict.

Raises:

Type Description
FedbiomedOptimizerError

If the input state dict has improper keys or fails to set up a declearn Optimizer and set back its state.

Source code in fedbiomed/common/optimizers/optimizer.py
@classmethod
def load_state(cls, state: Dict[str, Any]) -> Self:
    """Instantiate an Optimizer from its breakpoint state dict.

    Args:
        state: state-and-config dict created using the `get_state` method.

    Returns:
        Optimizer instance re-created from the `state` dict.

    Raises:
        FedbiomedOptimizerError: If the input `state` dict has improper keys
            or fails to set up a declearn Optimizer and set back its state.
    """
    try:
        optim = DeclearnOptimizer.from_config(state["config"])
        optim.set_state(state["states"])
    except KeyError as exc:
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB621.value}: Missing field in the breakpoints state: {exc}"
        ) from exc
    except Exception as exc:
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB621.value}: `Optimizer.load_state`: {exc}"
        ) from exc
    return cls(
        lr=optim.lrate,
        decay=optim.w_decay,
        modules=optim.modules,
        regularizers=optim.regularizers,
    )
send_to_device
send_to_device(device, idx=None)

GPU support

Source code in fedbiomed/common/optimizers/optimizer.py
def send_to_device(self, device: Union[str, bool], idx: Optional[int] = None):
    """GPU support"""
    # for now GPU support on Researcher side is disabled
    set_device_policy(device, idx)
set_aux
set_aux(aux)

Update plug-in modules based on received shared auxiliary variables.

Parameters:

Name Type Description Default
aux Dict[str, AuxVar]

Auxiliary variables received from the counterpart optimizer (on the other side of the node-researcher frontier). On the researcher side, values must have been pre-aggregated based on the ones sent by nodes.

required

Raises:

Type Description
FedbiomedOptimizerError

If a key from aux_var does not match the name of any module plugged in this optimizer (i.e. if received variables cannot be mapped to a destinatory module).

Note

"Auxiliary variables" are information that is shared between the nodes and researcher between training rounds, to synchronize some optimizer plug-ins that work by pair. The inputs to this method are not simply stored by the Optimizer, but are processed into internal side effects; this method should therefore be called sparingly.

Source code in fedbiomed/common/optimizers/optimizer.py
def set_aux(self, aux: Dict[str, AuxVar]) -> None:
    """Update plug-in modules based on received shared auxiliary variables.

    Args:
        aux: Auxiliary variables received from the counterpart optimizer
            (on the other side of the node-researcher frontier). On the
            researcher side, values must have been pre-aggregated based
            on the ones sent by nodes.

    Raises:
        FedbiomedOptimizerError: If a key from `aux_var` does not match the
            name of any module plugged in this optimizer (i.e. if received
            variables cannot be mapped to a destinatory module).

    !!! info "Note"
        "Auxiliary variables" are information that is shared between the
        nodes and researcher between training rounds, to synchronize some
        optimizer plug-ins that work by pair. The inputs to this method are
        not simply stored by the Optimizer, but are processed into internal
        side effects; this method should therefore be called sparingly.
    """
    try:
        self._optimizer.process_aux_var(aux)
    except Exception as exc:
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB621.value}: `Optimizer.set_aux`: {exc}"
        ) from exc
step
step(grads, weights)

Run an optimization step to compute and return model weight updates.

Use the pre-assigned weights and grads (set using the set_weights and set_grads methods) to compute weight updates, using the pipeline defined by this instance.

Parameters:

Name Type Description Default
grads Vector

Raw gradients based on which to compute weights updates, wrapped into a declearn Vector structure.

required
weights Vector

Current values of the weights with respect to which the gradients were computed, wrapped into a declearn Vector with the same concrete type as grads.

required

Returns:

Type Description
Vector

Updates to be applied to the model weights, computed by: - running wrapped gradients and weights through the regularizer plug-ins (that add loss-regularization terms' derivatives); - running resulting gradients through the optimodule plug-ins (that perform any defined gradient-alteration operation); - adding a decoupled weight-decay term, if one is to be used; - scaling the updates by the base learning rate. The results are wrapped into a declearn Vector structure, the concrete type of which is same as input grads and weights.

Source code in fedbiomed/common/optimizers/optimizer.py
def step(self, grads: Vector, weights: Vector) -> Vector:
    """Run an optimization step to compute and return model weight updates.

    Use the pre-assigned `weights` and `grads` (set using the `set_weights`
    and `set_grads` methods) to compute weight updates, using the pipeline
    defined by this instance.

    Args:
        grads: Raw gradients based on which to compute weights updates,
            wrapped into a declearn Vector structure.
        weights: Current values of the weights with respect to which the
            gradients were computed, wrapped into a declearn Vector with
            the same concrete type as `grads`.

    Returns:
        Updates to be applied to the model weights, computed by:
            - running wrapped gradients and weights through the regularizer
              plug-ins (that add loss-regularization terms' derivatives);
            - running resulting gradients through the optimodule plug-ins
              (that perform any defined gradient-alteration operation);
            - adding a decoupled weight-decay term, if one is to be used;
            - scaling the updates by the base learning rate.
            The results are wrapped into a declearn Vector structure, the
            concrete type of which is same as input `grads` and `weights`.
    """
    # This code mostly replicates that of
    # `declearn.optimizer.Optimizer.compute_updates_from_gradients`.
    try:
        # Add loss-regularization terms' derivatives to the raw gradients.
        for reg in self._optimizer.regularizers:
            grads = reg.run(grads, weights)
        # Iteratively refine updates by running them through the optimodules.
        for mod in self._optimizer.modules:
            grads = mod.run(grads)
        # Apply the base learning rate.
        updates = - self._optimizer.lrate * grads
        # Optionally add the decoupled weight decay term.
        if self._optimizer.w_decay:
            updates -= self._optimizer.w_decay * weights
        # Return the model updates.
        return updates
    except Exception as exc:
        raise FedbiomedOptimizerError(
            f"{ErrorNumbers.FB621.value}: error in 'step': {exc}"
        ) from exc

SklearnOptimizerProcessing

SklearnOptimizerProcessing(model, disable_internal_optimizer)

Context manager used for scikit-learn model, that checks if model parameter(s) has(ve) been changed when disabling scikit-learn internal optimizer - ie when calling disable_internal_optimizer method

Parameters:

Name Type Description Default
model SkLearnModel

a SkLearnModel that wraps a scikit-learn model

required
disable_internal_optimizer bool

whether to disable scikit-learn model internal optimizer (True) in order to apply declearn one or to keep it (False)

required
Source code in fedbiomed/common/optimizers/generic_optimizers.py
def __init__(
    self,
    model: SkLearnModel,
    disable_internal_optimizer: bool
) -> None:
    """Constructor of the object. Sets internal variables

    Args:
        model: a SkLearnModel that wraps a scikit-learn model
        disable_internal_optimizer: whether to disable scikit-learn model internal optimizer (True) in order
            to apply declearn one or to keep it (False)
    """
    self._model = model
    self._disable_internal_optimizer = disable_internal_optimizer

Functions

flatten_auxvar_for_secagg

flatten_auxvar_for_secagg(aux_var)

Flatten a node's optimizer auxiliary variables for secure aggregation.

Parameters:

Name Type Description Default
aux_var Dict[str, AuxVar]

Optimizer auxiliary variables that are meant to be encrypted, formatted as a {module_name: module_aux_var} dict.

required

Returns:

Name Type Description
cryptable List[float]

List of flattened encryptable values.

enc_specs List[ValueSpec]

List of module-wise specifications describing the flattened values' initial names, types and shapes.

cleartext List[Optional[Dict[str, Any]]]

List of module-wise optional cleartext values, that need sharing and aggregation but not encryption.

clear_cls List[Tuple[str, Type[AuxVar]]]

List of module-wise tuples storing module name and source AuxVar subtype.

Raises:

Type Description
NotImplementedError

if a module is not compatible with SecAgg.

Source code in fedbiomed/common/optimizers/_secagg.py
def flatten_auxvar_for_secagg(
    aux_var: Dict[str, AuxVar],
) -> Tuple[
    List[float],
    List[ValueSpec],
    List[Optional[Dict[str, Any]]],
    List[Tuple[str, Type[AuxVar]]],
]:
    """Flatten a node's optimizer auxiliary variables for secure aggregation.

    Args:
        aux_var: Optimizer auxiliary variables that are meant to be encrypted,
            formatted as a `{module_name: module_aux_var}` dict.

    Returns:
        cryptable: List of flattened encryptable values.
        enc_specs: List of module-wise specifications describing the
            flattened values' initial names, types and shapes.
        cleartext: List of module-wise optional cleartext values, that
            need sharing and aggregation but not encryption.
        clear_cls: List of module-wise tuples storing module name and
            source `AuxVar` subtype.

    Raises:
        NotImplementedError: if a module is not compatible with SecAgg.
    """
    # Iteratively flatten and gather specs from module-wise AuxVar objects.
    flattened = []  # type: List[float]
    enc_specs = []  # type: List[ValueSpec]
    cleartext = []  # type: List[Optional[Dict[str, Any]]]
    clear_cls = []  # type: List[Tuple[str, Type[AuxVar]]]
    for module_name, module_auxv in aux_var.items():
        flat, spec, clrt = _flatten_aux_var(module_auxv)
        flattened.extend(flat)
        enc_specs.append(spec)
        cleartext.append(clrt)
        clear_cls.append((module_name, type(module_auxv)))
    # Wrap up the results into an EncryptedAuxVar instance.
    return flattened, enc_specs, cleartext, clear_cls

unflatten_auxvar_after_secagg

unflatten_auxvar_after_secagg(decrypted, enc_specs, cleartext, clear_cls)

Unflatten secure-aggregate optimizer auxiliary variables.

Parameters:

Name Type Description Default
decrypted List[float]

List of flattened decrypted (encryptable) values.

required
enc_specs List[ValueSpec]

List of module-wise specifications describing the flattened values' initial names, types and shapes.

required
cleartext List[Optional[Dict[str, Any]]]

List of module-wise optional cleartext values, that need sharing and aggregation but not encryption.

required
clear_cls List[Tuple[str, Type[AuxVar]]]

List of module-wise tuples storing module name and source AuxVar subtype.

required

Returns:

Type Description
Dict[str, AuxVar]

Unflattened optimizer auxiliary variables, as a dict

Dict[str, AuxVar]

with format {module_name: module_aux_var}.

Raises:

Type Description
RuntimeError

if auxiliary variables unflattening fails.

Source code in fedbiomed/common/optimizers/_secagg.py
def unflatten_auxvar_after_secagg(
    decrypted: List[float],
    enc_specs: List[ValueSpec],
    cleartext: List[Optional[Dict[str, Any]]],
    clear_cls: List[Tuple[str, Type[AuxVar]]],
) -> Dict[str, AuxVar]:
    """Unflatten secure-aggregate optimizer auxiliary variables.

    Args:
        decrypted: List of flattened decrypted (encryptable) values.
        enc_specs: List of module-wise specifications describing the
            flattened values' initial names, types and shapes.
        cleartext: List of module-wise optional cleartext values, that
            need sharing and aggregation but not encryption.
        clear_cls: List of module-wise tuples storing module name and
            source `AuxVar` subtype.

    Returns:
        Unflattened optimizer auxiliary variables, as a dict
        with format `{module_name: module_aux_var}`.

    Raises:
        RuntimeError: if auxiliary variables unflattening fails.
    """
    # Iteratively rebuild AuxVar instances, then return.
    aux_var = {}  # type: Dict[str, AuxVar]
    indx = 0
    for i, (name, aux_cls) in enumerate(clear_cls):
        size = sum(size for _, size, _ in enc_specs[i])
        aux_var[name] = _unflatten_aux_var(
            aux_cls=aux_cls,
            flattened=decrypted[indx:indx+size],
            enc_specs=enc_specs[i],
            cleartext=cleartext[i],
        )
    return aux_var