def _compute_almost_sure_reachability(self):
        objective = AS_REACH
        # removed stores reloads that had been removed from the MDP
        removed = set()

        # Initialize the helper values
        self.reach_safe = [inf] * self.states

        # Initialized safe values after reaching T
        self.get_safe()
        safe_after_T = lambda s: self.safe_values[
            s] if s in self.targets else inf

        done = False
        while not done:
            ### 2.1.1.
            # safe_after_T initializes each iteration of the fixpoint with:
            #  * self.safe_values[t] for targets (reach done, just survive)
            #  * ∞ for the rest

            self._sufficient_levels(self.reach_safe, removed, safe_after_T)

            ### 2.1.2. Initialize PosReach_M:
            #  * safe_value for target states
            #  * inf otherwise
            self.alsure_values = [inf] * self.states
            for t in self.targets:
                self.alsure_values[t] = self.get_safe()[t]

            self._init_strategy(objective)

            ### 2.1.3 Compute PosReach on sub-MDP
            # Mitigate reload removal (use Safe_M for survival)
            rem_survival_val = lambda s: self.reach_safe[s]
            rem_action_value = lambda a, v: self._action_value_T(
                a, v, rem_survival_val)

            # Avoid unnecesarry computations
            is_removed = lambda x: x in removed  # always ∞
            is_target = lambda x: x in self.targets  # always Safe
            skip_cond = lambda x: is_removed(x) or is_target(x)

            ## Finish the fixpoint
            largest_fixpoint(self.mdp,
                             self.alsure_values,
                             rem_action_value,
                             value_adj=self._reload_capper,
                             skip_state=skip_cond,
                             on_update=self._update_function(objective))

            ### 2.2. & 2.3. Detect bad reloads and remove them
            done = True
            # Iterate over reloads and remove unusable ones (∞)
            for s in range(self.states):
                if self.is_reload(s) and self.alsure_values[s] == inf:
                    if s not in removed:
                        removed.add(s)
                        done = False

            self._copy_strategy(SAFE, objective, self.targets)
    def _compute_buchi(self):
        objective = BUCHI

        # removed stores reloads that had been removed from the MDP
        removed = set()

        # Initialize the helper values
        self.buchi_safe = [inf] * self.states

        done = False
        while not done:
            ### 1.1. Compute Safe(M\removed) and store it in buchi_safe
            self._sufficient_levels(self.buchi_safe, removed, objective=HELPER)

            ### 1.2. Initialization of PosReach_M
            #  * buchi_safe for target states
            #  * inf otherwise
            self.buchi_values = [inf] * self.states
            for t in self.targets:
                self.buchi_values[t] = self.buchi_safe[t]

            self._init_strategy(objective)

            ### 1.3. Computation of PosReach on sub-MDP (with removed reloads)
            ## how much do I need to survive via this state after reloads removal
            # Use Safe_m (stored in buchi_safe) as value needed for survival
            rem_survival_val = lambda s: self.buchi_safe[s]
            # Navigate towards T and survive with Safe_m
            rem_action_value = lambda a, v: self._action_value_T(
                a, v, rem_survival_val)

            ## Avoid unnecessary computations
            is_removed = lambda x: x in removed  # always ∞
            is_target = lambda x: x in self.targets  # always Safe_M
            skip_cond = lambda x: is_removed(x) or is_target(x)

            ## Finish the fixpoint
            largest_fixpoint(self.mdp,
                             self.buchi_values,
                             rem_action_value,
                             value_adj=self._reload_capper,
                             skip_state=skip_cond,
                             on_update=self._update_function(objective))

            ### 2. & 3. Detect bad reloads and remove them
            done = True
            # Iterate over reloads and remove unusable ones (∞)
            for s in range(self.states):
                if self.is_reload(s) and self.buchi_values[s] == inf:
                    if s not in removed:
                        removed.add(s)
                        done = False

            self._copy_strategy(HELPER, objective, self.targets)
    def _compute_minInitCons(self):
        objective = MIN_INIT_CONS
        self._init_strategy(objective)

        self.mic_values = [inf] * self.states
        cap = lambda s, v: inf if v > self.cap else v
        largest_fixpoint(self.mdp,
                         self.mic_values,
                         self._action_value,
                         value_adj=cap,
                         on_update=self._update_function(objective))
    def _compute_positive_reachability(self):
        objective = POS_REACH
        self._init_strategy(objective)

        # Initialize:
        #  * safe_value for target states
        #  * inf otherwise
        self.get_safe()
        self.pos_reach_values = [inf] * self.states

        for t in self.targets:
            self.pos_reach_values[t] = self.safe_values[t]

        largest_fixpoint(
            self.mdp,
            self.pos_reach_values,
            self._action_value_T,
            value_adj=self._reload_capper,
            # Target states are always safe_values[t]
            skip_state=lambda x: x in self.targets,
            on_update=self._update_function(objective))

        self._copy_strategy(SAFE, objective, self.targets)
    def _sufficient_levels(self,
                           values=None,
                           removed=None,
                           init_val=lambda s: inf,
                           objective=HELPER):
        """Compute the safe_values using the largest-fixpoint method
        based on minInitCons computation with removal of reload states
        that have minInitCons() = ∞ in the previous itertions.

        The first computation computes, in fact, minInitCons (redundantly)

        The worst-case complexity is |R| * minInitCons = |R|*|S|^2

        `values`  : list used for computation
                    self.safe_values as default
        `removed` : set of reloads - start with the reloads already removed
                    ∅ by default
        `init_val`: state -> value - defines the values at the start of each
                    iteration of inner fixpoint (currently needed only for)
                    almost-sure reachability. It simulates switching between
                    the MDP with 2 sets of reload states (one before, one
                    [the original set R] after reaching T). 
        """
        if values is None:
            values = self.safe_values

        if removed is None:
            removed = set()

        done = False
        while not done:
            # Compute fixpoint without removed reloads
            self._init_strategy(objective)

            # Reset the computation, by default set all values to ∞.
            # `init_val: s -> v
            for s in range(self.states):
                values[s] = init_val(s)

            # Mitigate reload removal
            zero_cond = lambda x: self.is_reload(x) and x not in removed
            rem_action_value = lambda a, v: self._action_value(a, v, zero_cond)

            # Removed reloads are skipped
            skip_cond = lambda x: x in removed  # Improve performance only
            # Over capacity values -> ∞
            cap = lambda s, v: inf if v > self.cap else v

            largest_fixpoint(self.mdp,
                             values,
                             rem_action_value,
                             value_adj=cap,
                             skip_state=skip_cond,
                             on_update=self._update_function(objective))

            done = True
            # Iterate over reloads and remove unusable ones (∞)
            for s in range(self.states):
                if self.is_reload(s) and values[s] == inf:
                    if s not in removed:
                        removed.add(s)
                        done = False

        # Set reload values to 0, "< & +1"-trick to handle ∞
        for s in range(self.states):
            if self.mdp.is_reload(s) and values[s] < self.cap + 1:
                values[s] = 0