Beispiel #1
0
def test_pathos_pp_callable () :
    """Test parallel processnig with pathos: ParallelPool  
    """
    logger = getLogger("ostap.test_pathos_pp_callable")         
    if not pathos :
        logger.error ( "pathos is not available" )
        return
    
    logger.info ('Test job submission with %s' %  pathos ) 
    
    if DILL_PY3_issue : 
        logger.warning ("test is disabled (DILL/ROOT/PY3 issue)" )
        return

    ## logger.warning ("test is disabled for UNKNOWN REASON")
    ## return

    from pathos.helpers import cpu_count
    ncpus = cpu_count  ()
    
    from pathos.pools import ParallelPool as Pool 

    pool = Pool ( ncpus )   
    logger.info ( "Pool is %s" %  ( type ( pool ).__name__ ) )

    pool.restart ( True ) 


    mh   = MakeHisto() 
    jobs = pool.uimap ( mh.process ,  [  ( i , n )  for  ( i , n ) in enumerate ( inputs ) ] )
    
    result = None 
    for h in progress_bar ( jobs , max_value = len ( inputs ) ) :
        if not result  : result = h
        else           : result.Add ( h )

    pool.close ()
    pool.join  ()
    pool.clear ()
    
    logger.info ( "Histogram is %s" % result.dump ( 80 , 10 )  )
    logger.info ( "Entries  %s/%s" % ( result.GetEntries() , sum ( inputs ) ) ) 
    
    with wait ( 1 ) , use_canvas ( 'test_pathos_pp_callable' ) : 
        result.draw (   ) 

    return result 
Beispiel #2
0
 def gap_stat(self, err_init, B=10):
     """
     Calcule les statistiques utiles dans le choix du nombre optimal de cluster.
     
     Paramètres d'entrée :
         err_init : matrice de la forme [nb_cluster; erreur de classification; erreur relative de classification; variance intra-classe]
         B : Nombre d'itération de k-means avec échantillons aléatoires.
         
     Paramètres de sortie :
         stat : matrice de la forme [nb_cluster; erreur de classification; erreur relative de classification; variance intra-classe;
                                     logarithme de l'erreur de classification; moyenne des log des erreurs avec echantillons aléatoires;
                                     différence entre les logs des erreurs obtenues et la moyenne des log des erreurs avec echantillons aléatoires;
                                     gap statistical | (différence des échantillons n) - (différence des échantillons n+1 * variance des log des erreurs avec echantillons aléatoires)]
     """
     pool = Pool(self.cpu)
     pool.close()
     pool.join()
     mini, maxi = np.min(self.data.data, axis=0), np.max(self.data.data,
                                                         axis=0)
     shape = self.data.data.shape
     log = np.log10(err_init[1])
     mean_alea, var_alea = [], []
     for i in range(1, self.nb_cluster + 1):
         err = []
         f = partial(self.__stat_i, mini=mini, maxi=maxi, shape=shape, i=i)
         pool.restart()
         err = list(pool.map(f, range(B)))
         pool.close()
         pool.join()
         err = np.log10(np.array(err))
         mean_alea.append(np.mean(err))
         var_alea.append(np.std(err))
     mean_alea = np.array(mean_alea)
     var_alea = np.array(var_alea) * np.sqrt(1 + (1 / float(B)))
     gap = mean_alea - log
     diff_gap = gap[0:-1] - (gap[1:] - var_alea[1:])
     diff_gap = np.hstack((diff_gap, 0))
     stat = np.vstack((err_init, log, mean_alea, gap, diff_gap))
     del pool
     return stat
