(self, can_shrink=True, max_refinement=1,
                        safety_factor=1., parallel_iterations=1)
Compute and update the safe set.

        Parameters
        ----------
        can_shrink : bool, optional
            A boolean determining whether previously safe states other than the
            initial safe set must be verified again (i.e., can the safe set
            shrink in volume?)
        max_refinement : int, optional
            The maximum integer divisor used for adaptive discretization.
        safety_factor : float, optional
            A multiplicative factor greater than 1 used to conservatively
            estimate the required adaptive discretization.
        parallel_iterations : int, optional
            The number of parallel iterations to use for safety verification in
            the adaptive case. Passed to `tf.map_fn`.
Compute and update the safe set.
Compute and update the safe set .
def update_safe_set(self, can_shrink=True, max_refinement=1,
                        safety_factor=1., parallel_iterations=1):
        """Compute and update the safe set.

        Parameters
        ----------
        can_shrink : bool, optional
            A boolean determining whether previously safe states other than the
            initial safe set must be verified again (i.e., can the safe set
            shrink in volume?)
        max_refinement : int, optional
            The maximum integer divisor used for adaptive discretization.
        safety_factor : float, optional
            A multiplicative factor greater than 1 used to conservatively
            estimate the required adaptive discretization.
        parallel_iterations : int, optional
            The number of parallel iterations to use for safety verification in
            the adaptive case. Passed to `tf.map_fn`.

        """
        safety_factor = np.maximum(safety_factor, 1.)
        storage = get_storage(self._storage)

        if storage is None:
            # Placeholder for states to evaluate for safety
            tf_states = tf.placeholder(config.dtype,
                                       shape=[None, self.discretization.ndim],
                                       name='verification_states')
            actions = self.policy(tf_states)
            next_states = self.dynamics(tf_states, actions)

            decrease = self.v_decrease_bound(tf_states, next_states)
            threshold = self.threshold(tf_states, self.tau)
            tf_negative = tf.squeeze(tf.less(decrease, threshold), axis=1)

            storage = [('states', tf_states), ('negative', tf_negative)]

            if self.adaptive:
                # Compute an integer n such that dv < threshold for tau / n
                ratio = safety_factor * threshold / decrease
                # If dv = 0, check for nan values, and clip to n = 0
                tf_n_req = tf.where(tf.is_nan(ratio),
                                    tf.zeros_like(ratio), ratio)
                # Edge case: ratio = 1 should correspond to n = 2
                # TODO
                # If dv < 0, also clip to n = 0
                tf_n_req = tf.ceil(tf.maximum(tf_n_req, 0))

                dim = int(self.discretization.ndim)
                lengths = self.discretization.unit_maxes.reshape((-1, 1))

                def refined_safety_check(data):
                    """Verify decrease condition in a locally refined grid."""
                    center = tf.reshape(data[:-1], [1, dim])
                    n_req = tf.cast(data[-1], tf.int32)

                    start = tf.constant(-1., dtype=config.dtype)
                    spacing = tf.reshape(tf.linspace(start, 1., n_req),
                                         [1, -1])
                    border = (0.5 * (1 - 1 / n_req) * lengths *
                              tf.tile(spacing, [dim, 1]))
                    mesh = tf.meshgrid(*tf.unstack(border), indexing='ij')
                    points = tf.stack([tf.reshape(col, [-1]) for col in mesh],
                                      axis=1)
                    points += center

                    refined_threshold = self.threshold(center,
                                                       self.tau / n_req)
                    negative = tf.less(decrease, refined_threshold)
                    refined_negative = tf.reduce_all(negative)
                    return refined_negative

                tf_refinement = tf.placeholder(tf.int32, [None, 1],
                                               'refinement')
                data = tf.concat([tf_states, tf.cast(tf_refinement,
                                                     config.dtype)], axis=1)
                tf_refined_negative = tf.map_fn(refined_safety_check, data,
                                                tf.bool, parallel_iterations)
                storage += [('n_req', tf_n_req), ('refinement', tf_refinement),
                            ('refined_negative', tf_refined_negative)]

            set_storage(self._storage, storage)
        else:
            if self.adaptive:
                (tf_states, tf_negative, tf_n_req, tf_refinement,
                 tf_refined_negative) = storage.values()
            else:
                tf_states, tf_negative = storage.values()

        # Get relevant properties
        feed_dict = self.feed_dict

        if can_shrink:
            # Reset the safe set and adaptive discretization
            safe_set = np.zeros_like(self.safe_set, dtype=bool)
            refinement = np.zeros_like(self._refinement, dtype=int)
            if self.initial_safe_set is not None:
                safe_set[self.initial_safe_set] = True
                refinement[self.initial_safe_set] = 1
        else:
            # Assume safe set cannot shrink
            safe_set = self.safe_set
            refinement = self._refinement

        value_order = np.argsort(self.values)
        safe_set = safe_set[value_order]
        refinement = refinement[value_order]

        # Verify safety in batches
        batch_size = config.gp_batch_size
        batch_generator = batchify((value_order, safe_set, refinement),
                                   batch_size)
        index_to_state = self.discretization.index_to_state

        #######################################################################

        for i, (indices, safe_batch, refine_batch) in batch_generator:
            states = index_to_state(indices)
            feed_dict[tf_states] = states

            # Update the safety with the safe_batch result
            negative = tf_negative.eval(feed_dict)
            safe_batch |= negative
            refine_batch[negative] = 1

            # Boolean array: argmin returns first element that is False
            # If all are safe then it returns 0
            bound = np.argmin(safe_batch)
            refine_bound = 0

            # Check if there are unsafe elements in the batch
            if bound > 0 or not safe_batch[0]:
                if self.adaptive and max_refinement > 1:
                    # Compute required adaptive refinement
                    feed_dict[tf_states] = states[bound:]
                    refine_batch[bound:] = tf_n_req.eval(feed_dict).ravel()

                    # We do not need to refine cells that correspond to known
                    # safe states
                    idx_safe = np.logical_or(negative,
                                             self.initial_safe_set[indices])
                    refine_batch[idx_safe] = 1

                    # Identify cells to refine
                    states_to_check = np.logical_and(refine_batch >= 1,
                                                     refine_batch <=
                                                     max_refinement)
                    states_to_check = states_to_check[bound:]

                    if np.all(states_to_check):
                        stop = len(states_to_check)
                    else:
                        stop = np.argmin(states_to_check)

                    if stop > 0:
                        feed_dict[tf_states] = states[bound:bound + stop]
                        feed_dict[tf_refinement] = refine_batch[bound:
                                                                bound + stop,
                                                                None]
                        refined_safe = tf_refined_negative.eval(feed_dict)

                        # Determine which states are safe under the refined
                        # discretization
                        if np.all(refined_safe):
                            refine_bound = len(refined_safe)
                        else:
                            refine_bound = np.argmin(refined_safe)
                        safe_batch[bound:bound + refine_bound] = True

                    # Break if the refined discretization does not work for all
                    # states after `bound`
                    if stop < len(states_to_check) or refine_bound < stop:
                        safe_batch[bound + refine_bound:] = False
                        refine_batch[bound + refine_bound:] = 0
                        break
                else:
                    # Make sure all following points are labeled as unsafe
                    safe_batch[bound:] = False
                    refine_batch[bound:] = 0
                    break

        # The largest index of a safe value
        max_index = i + bound + refine_bound - 1

        #######################################################################

        # Set placeholder for c_max to the corresponding value
        feed_dict[self.c_max] = self.values[value_order[max_index]]

        # Restore the order of the safe set and adaptive refinement
        safe_nodes = value_order[safe_set]
        self.safe_set[:] = False
        self.safe_set[safe_nodes] = True
        self._refinement[value_order] = refinement

        # Ensure the initial safe set is kept
        if self.initial_safe_set is not None:
            self.safe_set[self.initial_safe_set] = True
            self._refinement[self.initial_safe_set] = 1
