def eval(self, feed_dict=None):
        """
        Evaluates all contained tensors using a *feed_dict* and returns the ensemble value. The keys
        of *feed_dict* must be tensor ensembles. Its values can be batches, i.e., numpy arrays, or
        lists or tuples of batches. In the latter case, these lists or tuples must have the same
        length as the list of stored tensors as they will be mapped.
        """
        # first, check that the length of all feed_dict keys match our own length
        for tensor_ensemble in feed_dict:
            if len(tensor_ensemble.tensors) != len(self.tensors):
                raise EnsembleMismatchException("incompatible lengths of tensors: %d, %d" \
                                                % (len(self.tensors), len(tensor_ensemble.tensors)))

        # create a joined uuid
        _uuid = uuid4()

        # prepare feed_dicts
        feed_dicts = [{} for _ in range(len(self.tensors))]
        for tensor_ensemble, value in feed_dict.items():
            for i, tensor in enumerate(tensor_ensemble.tensors):
                if tensor is not None:
                    feed_dicts[i][tensor] = value[i] if isinstance(value, (list, tuple)) else value

        # eval all tensors
        values = [t.eval(feed_dict=d, _uuid=_uuid) for t, d in zip(self.tensors, feed_dicts)]

        # return the computed ensemble value
        return self.func(values)
Evaluates all contained tensors using a *feed_dict* and returns the ensemble value. The keys
        of *feed_dict* must be tensor ensembles. Its values can be batches, i.e., numpy arrays, or
        lists or tuples of batches. In the latter case, these lists or tuples must have the same
        length as the list of stored tensors as they will be mapped.