Example #1
0
class Replayer(object):
    """
    Base class for replaying log files
    :ivar files: List of names of the log files to process
    :type files: List(str)
    :ivar rddl_file: Name of file containing the RDDL specification of the domain
    :type rddl_file: str
    :ivar action_file: Name of CSV file containing the mapping between JSON messages and PsychSim actions
    :type action_file: str
    """
    OBSERVER = 'ATOMIC'

    COUNT_ACTIONS_ARGS = [
        ('Event:dialogue_event', {}),
        ('Event:VictimPickedUp', {}),
        ('Event:VictimPlaced', {}),
        ('Event:ToolUsed', {}),
        ('Event:Triage', {'triage_state': 'SUCCESSFUL'}),
        ('Event:RoleSelected', {})
    ]

    def __init__(self, files=[], maps=None, processor=None, rddl_file=None, action_file=None, feature_output=None, logger=logging):
        # Extract files to process
        self.files = accumulate_files(files)
        self.processor = processor
        self.logger = logger
        self.rddl_file = rddl_file
        self.action_file = action_file
        if self.action_file:
            Msg2ActionEntry.read_psysim_msg_conversion(self.action_file)
            self.msg_types = Msg2ActionEntry.get_msg_types()
        else:
            self.msg_types = None
        self.rddl_converter = None

        # Feature count bookkeeping
        self.derived_features = []
        self.feature_output = feature_output
        self.feature_data = []
        self.condition_fields = None

        # information for each log file # TODO maybe encapsulate in an object and send as arg in post_replay()?
        self.world = None
        self.triage_agent = None
        self.observer = None
        self.victims = None
        self.world_map = None
        self.map_table = None
        self.parser = None
        self.conditions = None
        self.file_name = None

        # Extract maps
        self.maps = get_default_maps(logger) if maps is None else maps

    def get_map(self, logger=logging):
        # try to get map name directly from conditions dictionary
        try:
            map_name = self.conditions['CondWin']
            map_table = self.maps[map_name]
            return map_name, map_table
        except KeyError:
            # Maybe Phase 1 filename scheme?
            map_name = self.conditions['CondWin'][0]
            map_table = self.maps[map_name]
            return map_name, map_table

        # todo to be retro-compatible would have to determine the map some other way..
        logger.error('Unable to find matching map')
        return None, None

    def process_files(self, num_steps=0, fname=None):
        """
        :param num_steps: if nonzero, the maximum number of steps to replay from each log (default is 0)
        :type num_steps: int
        :param fname: Name of log file to process (default is all of them)
        :type fname: str
        """
        if fname is None:
            files = self.files
        else:
            files = [fname]
        # Get to work
        for fname in files:
            self.file_name = fname
            logger = self.logger.getLogger(os.path.splitext(os.path.basename(fname))[0])
            logger.debug('Full path: {}'.format(fname))
            self.conditions = filename_to_condition(os.path.splitext(os.path.basename(fname))[0])

            # Parse events from log file
            logger_name = type(self.processor).__name__ if self.processor is not None else ''
            _, ext = os.path.splitext(fname)
            ext = ext.lower()
            if ext == '.csv' or ext == '.xlsx':
                map_name, self.map_table = self.get_map(logger)
                if map_name is None or self.map_table is None:
                    # could not determine map
                    continue
                self.parser = ProcessCSV(fname, self.processor, logger.getChild(logger_name))
            elif ext == '.metadata':
                try:
                    self.parser = MsgQCreator(fname, self.processor, logger=logger.getChild(logger_name))
                except:
                    logger.error('Unable to extract actions/events')
                    logger.error(traceback.format_exc())
                    continue
                self.derived_features = _get_derived_features(self.parser)
            else:
                raise ValueError('Unable to parse log file: {}, unknown extension.'.format(fname))

            # set parser to processor
            if self.processor is not None:
                self.processor.parser = self.parser

            if not self.pre_replay(logger=logger.getChild('pre_replay')):
                # Failure in creating world
                continue

            # Replay actions from log file
            try:
                self.parser.getActionsAndEvents(self.victims, self.world_map)
            except:
                logger.error(traceback.format_exc())
                logger.error('Unable to extract actions/events')
                continue
            if num_steps == 0:
                last = len(self.parser.actions)
            else:
                last = num_steps + 1
            try:
                self.replay(last, logger)
            except:
                logger.error(traceback.format_exc())
                logger.error('Unable to complete re-simulation')
            self.post_replay()
            if self.world_map: self.world_map.clear()
        if self.feature_output:
            assert self.condition_fields is not None, 'Never extracted condition fields from filename'
            with open(self.feature_output, 'w') as csvfile:
                cumulative_fields = [set(row.keys()) for row in self.feature_data]
                fields = self.condition_fields + sorted(set.union(*cumulative_fields)-{'File'})
                fields.append('File')
                writer = csv.DictWriter(csvfile, fields, extrasaction='ignore')
                writer.writeheader()
                for row in self.feature_data:
                    row.update(filename_to_condition(row['File']))
                    writer.writerow(row)

    def pre_replay(self, logger=logging):
        # Create PsychSim model
        logger.info('Creating world')

        try:
            self.parser.startProcessing(self.derived_features, self.msg_types)
        except:
            logger.error('Unable to start parser')
            logger.error(traceback.format_exc())
            return False

        if self.feature_output:
            # processes data to extract features depending on type of count
            features = {'File': os.path.splitext(os.path.basename(self.file_name))[0]}
            if self.condition_fields is None:
                self.condition_fields = list(filename_to_condition(features['File']).keys())
            for feature in self.derived_features:
                features.update(_get_feature_values(feature))
            self.feature_data.append(features)

        try:
            if self.rddl_file:
                # Team mission
                self.rddl_converter = Converter()
                self.rddl_converter.convert_file(self.rddl_file)
                self.world = self.rddl_converter.world
                players = set(self.parser.agentToPlayer.keys())
                zero_models = {name: self.world.agents[name].zero_level() for name in players}
                for name in players:
                    agent = self.world.agents[name]
                    agent.create_belief_state()
                    agent.create_belief_state(model=zero_models[name])
                    agent.setAttribute('selection', 'distribution', zero_models[name])
                    for other_name in players-{name}:
                        other_agent = self.world.agents[other_name]
                        self.world.setModel(name, zero_models[name], other_name, other_agent.get_true_model())
                    agent.set_observations()
            else:
                # Solo mission
                self.world, self.triage_agent, _, self.victims, self.world_map = \
                    make_single_player_world(self.parser.player_name(), self.map_table.init_loc,
                                             self.map_table.adjacency, self.map_table.victims, False, True,
                                             False, logger.getChild('make_single_player_world'))
        except:
            logger.error('Unable to create world')
            exc_type, exc_value, exc_traceback = sys.exc_info()
            logger.error(traceback.format_exc())
            return False
        return True

    def replay(self, duration, logger):
        if isinstance(self.parser, MsgQCreator):
            num = len(self.parser.actions)
            for i, msgs in enumerate(self.parser.actions):
                logger.info(f'Message {i} out of {num}')
                debug = {ag_name: {} for ag_name in self.rddl_converter.actions}
                
                actions = {}
                any_none = False
                for player_name, msg in msgs.items():
                    action_name = Msg2ActionEntry.get_action(msg)
                    if action_name not in self.rddl_converter.actions[player_name]:
                        any_none = True
                        logger.warning(f'Msg {msg} has no associated action ({action_name} missing from {", ".join(sorted(self.rddl_converter.actions[player_name]))})')
                    else:
                        logger.info(f'Msg {msg} becomes {action_name}')
                        action = self.rddl_converter.actions[player_name][action_name]
                        actions[player_name] = action
                
                if any_none:
                    continue
                self.pre_step()
                self.world.step(actions, debug=debug)

                self.post_step(actions, debug)
        else:
            self.parser.runTimeless(self.world, 0, duration, duration, permissive=True)

    def post_replay(self):
        pass

    def pre_step(self):
        pass

    def post_step(self, debug):
        pass

    def read_filename(self, fname):
        raise DeprecationWarning('Use filename_to_condition function (in this module) instead')

    def parameterized_replay(self, args):
        if args['profile']:
            return cProfile.run('self.process_files(args["number"])', sort=1)
        elif args['1']:
            return self.process_files(args['number'], replayer.files[0])
        else:
            return self.process_files(args['number'])
    help='Whether to select an outcome if dynamics are stochastic.')