def update_safe_set ( self , can_shrink = True , max_refinement = 1 , safety_factor = 1. , parallel_iterations = 1 ) : safety_factor = np . maximum ( safety_factor , 1. ) storage = get_storage ( self . _storage ) if storage is None : # Placeholder for states to evaluate for safety tf_states = tf . placeholder ( config . dtype , shape = [ None , self . discretization . ndim ] , name = 'verification_states' ) actions = self . policy ( tf_states ) next_states = self . dynamics ( tf_states , actions ) decrease = self . v_decrease_bound ( tf_states , next_states ) threshold = self . threshold ( tf_states , self . tau ) tf_negative = tf . squeeze ( tf . less ( decrease , threshold ) , axis = 1 ) storage = [ ( 'states' , tf_states ) , ( 'negative' , tf_negative ) ] if self . adaptive : # Compute an integer n such that dv < threshold for tau / n ratio = safety_factor * threshold / decrease # If dv = 0, check for nan values, and clip to n = 0 tf_n_req = tf . where ( tf . is_nan ( ratio ) , tf . zeros_like ( ratio ) , ratio ) # Edge case: ratio = 1 should correspond to n = 2 # TODO # If dv < 0, also clip to n = 0 tf_n_req = tf . ceil ( tf . maximum ( tf_n_req , 0 ) ) dim = int ( self . discretization . ndim ) lengths = self . discretization . unit_maxes . reshape ( ( - 1 , 1 ) ) def refined_safety_check ( data ) : """Verify decrease condition in a locally refined grid.""" center = tf . reshape ( data [ : - 1 ] , [ 1 , dim ] ) n_req = tf . cast ( data [ - 1 ] , tf . int32 ) start = tf . constant ( - 1. , dtype = config . dtype ) spacing = tf . reshape ( tf . linspace ( start , 1. , n_req ) , [ 1 , - 1 ] ) border = ( 0.5 * ( 1 - 1 / n_req ) * lengths * tf . tile ( spacing , [ dim , 1 ] ) ) mesh = tf . meshgrid ( * tf . unstack ( border ) , indexing = 'ij' ) points = tf . stack ( [ tf . reshape ( col , [ - 1 ] ) for col in mesh ] , axis = 1 ) points += center refined_threshold = self . threshold ( center , self . tau / n_req ) negative = tf . less ( decrease , refined_threshold ) refined_negative = tf . reduce_all ( negative ) return refined_negative tf_refinement = tf . placeholder ( tf . int32 , [ None , 1 ] , 'refinement' ) data = tf . concat ( [ tf_states , tf . cast ( tf_refinement , config . dtype ) ] , axis = 1 ) tf_refined_negative = tf . map_fn ( refined_safety_check , data , tf . bool , parallel_iterations ) storage += [ ( 'n_req' , tf_n_req ) , ( 'refinement' , tf_refinement ) , ( 'refined_negative' , tf_refined_negative ) ] set_storage ( self . _storage , storage ) else : if self . adaptive : ( tf_states , tf_negative , tf_n_req , tf_refinement , tf_refined_negative ) = storage . values ( ) else : tf_states , tf_negative = storage . values ( ) # Get relevant properties feed_dict = self . feed_dict if can_shrink : # Reset the safe set and adaptive discretization safe_set = np . zeros_like ( self . safe_set , dtype = bool ) refinement = np . zeros_like ( self . _refinement , dtype = int ) if self . initial_safe_set is not None : safe_set [ self . initial_safe_set ] = True refinement [ self . initial_safe_set ] = 1 else : # Assume safe set cannot shrink safe_set = self . safe_set refinement = self . _refinement value_order = np . argsort ( self . values ) safe_set = safe_set [ value_order ] refinement = refinement [ value_order ] # Verify safety in batches batch_size = config . gp_batch_size batch_generator = batchify ( ( value_order , safe_set , refinement ) , batch_size ) index_to_state = self . discretization . index_to_state ####################################################################### for i , ( indices , safe_batch , refine_batch ) in batch_generator : states = index_to_state ( indices ) feed_dict [ tf_states ] = states # Update the safety with the safe_batch result negative = tf_negative . eval ( feed_dict ) safe_batch |= negative refine_batch [ negative ] = 1 # Boolean array: argmin returns first element that is False # If all are safe then it returns 0 bound = np . argmin ( safe_batch ) refine_bound = 0 # Check if there are unsafe elements in the batch if bound > 0 or not safe_batch [ 0 ] : if self . adaptive and max_refinement > 1 : # Compute required adaptive refinement feed_dict [ tf_states ] = states [ bound : ] refine_batch [ bound : ] = tf_n_req . eval ( feed_dict ) . ravel ( ) # We do not need to refine cells that correspond to known # safe states idx_safe = np . logical_or ( negative , self . initial_safe_set [ indices ] ) refine_batch [ idx_safe ] = 1 # Identify cells to refine states_to_check = np . logical_and ( refine_batch >= 1 , refine_batch <= max_refinement ) states_to_check = states_to_check [ bound : ] if np . all ( states_to_check ) : stop = len ( states_to_check ) else : stop = np . argmin ( states_to_check ) if stop > 0 : feed_dict [ tf_states ] = states [ bound : bound + stop ] feed_dict [ tf_refinement ] = refine_batch [ bound : bound + stop , None ] refined_safe = tf_refined_negative . eval ( feed_dict ) # Determine which states are safe under the refined # discretization if np . all ( refined_safe ) : refine_bound = len ( refined_safe ) else : refine_bound = np . argmin ( refined_safe ) safe_batch [ bound : bound + refine_bound ] = True # Break if the refined discretization does not work for all # states after `bound` if stop < len ( states_to_check ) or refine_bound < stop : safe_batch [ bound + refine_bound : ] = False refine_batch [ bound + refine_bound : ] = 0 break else : # Make sure all following points are labeled as unsafe safe_batch [ bound : ] = False refine_batch [ bound : ] = 0 break # The largest index of a safe value max_index = i + bound + refine_bound - 1 ####################################################################### # Set placeholder for c_max to the corresponding value feed_dict [ self . c_max ] = self . values [ value_order [ max_index ] ] # Restore the order of the safe set and adaptive refinement safe_nodes = value_order [ safe_set ] self . safe_set [ : ] = False self . safe_set [ safe_nodes ] = True self . _refinement [ value_order ] = refinement # Ensure the initial safe set is kept if self . initial_safe_set is not None : self . safe_set [ self . initial_safe_set ] = True self . _refinement [ self . initial_safe_set ] = 1