def process_contribution()

in simulation/decai/simulation/contract/incentive/prediction_market.py [0:0]


    def process_contribution(self):
        """
        Reward Phase:
        Process the next data contribution.
        """
        assert self.remaining_bounty_rounds > 0, "The market has ended."

        if self.state == MarketPhase.REWARD_RESTART:
            self._next_data_index = 0
            self._logger.debug("Remaining bounty rounds: %s", self.remaining_bounty_rounds)
            self._scores = defaultdict(float)

            if self._reset_model_during_reward_phase:
                # The paper implies that we should not retrain the model and instead only train once.
                # The problem there is that a contributor is affected by bad contributions
                # between them and the last counted contribution after bad contributions are filtered out.
                self.model.reset_model()

            if self.prev_acc is None:
                # XXX This evaluation can be expensive and likely won't work in Ethereum.
                # We need to find a more efficient way to do this or let a contributor proved they did it.
                self.prev_acc = self.model.evaluate(self.test_data, self.test_labels)
                self.original_acc = self.prev_acc
                self._logger.debug("Accuracy: %0.2f%%", self.prev_acc * 100)
            elif not self._reset_model_during_reward_phase:
                # When calculating rewards, the score, the same accuracy for the initial model should be used.
                self.prev_acc = self.original_acc

            self._num_market_contributions: Dict[Address, int] = Counter()
            self._worst_contribution: Optional[_Contribution] = None
            self._worst_contributor: Optional[Address] = None
            self._min_score = math.inf
            self.state = MarketPhase.REWARD
        else:
            assert self.state == MarketPhase.REWARD

        contribution = self._market_data[self._next_data_index]
        self._num_market_contributions[contribution.contributor_address] += 1
        self.model.update(contribution.data, contribution.classification)
        if not self._reset_model_during_reward_phase and contribution.accuracy is None:
            # XXX Potentially expensive gas cost.
            contribution.accuracy = self.model.evaluate(self.test_data, self.test_labels)

        self._next_data_index += 1
        iterated_through_all_contributions = self._next_data_index >= self.get_num_contributions_in_market()

        if iterated_through_all_contributions \
                or not self._group_contributions \
                or self._market_data[self._next_data_index].contributor_address != contribution.contributor_address:
            # Need to compute score.

            if self._reset_model_during_reward_phase:
                # XXX Potentially expensive gas cost.
                acc = self.model.evaluate(self.test_data, self.test_labels)
            else:
                acc = contribution.accuracy

            score_change = acc - self.prev_acc
            if self._group_contributions:
                new_score = self._scores[contribution.contributor_address] = \
                    self._scores[contribution.contributor_address] + score_change
            else:
                new_score = contribution.score = score_change

            if new_score < self._min_score:
                self._min_score = new_score
                if self._group_contributions:
                    self._worst_contributor = contribution.contributor_address
                else:
                    self._worst_contribution = contribution
            elif self._group_contributions and self._worst_contributor == contribution.contributor_address:
                # Their score increased, they might not be the worst anymore.
                # Optimize: use a heap.
                self._worst_contributor, self._min_score = min(self._scores.items(), key=lambda x: x[1])

            self.prev_acc = acc
            if iterated_through_all_contributions:
                # Find min score and remove that address from the list.
                self._logger.debug("Minimum score: %.2f", self._min_score)
                if self._min_score < 0:
                    if self._group_contributions:
                        num_rounds = self._market_balances[self._worst_contributor] / -self._min_score
                    else:
                        num_rounds = self._worst_contribution.balance / -self._min_score

                    if num_rounds > self.remaining_bounty_rounds:
                        num_rounds = self.remaining_bounty_rounds

                    self._logger.debug("Will simulate %.2f rounds.", num_rounds)

                    self.remaining_bounty_rounds -= num_rounds
                    if self.remaining_bounty_rounds == 0:
                        self._end_reward_phase(num_rounds)
                    else:
                        if self._group_contributions:
                            participants_to_remove = set()
                            for participant, score in self._scores.items():
                                self._logger.debug("Score for \"%s\": %.2f", participant, score)
                                self._market_balances[participant] += score * num_rounds
                                if self._market_balances[participant] < self._num_market_contributions[participant]:
                                    # They don't have enough left to stake next time.
                                    participants_to_remove.add(participant)
                            self._market_data: List[_Contribution] = list(
                                filter(lambda c: c.contributor_address not in participants_to_remove,
                                       self._market_data))
                        else:
                            for contribution in self._market_data:
                                contribution.balance += contribution.score * num_rounds
                                if contribution.balance < 1:
                                    # Contribution is going to get kicked out.
                                    self._market_balances[contribution.contributor_address] += contribution.balance
                            self._market_data: List[_Contribution] = \
                                list(filter(lambda c: c.balance >= 1, self._market_data))
                        if self.get_num_contributions_in_market() == 0:
                            self.state = MarketPhase.REWARD_COLLECT
                            self.remaining_bounty_rounds = 0
                            self.reward_phase_end_time_s = self._time()
                        else:
                            self.state = MarketPhase.REWARD_RESTART
                else:
                    num_rounds = self.remaining_bounty_rounds
                    self.remaining_bounty_rounds = 0
                    self._end_reward_phase(num_rounds)