Beispiel #3
0
def parmap(f,
           X,
           nprocs=multiprocessing.cpu_count(),
           chunk_size=1,
           use_tqdm=False,
           **tqdm_kwargs):

    if len(X) == 0:
        return []  # like map

    # nprocs = min(nprocs, cn.max_procs)
    if nprocs != multiprocessing.cpu_count() and len(X) < nprocs * chunk_size:
        chunk_size = 1  # use chunk_size = 1 if there is enough procs for a batch size of 1
    nprocs = int(max(1, min(nprocs, len(X) / chunk_size)))  # at least 1
    if len(X) < nprocs:
        if nprocs != multiprocessing.cpu_count():
            print("parmap too much procs")
        nprocs = len(X)  # too much procs

    if force_serial or nprocs == 1:  # we want it serial (maybe for profiling)
        return list(map(f, tqdm(X, smoothing=0, **tqdm_kwargs)))

    def _spawn_fun(input, func, c):
        import random, numpy
        random.seed(1554 + i + c)
        numpy.random.seed(42 + i + c)  # set random seeds
        try:
            res = func(input)
            res_dict = dict()
            res_dict["res"] = res
            # res_dict["functions_dict"] = function_cache2.caches_dicts
            # res_dict["experiment_purpose"] = cn2.experiment_purpose
            # res_dict["curr_params_list"] = cn2.curr_experiment_params_list
            return res_dict
        except:
            import traceback
            traceback.print_exc()
            raise  # re-raise exception

    # if chunk_size == 1:
    #     chunk_size = math.ceil(float(len(X)) / nprocs)  # all procs work on an equal chunk

    try:  # try-catch hides bugs
        global proc_count
        old_proc_count = proc_count
        proc_count = nprocs
        p = Pool(nprocs)
        p.restart(force=True)
        # can throw if current proc is daemon
        if use_tqdm:
            retval_par = tqdm(p.imap(_spawn_fun,
                                     X, [f] * len(X),
                                     range(len(X)),
                                     chunk_size=chunk_size),
                              total=len(X),
                              smoothing=0,
                              **tqdm_kwargs)
        else:
            retval_par = p.map(_spawn_fun,
                               X, [f] * len(X),
                               range(len(X)),
                               chunk_size=chunk_size)

        retval = list(map(lambda res_dict: res_dict["res"],
                          retval_par))  # make it like the original map

        p.terminate()
        # for res_dict in retval_par:  # add all experiments params we missed
        #     curr_params_list = res_dict["curr_params_list"]
        #     for param in curr_params_list:
        #         cn.add_experiment_param(param)
        # cn.experiment_purpose = retval_par[0]["experiment_purpose"]  # use the "experiment_purpose" from the fork
        # function_cache.merge_cache_dicts_from_parallel_runs(map(lambda a: a["functions_dict"], retval_par))  # merge all
        proc_count = old_proc_count
        global i
        i += 1
    except AssertionError as e:
        if str(e) == "daemonic processes are not allowed to have children":
            retval = map(f, X)  # can't have pool inside pool
        else:
            print("error message is: " + str(e))
            raise  # re-raise orig exception
    return retval
Beispiel #4
0
def parmap(f: Callable,
           X: List[object],
           nprocs=multiprocessing.cpu_count(),
           force_parallel=False,
           chunk_size=1,
           use_tqdm=False,
           keep_child_tqdm=True,
           **tqdm_kwargs) -> list:
    """
    Utility function for doing parallel calculations with multiprocessing.
    Splits the parameters into chunks (if wanted) and calls.
    Equivalent to list(map(func, params_iter))
    Args:
        f: The function we want to calculate for each element
        X: The parameters for the function (each element ins a list)
        chunk_size: Optional, the chunk size for the workers to work on
        nprocs: The number of procs to use (defaults for all cores)
        use_tqdm: Whether to use tqdm (default to False)
        tqdm_kwargs: kwargs passed to tqdm

    Returns:
        The list of results after applying func to each element

    Has problems with using self.___ as variables in f (causes self to be pickled)
    """
    if len(X) == 0:
        return []  # like map
    if nprocs != multiprocessing.cpu_count() and len(X) < nprocs * chunk_size:
        chunk_size = 1  # use chunk_size = 1 if there is enough procs for a batch size of 1

    nprocs = int(max(1, min(nprocs, len(X) / chunk_size)))  # at least 1
    if len(X) < nprocs:
        if nprocs != multiprocessing.cpu_count():
            print("parmap too much procs")
        nprocs = len(X)  # too much procs

    args = zip(X, [f] * len(X), range(len(X)), [keep_child_tqdm] * len(X))
    if chunk_size > 1:
        args = list(chunk_iterator(args, chunk_size))
        s_fun = _chunk_spawn_fun  # spawn fun
    else:
        s_fun = _spawn_fun  # spawn fun

    if (nprocs == 1 and not force_parallel
        ) or force_serial:  # we want it serial (maybe for profiling)
        return list(map(f, tqdm(X, disable=not use_tqdm, **tqdm_kwargs)))

    try:  # try-catch hides bugs
        global proc_count
        old_proc_count = proc_count
        proc_count = nprocs
        p = Pool(nprocs)
        p.restart(force=True)
        # can throw if current proc is daemon
        if use_tqdm:
            retval_par = tqdm(p.imap(lambda arg: s_fun(arg), args),
                              total=int(len(X) / chunk_size),
                              **tqdm_kwargs)
        else:
            # import  pdb
            # pdb.set_trace()
            retval_par = p.map(lambda arg: s_fun(arg), args)

        retval = list(retval_par)  # make it like the original map
        if chunk_size > 1:
            retval = flatten(retval)

        p.terminate()
        proc_count = old_proc_count
        global i
        i += 1
    except AssertionError as e:
        # if e == "daemonic processes are not allowed to have children":
        retval = list(map(f,
                          tqdm(X, disable=not use_tqdm,
                               **tqdm_kwargs)))  # can't have pool inside pool
    return retval