parser.add_argument(
    '--log-actions',
    action='store_true',
    help='Whether to log agents\' actions in addition to current state.')
parser.add_argument(
    '--log-rewards',
    action='store_true',
    help=
    'Whether to log agents\' rewards wrt chosen actions in addition to current state.'
)
args = parser.parse_args()
args.log_rewards = True

conv = Converter()
conv.convert_file(RDDL_FILE, verbose=True)

################  J S O N   M S G   T O  P S Y C H S I M   A C T I O N   N A M E

fname = '../data/rddl_psim/rddl2actions_small.csv'
Msg2ActionEntry.read_psysim_msg_conversion(fname)

#################  F A K E    M S G S
all_msgs = []
#all_msgs.append({'p1': {'type':'Marker Block 1', 'playername':'p1', 'sub_type':'Event:MarkerPlaced'},
#                 'p2':{'room_name':'loc12', 'playername':'p2', 'sub_type':'Event:location'}})
#all_msgs.append({'p1': {'room_name':'loc12', 'playername':'p1', 'sub_type':'Event:location'},
#                 'p2':{'room_name':'loc11', 'playername':'p2', 'sub_type':'Event:location'}})

#all_msgs.append({'p1': {'room_name':'tkt_4', 'playername':'p1', 'sub_type':'Event:Location'},
#                 'p2':{'room_name':'tkt_5', 'playername':'p2', 'sub_type':'Event:Location'},
Example #3
0
        action='store_true',
        help=
        'Whether to log agents\' rewards wrt chosen actions in addition to current state.'
    )
    args = parser.parse_args()

    # prepare log to screen
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s [%(levelname)s] %(message)s',
                        datefmt='%H:%M:%S')

    # parse and convert RDDL file
    if not os.path.isfile(args.input):
        raise ValueError(f'Could not find RDDL file: {args.input}')
    conv = Converter()
    conv.convert_file(args.input, verbose=True)

    logging.info('')
    logging.info('==================================================')
    logging.info('Starting simulation...')
    conv.log_state()
    for i in tqdm(range(args.steps), ):
        logging.info('\n__________________________________________________')
        logging.info(f'Step {i}:')
        debug = {ag_name: {}
                 for ag_name in conv.actions.keys()
                 } if args.log_rewards else set()
        conv.world.step(debug=debug,
                        threshold=args.threshold,
                        select=args.select)
        conv.log_state(log_actions=args.log_actions)