Beispiel #5
0
class MainLoop(metaclass=ABCMeta):
    """
    Defines the logical loop of a game, running MAX_FPS times per second, sending the inputs to the HumanControllerWrapper, and the
    game updates to the BotControllerWrappers.
    """
    def __init__(self, api: API):
        """
        Instantiates the logical loop

        Args:
            api: The game to run in this loop
        """
        self.api = api
        self.game = api.game
        self.game.addCustomMoveFunc = self._addCustomMove
        self._currentTurnTaken = False
        self._screen = None
        self._state = CONTINUE  # The game must go on at start
        self._eventsToSend = {}  # type: Dict[ControllerWrapper, List[Event]]

        self.wrappersConnection = {
        }  # type: Dict[ControllerWrapper, PipeConnection]
        self.wrappersInfoConnection = {
        }  # type: Dict[ControllerWrapper, PipeConnection]

        self.wrappers = {}  # type: Dict[ControllerWrapper, Unit]
        self._unitsMoves = {}  # type: Dict[Unit, Tuple[Path, Queue]]
        self._moveDescriptors = {}  # type: Dict[Path, MoveDescriptor]
        self._otherMoves = {}  # type: Dict[Unit, Path]
        self._killSent = {
        }  # Used to maintain the fact that the kill event has been sent
        self.executor = None
        self._prepared = False
        self._frames = 0

    # -------------------- PUBLIC METHODS -------------------- #

    def run(self, max_fps: int = MAX_FPS) -> Union[None, Tuple[Unit, ...]]:
        """
        Launch the game and its logical loop

        Args:
            max_fps: The maximum frame per seconds of the game

        Returns:
            a tuple containing all the winning players, or an empty tuple in case of draw,
            or None if the game was closed by the user
        """
        pygame.init()
        clock = pygame.time.Clock()
        assert self.game.board.graphics is not None
        try:
            self._screen = pygame.display.set_mode(
                self.game.board.graphics.size, DOUBLEBUF)
        except pygame.error:  # No video device
            pass
        if not self._prepared:
            self._prepareLoop()
        for player_number in self.api.getPlayerNumbers():
            self._sendEventsToController(player_number)
        while self._state != END:
            clock.tick(max_fps)
            self._frames += 1
            self._handleInputs()
            if self._state == FINISH:
                self.executor.terminate()
                self._prepared = False
                return None
            elif self._state != PAUSE:
                self._state = self._checkGameState()
                if self._state == CONTINUE:
                    self._getNextMoveFromControllerWrapperIfAvailable()
                    self._handlePendingMoves()
                    self._refreshScreen()
        self.executor.terminate()
        self._prepared = False
        return self.game.winningPlayers

    def addUnit(self,
                unit: Unit,
                wrapper: ControllerWrapper,
                tile_id: TileIdentifier,
                initial_action: MoveDescriptor = None,
                team: int = -1) -> None:
        """
        Adds a unit to the game, located on the tile corresponding
        to the the given tile id and controlled by the given controller

        Args:
            unit: The unit to add to the game
            wrapper: The linker of that unit
            tile_id: The identifier of the tile it will be placed on
            initial_action: The initial action of the unit
            team: The number of the team this player is in (-1 = no team)
        """
        is_controlled = wrapper is not None
        self.game.addUnit(unit, team, tile_id, is_avatar=is_controlled)
        if is_controlled:
            self._addControllerWrapper(wrapper, unit)
            if self._mustSendInitialWakeEvent(initial_action, unit):
                self._eventsToSend[wrapper].append(WakeEvent())
        self._unitsMoves[unit] = (None, Queue())
        tile = self.game.board.getTileById(tile_id)
        resize_unit(unit, self.game.board)
        unit.moveTo(tile.center)
        if initial_action is not None:
            unit.setLastAction(initial_action)
            self._handleEvent(unit,
                              initial_action,
                              wrapper.controller.playerNumber,
                              force=True)

    def getWrapperFromPlayerNumber(self, player_number: int):
        """
        Retrieves the wrapper from the given player number

        Args:
            player_number: The number representing the player for which we want the wrapper 

        Returns: The wrapper that wraps the controller of the given player
        """
        found = None
        for wrapper in self.wrappersConnection:
            if wrapper.controller.playerNumber == player_number:
                found = wrapper
                break
        return found

    def pause(self) -> None:
        """
        Change the state of the game to "PAUSE"
        """
        self._state = PAUSE
        print(self._frames, "frames")

    def resume(self) -> None:
        """
        Resume the game
        """
        self._state = CONTINUE

    # -------------------- PROTECTED METHODS -------------------- #

    def _refreshScreen(self) -> None:
        """
        Update the visual state of the game
        """
        try:
            if self._screen is None:
                raise pygame.error("No Video device")
            self.game.board.draw(self._screen)
            drawn_units = []
            for unit in self.wrappers.values():
                if unit.isAlive():
                    unit.draw(self._screen)
                    drawn_units.append(unit)
            for unit in self.game.unitsLocation:
                if unit.isAlive() and unit not in drawn_units:
                    unit.draw(self._screen)
            pygame.display.flip()
        except pygame.error:  # No video device
            pass

    def _handleInputs(self) -> None:
        """
        Handles all the user input (mouse and keyboard)
        """
        try:
            events_got = pygame.event.get()
        except pygame.error:  # No video device
            events_got = []
        for event in events_got:
            if event.type == QUIT:
                self._state = FINISH
            elif event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    if self._state == CONTINUE:
                        self.pause()
                    elif self._state == PAUSE:
                        self.resume()
                else:
                    self._dispatchInputToHumanControllers(event.key)
            elif event.type == MOUSEBUTTONDOWN:
                self._dispatchMouseEventToHumanControllers(event.pos)
            elif event.type == MOUSEBUTTONUP:
                self._dispatchMouseEventToHumanControllers(None, click_up=True)

    def _addMove(self, unit: Unit, move: Path) -> None:
        """
        Adds a move (cancelling the pending moves)

        Args:
            unit: The unit for which add a move
            move: The move to add for the given controller
        """
        if self._unitsMoves[unit][0] is not None:
            self._cancelCurrentMoves(unit)
        fifo = self._unitsMoves[unit][1]  # type: Queue
        fifo.put(move)

    def _addCustomMove(self, unit: Unit, move: Path,
                       event: MoveDescriptor) -> None:
        """
        Adds a move that is NOT PERFORMED BY A CONTROLLER

        Args:
            unit: The unit that will be moved
            move: The move that will be performed
        """
        if unit not in self._otherMoves or self._otherMoves[unit] is None:
            self._otherMoves[unit] = move
        self._moveDescriptors[move] = event

    def _cancelCurrentMoves(self, unit: Unit) -> None:
        """
        Cancel the current movement if there is one and remove all the other pending movements.

        Args:
            unit: The unit for which cancel the movements
        """
        if unit in self._unitsMoves:
            move_tuple = self._unitsMoves[unit]
            fifo = move_tuple[1]  # type: Queue
            last_move = move_tuple[0]  # type: Path
            new_fifo = Queue()
            if last_move is not None:
                last_move.stop()
            while True:
                try:
                    move = fifo.get_nowait()
                    del self._moveDescriptors[move]
                except Empty:
                    break
            self._unitsMoves[unit] = (last_move, new_fifo)

    def _dispatchInputToHumanControllers(self, input_key) -> None:
        """
        Handles keyboard events and send them to Human Controllers to trigger actions if needed

        Args:
            input_key: The key pressed on the keyboard
        """
        for linker in self.wrappers:  # type: HumanControllerWrapper
            if issubclass(type(linker), HumanControllerWrapper):
                self._getPipeConnection(linker).send(
                    self.game.createKeyboardEvent(
                        self._getUnitFromControllerWrapper(linker), input_key))

    def _dispatchMouseEventToHumanControllers(self,
                                              pixel: Optional[Coordinates],
                                              click_up=False) -> None:
        """
        Handles mouse events and send them to Human Controllers to trigger actions if needed

        Args:
            pixel: The pixel clicked
            click_up: True if the button was released, False if the button was pressed
        """
        tile = None
        if pixel is not None:
            tile = self.game.board.getTileByPixel(pixel)
        self._previouslyClickedTile = tile
        mouse_state = pygame.mouse.get_pressed()
        for linker in self.wrappers:  # type: ControllerWrapper
            if issubclass(type(linker), HumanControllerWrapper):
                tile_id = None
                if tile is not None:
                    tile_id = tile.identifier
                self._getPipeConnection(linker).send(
                    self.game.createMouseEvent(
                        self._getUnitFromControllerWrapper(linker), pixel,
                        mouse_state, click_up, tile_id))

    def _getNextMoveFromControllerWrapperIfAvailable(self) -> None:
        """
        Gets event from the controllers and dispatch them to the right method
        """
        for current_wrapper in self.wrappersConnection:  # type: ControllerWrapper
            pipe_conn = self._getPipeConnection(current_wrapper)
            if pipe_conn.poll():
                move = pipe_conn.recv()
                if self._mustRetrieveNextMove(current_wrapper):
                    self._handleEvent(self.wrappers[current_wrapper], move,
                                      current_wrapper.controller.playerNumber)

    def _handlePendingMoves(self) -> None:
        """
        Get the next move to be performed and perform its next step
        """
        moved_units = []

        completed_moves = {
        }  # type: Dict[Unit, Tuple[TileIdentifier, MoveDescriptor]]
        just_started = {}  # type: Dict[int, MoveDescriptor]
        illegal_moves = []  # type: List[Unit]
        impossible_moves = {}  # type: List[Unit]

        self._handleOtherMoves(completed_moves, illegal_moves,
                               impossible_moves, just_started, moved_units)
        self._handleMoves(completed_moves, illegal_moves, impossible_moves,
                          just_started, moved_units)
        self._updateFromMoves(completed_moves, illegal_moves, impossible_moves,
                              just_started)

    def _updateFromMoves(self, completed_moves, illegal_moves,
                         impossible_moves, just_started):
        players_to_be_sent_messages = []
        for unit, (tile_id, move_descriptor) in completed_moves.items():
            self.game.updateGameState(unit, tile_id, move_descriptor)
        for player_number, move_descriptor in just_started.items():
            controller_unit = self.game.getControllerUnitForNumber(
                player_number)
            if controller_unit is not None:
                controller_unit.setCurrentAction(move_descriptor)
            self._addMessageToSendToAll(player_number, move_descriptor)
            players_to_be_sent_messages = self._getPlayerNumbersToWhichSendEvents(
            )
        for player_number in players_to_be_sent_messages:
            self._sendEventsToController(player_number)
        for unit in illegal_moves:
            self.game.unitsLocation[
                unit] = self.game.board.OUT_OF_BOARD_TILE.identifier
            self._killUnit(unit,
                           self.getWrapperFromPlayerNumber(unit.playerNumber))
            # self.game.checkIfFinished()
            self._cancelCurrentMoves(unit)
        for unit in impossible_moves:
            self._cancelCurrentMoves(unit)
        self.game.checkIfFinished()

    def _handleMoves(self, completed_moves, illegal_moves, impossible_moves,
                     just_started, moved_units):
        for wrapper in self.wrappers:  # type: ControllerWrapper
            unit = self._getUnitFromControllerWrapper(wrapper)
            if unit not in moved_units:  # Two moves on the same unit cannot be performed at the same time...
                if not unit.isAlive() and (unit not in self._killSent
                                           or not self._killSent[unit]):
                    self.wrappersInfoConnection[wrapper].send(
                        SpecialEvent(flag=SpecialEvent.UNIT_KILLED))
                    self._killSent[unit] = True
                current_move = self._getNextMoveForUnitIfAvailable(unit)
                if current_move is not None:
                    move_state = self._performNextStepOfMove(
                        current_move.unit, current_move)
                    self._fillMoveStructures(completed_moves, just_started,
                                             illegal_moves, impossible_moves,
                                             current_move, move_state)

    def _handleOtherMoves(self, completed_moves, illegal_moves,
                          impossible_moves, just_started, moved_units):
        for unit in self._otherMoves:  # type: Unit
            move = self._otherMoves[unit]
            if move is not None:
                move_state = self._performNextStepOfMove(move.unit, move)
                if move_state != MOVE_FAILED:
                    moved_units.append(move.unit)
                if move.finished():
                    self._otherMoves[unit] = None
                self._fillMoveStructures(completed_moves, just_started,
                                         illegal_moves, impossible_moves, move,
                                         move_state)

    def _fillMoveStructures(self, completed_moves: Dict[Unit,
                                                        Tuple[TileIdentifier,
                                                              MoveDescriptor]],
                            just_started: Dict[int, MoveDescriptor],
                            illegal_moves: List[Unit],
                            impossible_moves: List[Unit], move: Path,
                            move_state: int):
        """
        Takes a move's state and the data structures of the performed moves in this iteration an fill them
         following the state's value
         
        Args:
            completed_moves: The dict containing the units that completed a move along with their new tile_id 
            just_started: 
                The dict containing the number of the units that started a move, 
                along with the descriptor of the started move
            illegal_moves: The list containing all the units that performed an illegal move this iteration  
            impossible_moves: The list containing all the units that performed an impossible move this iteration  
            move: The performed move
            move_state: The state of the performed move
        """
        if move_state == MOVE_COMPLETED_AND_JUST_STARTED:
            completed_moves[move.unit] = (move.reachedTileIdentifier,
                                          self._moveDescriptors[move])
            just_started[move.unit.playerNumber] = self._moveDescriptors[move]
        elif move_state == MOVE_COMPLETED:
            completed_moves[move.unit] = (move.reachedTileIdentifier,
                                          self._moveDescriptors[move])
        elif move_state == MOVE_JUST_STARTED:
            just_started[move.unit.playerNumber] = self._moveDescriptors[move]
        elif move_state == MOVE_ILLEGAL:
            illegal_moves.append(move.unit)
        elif move_state == MOVE_IMPOSSIBLE:
            impossible_moves.append(move.unit)

    def _addMessageToSendToAll(self, moved_unit_number: int,
                               move_descriptor: MoveDescriptor):
        """
        Adds a message to the message queue of each ControllerWrapper
        
        Args:
            moved_unit_number: The number representing the unit that moved 
            move_descriptor: The descriptor of the performed move
        """
        controlled_unit = self.game.getControllerUnitForNumber(
            moved_unit_number)
        if controlled_unit is None:
            controlled_unit_number = moved_unit_number
        else:
            controlled_unit_number = controlled_unit.playerNumber
        for wrapper in self._eventsToSend:
            self._eventsToSend[wrapper].append(
                BotEvent(controlled_unit_number, move_descriptor))

    @staticmethod
    def _performNextStepOfMove(unit: Unit, current_move: Path) -> int:
        """
        Perform the next step of the given move on the given unit

        Args:
            unit: The unit that performs the move
            current_move: The current move to perform
        
        Returns:
            A couple of booleans. The first indicating that the move has been completed and the second indicating that
            the move has just started
        """
        if unit.isAlive():
            if current_move is not None:
                try:
                    just_started, move_completed, tile_id = current_move.performNextMove(
                    )
                    if just_started and move_completed:
                        return MOVE_COMPLETED_AND_JUST_STARTED
                    elif move_completed:  # A new tile has been reached by the movement
                        return MOVE_COMPLETED
                    elif just_started:
                        return MOVE_JUST_STARTED
                    return MOVE_IN_PROGRESS
                except IllegalMove:
                    return MOVE_ILLEGAL
                except ImpossibleMove:
                    return MOVE_IMPOSSIBLE
        else:
            if current_move is not None:
                current_move.stop(cancel_post_action=True)
        return MOVE_FAILED

    def _getNextMoveForUnitIfAvailable(self, unit: Unit) -> Union[Path, None]:
        """
        Checks if a move is available for the given controller, and if so, returns it

        Args:
            unit: The given

        Returns: The next move if it is available, and None otherwise
        """
        moves = self._unitsMoves[unit]
        current_move = moves[0]  # type: Path
        if current_move is None or current_move.finished():
            if current_move is not None:
                if isinstance(current_move, Path):
                    self._reactToFinishedMove()
                    del self._moveDescriptors[current_move]
            try:
                move = moves[1].get_nowait()  # type: Path
                self._unitsMoves[unit] = (move, moves[1])
                current_move = move
            except Empty:
                self._unitsMoves[unit] = (None, moves[1])
                current_move = None
        return current_move

    def _checkGameState(self) -> int:
        """
        Checks if the game is finished

        Returns: 0 = CONTINUE; 2 = END
        """
        if self.game.isFinished():
            self.winningPlayers = self.game.winningPlayers
            return END
        return CONTINUE

    def _handleEvent(self,
                     unit: Unit,
                     event: MoveDescriptor,
                     player_number: int,
                     force: bool = False) -> None:
        """
        The goal of this method is to handle the given event for the given unit

        Args:
            unit: The unit that sent the event through its linker
            event: The event sent by the controller
        """
        try:
            move = self.api.createMoveForDescriptor(
                unit, event, force=force)  # may raise: UnfeasibleMoveException
            self._currentTurnTaken = True
            self._moveDescriptors[move] = event
            self._addMove(unit, move)
        except UnfeasibleMoveException:
            self._sendEventsToController(player_number, event=WakeEvent())

    def _getPipeConnection(self, linker: ControllerWrapper) -> PipeConnection:
        """
        Args:
            linker: The linker for which we want the pipe connection

        Returns: The pipe connection to send and receive game updates
        """
        return self.wrappersConnection[linker]

    def _getUnitFromControllerWrapper(self, linker: ControllerWrapper) -> Unit:
        """
        Args:
            linker: The linker for which we want the unit

        Returns: The unit for the given linker
        """
        return self.wrappers[linker]

    def _sendEventsToController(self, player_number: int, event: Event = None):

        player_wrapper = self.getWrapperFromPlayerNumber(player_number)
        pipe_conn = self._getPipeConnection(player_wrapper)
        if event is None:
            events = self._eventsToSend[player_wrapper]
            if len(events) > 0:
                event = MultipleEvents(events)
        if event is not None:
            pipe_conn.send(event)
            self._eventsToSend[player_wrapper] = []

    def _informBotOnPerformedMove(self, moved_unit_number: int,
                                  move_descriptor: MoveDescriptor) -> None:
        """
        Update the game state of the bot controllers

        Args:
            moved_unit_number: The number representing the unit that moved and caused the update
            move_descriptor: The move that caused the update
        """
        for wrapper in self.wrappers:
            if issubclass(type(wrapper), BotControllerWrapper):
                pipe_conn = self._getPipeConnection(wrapper)
                pipe_conn.send(BotEvent(moved_unit_number, move_descriptor))

    def _killUnit(self, unit: Unit, linker: ControllerWrapper) -> None:
        """
        Kills the given unit and tells its linker

        Args:
            unit: The unit to kill
            linker: The linker, to which tell that the unit is dead
        """
        unit.kill()
        if not unit.isAlive():
            self.wrappersInfoConnection[linker].send(
                SpecialEvent(flag=SpecialEvent.UNIT_KILLED))

    def _addCollaborationPipes(self, linker: BotControllerWrapper) -> None:
        """
        Adds the collaboration pipes between the given linker and its teammate's

        Args:
            linker: The linker to connect with its teammate
        """
        for teammate in self.game.teams[self.game.unitsTeam[
                self.wrappers[linker]]]:
            if teammate is not self.wrappers[linker]:
                teammate_linker = None  # type: BotControllerWrapper
                for other_linker in self.wrappers:
                    if self.wrappers[other_linker] is teammate:
                        teammate_linker = other_linker
                        break
                pipe1, pipe2 = Pipe()
                linker.addCollaborationPipe(
                    teammate_linker.controller.playerNumber, pipe1)
                teammate_linker.addCollaborationPipe(
                    linker.controller.playerNumber, pipe2)

    def _prepareLoop(self) -> None:
        """
        Launches the processes of the AIs
        """
        self.executor = Pool(len(self.wrappers))
        try:
            self.executor.apipe(lambda: None)
        except ValueError:
            self.executor.restart()
        for wrapper in self.wrappers:
            if isinstance(wrapper, BotControllerWrapper):
                wrapper.controller.gameState = self.game.copy()
            self.executor.apipe(wrapper.run)
        for wrapper in self.wrappers:
            pipe = self._getPipeConnection(wrapper)
            event = pipe.recv(
            )  # Waiting for the processes to launch correctly
            assert (isinstance(event, ReadyEvent))
        self._prepared = True

    def _addControllerWrapper(self, wrapper: ControllerWrapper,
                              unit: Unit) -> None:
        """
        Adds the linker to the loop, creating the pipe connections

        Args:
            wrapper: The linker to add
            unit: The unit, linked by this linker
        """
        self.wrappers[wrapper] = unit
        parent_conn, child_conn = Pipe()
        parent_info_conn, child_info_conn = Pipe()
        self.wrappersConnection[wrapper] = parent_conn
        self.wrappersInfoConnection[wrapper] = parent_info_conn
        self._eventsToSend[wrapper] = []
        wrapper.setMainPipe(child_conn)
        wrapper.setGameInfoPipe(child_info_conn)
        if isinstance(wrapper, BotControllerWrapper):
            self._addCollaborationPipes(wrapper)

    @abstractmethod
    def _mustSendInitialWakeEvent(self, initial_action: MoveDescriptor,
                                  unit: Unit) -> bool:
        pass

    @abstractmethod
    def _mustRetrieveNextMove(self,
                              current_wrapper: ControllerWrapper) -> bool:
        pass

    @abstractmethod
    def _getPlayerNumbersToWhichSendEvents(self) -> List[int]:
        pass

    @abstractmethod
    def _reactToFinishedMove(self):
        pass
Beispiel #6
0
 def run_global_automated(self,
                          grphq=False,
                          duration_gif=0.5,
                          pas=1,
                          B=10,
                          loop=100):
     """
     Implémentation modifiée de run_global où le choix du nombre de cluster est déterminé par des statistiques calculés au fur et à mesure.
     Les paramètres sont : grphq, duration_gif, pas, B, loop et correspondent aux définitions évoqués dans run_global.
     Paramètre de sortie : instance idéal de Kmeans.
     """
     pool = Pool(self.cpu)
     pool.close()
     pool.join()
     mini, maxi = np.min(self.data.data, axis=0), np.max(self.data.data,
                                                         axis=0)
     shape = self.data.data.shape
     i = 1
     self.set_nb_cluster(i)
     self.choose_means_initiate()
     self.calc_grp()
     self.choose_means()
     self.calc_grp()
     means = self.means
     if grphq:
         self.grphq.plot_graph(self.data.data, self.grp,
                               self.means.reshape((1, -1)), 1)
     self.print_meta_data()
     gap, var = self.gap_stat_mono(self.error, i, mini, maxi, shape, pool,
                                   B)
     cond = True
     km_cpy = self.copy(erase_dir=False)
     print("Fin de l'étape {}".format(i))
     while cond:
         i += 1
         self.set_nb_cluster(i)
         pool.restart()
         f = partial(self.__multi_j, loop=loop, means=means)
         s = pool.uimap(f, range(0, self.L, pas))
         pool.close()
         pool.join()
         s = np.array(list(s))
         arg = np.argmin(s[:, 1])
         j = int(s[arg, 0])
         means_cpy = np.vstack((means, self.data.data[j]))
         self.means = means_cpy
         k = 0
         backup = (None, None, -1, -1)
         self.calc_grp()
         while (self.cond_conv(backup)) and (k < loop):
             k += 1
             backup = self.backup_metadata()
             self.choose_means()
             if ((self.choose_means != self.choose_means_moy_true)
                     and (self.choose_means != self.choose_means_med_true)):
                 self.calc_grp()
             self.migration = np.count_nonzero(
                 (self.grp[:, 1] - backup[1][:, 1]))
             self.same_means = np.array_equal(self.means, backup[0])
         means = self.means
         gap_f, var_f = self.gap_stat_mono(self.error, i, mini, maxi, shape,
                                           pool, B)
         diff = gap - (gap_f - var_f)
         print("Gap statistical (étape {}) : {}".format(i - 1, diff))
         if grphq:
             self.grphq.plot_graph(self.data.data, self.grp, self.means, i)
         self.print_meta_data()
         print("Fin de l'étape {}".format(i))
         if diff >= 0:
             break
         else:
             gap = gap_f
             km_cpy = self.copy(erase_dir=False)
     if grphq: self.grphq.create_gif(duration=duration_gif)
     self = km_cpy.copy(erase_dir=False)
     self.calc_grp()
     print("Le nombre optimal de classes est : {}".format(self.nb_cluster))
     del pool
     return self
Beispiel #7
0
 def run_global(self,
                loop=100,
                grphq=False,
                duration_gif=0.5,
                pas=1,
                choose_nb_graph=False,
                B=10):
     """
     Implémente l'algorithme des global k-means qui calcule incrémentalement 
     la configuration optimale des groupes pour un nombre de clusters donnée.
     L'algotrithme procède comme suit :
         0) On définit le nombre cluster à 1 et on calcule le centre de la matrice de données.
         1) On incrément le nombre de cluster.
         On définit comme centre les centres de l'étape précédente.
         On définit successivement chaque individu de la matrice de données comme dernier centre, on exécute l'algorithme du
             k-means avec chaque lot de centres et on garde le lot de centre qui minimise l'erreur.
         i+1) On réitère l'étape précédente jusqu'à obtenir le bon nombre de groupe.
     !!! Très gourmand en ressources.
     
     Paramètres d'entrée :
         loop : entier définissant le nombre d'itérations au sein des calcule de k-means avant arrêt du calcul, défaut = 100
         grphq : boolean indiquant si les graphes doivent être affichés et enregistrés.
         duration_gif : réel qui caractérise la durée de chaque image dans la production du gif final, inutile si grphq = False
         pas : entier qui détermine l'écart entre chaque individu à tester pour le choix des individus comme centre.
         choose_nb_graph : boolean, affiche un lot de statistiques qui permettent de déterminer le nombre idéal de clusters.
         B : entier qui qui entre en jeu dans le calcul des statistiques évoquées précédemment.
     Paramètre de sortie :
         err : erreur de classification pour le nombre de cluster choisi.
     """
     pool = Pool(self.cpu)
     pool.close()
     pool.join()
     err = []
     n = self.nb_cluster
     self.set_nb_cluster(1)
     self.choose_means_initiate()
     self.calc_grp()
     self.choose_means()
     self.calc_grp()
     means = self.means
     err.append([1, self.error, self.clustering_error_rel(), self.var])
     if grphq:
         self.grphq.plot_graph(self.data.data, self.grp,
                               self.means.reshape((1, -1)), 1)
     self.print_meta_data()
     print("Fin de l'étape {}".format(1))
     for i in range(2, n + 1):
         self.set_nb_cluster(i)
         pool.restart()
         f = partial(self.__multi_j, loop=loop, means=means)
         s = pool.uimap(f, range(0, self.L, pas))
         pool.close()
         pool.join()
         s = np.array(list(s))
         arg = np.argmin(s[:, 1])
         j = int(s[arg, 0])
         means_cpy = np.vstack((means, self.data.data[j]))
         self.means = means_cpy
         k = 0
         backup = (None, None, -1, -1)
         self.calc_grp()
         while (self.cond_conv(backup)) and (k < loop):
             k += 1
             backup = self.backup_metadata()
             self.choose_means()
             if ((self.choose_means != self.choose_means_moy_true)
                     and (self.choose_means != self.choose_means_med_true)):
                 self.calc_grp()
             self.migration = np.count_nonzero(
                 (self.grp[:, 1] - backup[1][:, 1]))
             self.same_means = np.array_equal(self.means, backup[0])
         means = self.means
         if grphq:
             self.grphq.plot_graph(self.data.data, self.grp, self.means, i)
         self.print_meta_data()
         err.append([i, self.error, self.clustering_error_rel(), self.var])
         print("Fin de l'étape {}".format(i))
     err = np.array(err)
     err = err[np.argsort(err[:, 0]), :].T
     if grphq: self.grphq.create_gif(duration=duration_gif)
     if choose_nb_graph:
         self.grphq.plot_crb_err_cluster(self.gap_stat(err, B))
     del pool
     return err