def load_agent_state_core(data_central, id_agent, agent, id_robot,
                          reset_state=False,
                          raise_if_no_state=False):

    db = data_central.get_agent_state_db()
    key = dict(id_robot=id_robot, id_agent=id_agent)

    if reset_state:
        
        state = LearningState(id_robot=id_robot, id_agent=id_agent)
        return agent, state
    else:
        if db.has_state(**key):
            logger.info('Using previous learned state.')
            state = db.reload_state_for_agent(id_agent=id_agent, id_robot=id_robot,
                                              agent=agent)
            return agent, state
        else:
            msg = 'No previous learned state found for %r/%r.' % (id_agent, id_robot)
            
            if raise_if_no_state:
                raise Exception(msg)
            
            logger.info(msg)
                
            state = LearningState(id_robot=id_robot, id_agent=id_agent)
            return agent, state

    assert False
Esempio n. 2
0
def cmd_list_states(data_central, argv):
    '''Shows a summary of the states present in DB. '''
    parser = OptionParser(prog='list-states',
                          usage=cmd_list_states.short_usage)
    parser.disable_interspersed_args()
    parser.add_option("-v", dest='verbose', default=False, action='store_true',
                      help="Show more verbose output.")
    (options, args) = parser.parse_args(argv)

    check_no_spurious(args)
    db = data_central.get_agent_state_db()

    combinations = list(db.list_states())
    if not combinations:
        logger.info('No learning states saved in DB.')
    else:
        logger.info('Found %d combinations in DB.' % len(combinations))

    for id_agent, id_robot in combinations:
        logger.info('- Found state a: %-35s  r: %-25s' % (id_agent, id_robot))

        if options.verbose:
            try:
                state = db.get_state(id_robot=id_robot, id_agent=id_agent)
                logger.info('  # episodes: %s' % len(state.id_episodes))
                logger.info('      object: %s' % 
                            describe_value(state.agent_state))
            except Exception as e:
                logger.error('  (could not load state: %s) ' % e)

    if not options.verbose:
        logger.debug('Use -v for more information.')
Esempio n. 3
0
    def __init__(self, data_central, id_robot, id_agent, num_episodes,
                 cumulative=True, interval_print=5):
        self.data_central = data_central
        self.id_robot = id_robot
        self.cumulative = cumulative

        if self.cumulative:
            log_index = data_central.get_log_index()
            self.done_before = log_index.get_episodes_for_robot(id_robot,
                                                                id_agent)
            self.num_episodes_done_before = len(self.done_before)
            self.num_episodes_todo = (num_episodes - 
                                      self.num_episodes_done_before)
            logger.info('Preparing to do %d episodes (already done %d).' % 
                        (self.num_episodes_todo,
                         self.num_episodes_done_before))
        else:
            self.num_episodes_todo = num_episodes
            logger.info('Preparing to do %d episodes.' % 
                        self.num_episodes_todo)
        self.num_episodes_done = 0
        self.num_observations = 0
        self.num_observations_episode = 0
        self.observations_per_episode = []

        self.interval_print = interval_print
        self.tracker = InAWhile(interval_print)
        self.id_episodes = set()

        try:
            from compmake import progress
            progress('Simulating episodes', (0, self.num_episodes_todo))
        except ImportError:
            pass
Esempio n. 4
0
def task_nuislog(data_central, id_equiv, id_robot, with_extra=False):
    log_index = data_central.get_log_index()

    add_dummy_robots(data_central)

    # First, look for all episodes of id_robot
    streams = log_index.get_streams_for_robot(id_robot)
    logger.info("Found %d streams for robot %r" % (len(streams), id_robot))
    if len(streams) == 0:
        msg = "No existing logs for %r" % id_robot
        raise UserError(msg)

    # Instance the equiv robot
    config = data_central.get_bo_config()
    equiv = config.robots.instance(id_equiv)

    if not isinstance(equiv, EquivRobot):
        msg = "I expect the robot %r to be an EquivRobot robot " "but found %s." % (id_equiv, describe_type(equiv))
        raise UserError(msg)

    assert isinstance(equiv, EquivRobot)

    if equiv.inner_robot_name != id_robot:
        msg = "The equiv robot inner robot is %r, but you requested %r" % (equiv.inner_robot_name, id_robot)
        raise UserError(msg)

    # FIXME: this is going to work only for 1 nuisance
    for stream in streams:
        logger.info("Converting %r" % stream)
        nuislog_stream(data_central, stream, id_equiv, equiv, with_extra=with_extra)
Esempio n. 5
0
def cmd_clean_states(data_central, argv):
    ''' Cleans agents states. '''
    # TODO: to implement
    parser = OptionParser(prog='clean-states',
                          usage=cmd_clean_states.short_usage)
    parser.disable_interspersed_args()
    parser.add_option("-a", "--agent", dest='agent', help="Agent ID")
    parser.add_option("-r", "--robot", dest='robot', help="Robot ID")
    (options, args) = parser.parse_args(argv)
    check_no_spurious(args)
    check_mandatory(options, ['agent', 'robot'])
    
    
    id_agent = options.agent
    id_robot = options.robot

    db = data_central.get_agent_state_db()

    # TODO: check we know agent and robot?
    
    if not db.has_state(id_agent=id_agent, id_robot=id_robot):
        logger.info('The state has already been cleaned.')
    else:
        logger.info('Cleaning state for %r/%r...' % (id_agent, id_robot))
        db.delete_state(id_agent=id_agent, id_robot=id_robot)
Esempio n. 6
0
def create_video(data_central, id_robot, id_agent,
                 model='boot_log2movie',
                 model_params={},  # {'plotter.zoom': 2},
                 suffix='',
                 id_episode=''):

    logger.info('Creating video for robot: %r agent: %r episode %r' % 
                (id_robot, id_agent, id_episode))

    ds = data_central.get_dir_structure()

    basename = ds.get_video_basename(id_robot=id_robot,
                                     id_agent=id_agent,
                                     id_episode=id_episode)
    if suffix:
        basename += '-%s' % suffix

    logdir = ds.get_simulated_logs_dir()

    # TODO: check has_procgraph
    import procgraph_vehicles  # @UnusedImport
    from procgraph import pg
    # logger.info('Writing to %r.' % basename)
    config = dict(logdir=logdir,
                id_robot=id_robot,
                id_agent=id_agent,
                id_episode=id_episode,
                output=basename,
                **model_params)
    pg(model, config=config, stats=False)

    ds.file_is_done(basename, desc="")
Esempio n. 7
0
def learn_log(data_central, id_agent, id_robot,
              reset=False,
              publish_interval=None,
              publish_once=False,
              interval_save=None,
              interval_print=None,
              episodes=None,
              save_state=True,
              parallel_hint=None,
              live_plugins=[]):
    ''' If episodes is not None, it is a list of episodes id to learn. '''
    from bootstrapping_olympics.library.live_plugins import CompmakeProgress
    from bootstrapping_olympics.library.live_plugins import PrintStatus
    
    logger.info('id_agent: %r\nepisodes:\n%r' % (id_agent, episodes))

    bo_config = data_central.get_bo_config()

    live_plugins = [bo_config.live_plugins.instance(x)
                    for x in live_plugins]
    
    live_plugins.append(CompmakeProgress())
    live_plugins.append(PrintStatus(interval_print))
    # if publish_interval is not None:
    warnings.warn('add publish_interval plugins')
#    if publish_interval is not None:
#    if 0 == state.num_observations % publish_interval:
#        phase = 'learn-active'
#        filename = ds.get_report_filename(id_agent=id_agent, id_robot=id_robot,
#                                          id_state=state.id_state, phase=phase)
# 
#        publish_agent_output(data_central, state, agent, '%05d' % state.num_observations,
#                             filename)


    
    agent0, state0 = load_agent_state(data_central, id_agent=id_agent,
                                    id_robot=id_robot, reset_state=reset)
    
    if parallel_hint is not None:
        logger.info('setting parallel hint: %r' % str(parallel_hint))
        agent0.parallel_process_hint(*parallel_hint)
    
    agent, state = learn_log_base(data_central, id_agent, (agent0, state0),
                                    id_robot, episodes,
                                    live_plugins=[])

    # Saving agent state
    if save_state:
        logger.debug('Saving state (end of streams)')
        state.agent_state = agent.get_state()
        # ds = data_central.get_dir_structure()
        db = data_central.get_agent_state_db()
        db.set_state(state=state, id_robot=id_robot, id_agent=id_agent)
          
    return agent, state
 def file_is_done(self, filename_or_basename, desc=None):  # @UnusedVariable
     """ 
         Notifies that some file is done writing. 
         Used to create a list of recent files that are done.
     """
     path = friendly_path(filename_or_basename)
     if os.path.exists(filename_or_basename):
         size = friendly_filesize(filename_or_basename)
         logger.info('Written %r (%r)' % (path, size))
     else:
         logger.info('Written %r' % (path))
Esempio n. 9
0
    def main(self, agents, robots,
                    explore=None,
                    explore_modulus=None,
                    servo=None,
                    servonav=None,
                    predict=None):

        if predict is None:
            logger.info('No prediction specified.')
            
        if not robots:
            raise SemanticMistake('Please specify at least one robot.')

        if not agents:
            raise SemanticMistake('Please specify at least one agent.')

        for id_robot in robots:
            self.add_task_robot_report(id_robot=id_robot)
            
        if explore_modulus is not None:
            self.add_tasks_explore_modulus(robots, **explore_modulus)
            num_ep_expl = explore_modulus['num_episodes']
            explorer = explore_modulus['explorer']
        # TODO: validate inputs
        if explore is not None:
            for id_robot in robots:
                self.add_tasks_explore(id_robot=id_robot, **explore)
            num_ep_expl = explore['num_episodes']
            explorer = explore['explorer']
            
        for id_robot, id_agent in itertools.product(robots, agents):
            compatible, reason = self.are_compatible(id_robot, id_agent)
            if not compatible:
                logger.info('Avoiding combination %s / %s: %s' % 
                             (id_robot, id_agent, reason))
                continue

            # FIXME: num_ep_expl (should work also for logs)
            self.add_learning(id_robot, id_agent, num_ep_expl=num_ep_expl,
                              explorer=explorer,  # XXX
                              publish_progress=False,
                              save_pickle=True)  # TODO: make param

            if servo is not None:
                self.add_tasks_servo(id_agent=id_agent, id_robot=id_robot,
                                     **servo)

            if servonav is not None:
                self.add_tasks_servonav(id_agent=id_agent, id_robot=id_robot,
                                        **servonav)

            if predict is not None:
                self.add_tasks_predict(id_agent=id_agent, id_robot=id_robot,
                                       **predict)
Esempio n. 10
0
def cmd_learn_log(data_central, argv):
    '''
        Runs the learning for a given agent and log. 
       
       
        Running a live plugin:
        
            bom  [agent/robot options] --reset --dontsave --plugin dummy
        
    '''
    parser = OptionParser(prog='learn-log', usage=cmd_learn_log.short_usage)
    parser.disable_interspersed_args()
    parser.add_option("-a", "--agent", dest='agent', help="Agent ID")
    parser.add_option("-r", "--robot", dest='robot', help="Robot ID")
    parser.add_option("--reset", default=False, action='store_true',
                      help="Do not use cached state.")
    parser.add_option("-p", "--publish", dest='publish_interval', type='float',
                      default=None,
                      help="Publish debug information every N cycles.")
    parser.add_option("--once", default=False, action='store_true',
                      help="Just plot the published information and exit.")
    parser.add_option("--interval_save", type='int', default=300,
                      help="Interval for saving state (seconds) [%default]")
    parser.add_option("--interval_print", type='int', default=5,
                      help="Interval for printing stats (seconds) [%default]")
    parser.add_option("--dontsave", default=False, action='store_true',
                      help="Do not save the state of the agent.")

    parser.add_option("--plugin", default=[],
                      action="append", type="string",
                      help="Run the specified plugin model during "
                           "learning. (eg, visualization)")

    (options, args) = parser.parse_args(argv)

    check_no_spurious(args)
    check_mandatory(options, ['agent', 'robot'])

    if options.publish_interval is  None and not options.once:
        msg = 'Not creating any report; pass -p <interval> or --once to do it.'
        logger.info(msg)

    learn_log(data_central=data_central,
              id_agent=options.agent,
              id_robot=options.robot,
              reset=options.reset,
              publish_interval=options.publish_interval,
              publish_once=options.once,
              interval_save=options.interval_save,
              interval_print=options.interval_print,
              save_state=not(options.dontsave),
              live_plugins=options.plugin)
Esempio n. 11
0
def cmd_list_agents(data_central, argv):
    '''Shows a summary of the agents in the configuration. '''
    parser = OptionParser(prog='list-agents',
                          usage=cmd_list_agents.short_usage)
    parser.disable_interspersed_args()
    parser.add_option("-v", dest='verbose', default=False, action='store_true',
                      help="Show more verbose output.")
    (options, args) = parser.parse_args(argv)

    check_no_spurious(args)

    bo_config = data_central.get_bo_config()
    agents = bo_config.agents
    which = bo_config.agents.keys()  # TODO: selection

    max_len = max(len(x) for x in which)
    formats = '%%%ds: %%s' % (max_len + 1)

    logger.info('I know %d agents:' % len(agents))
    for id_agent in which:
        agent_spec = agents[id_agent]
        logger.info(formats % (id_agent, agent_spec['desc']))

    if options.verbose:
        for id_agent in which:
            agent_spec = agents[id_agent]
            logger.info(pformat(agent_spec))
    else:
        logger.info('Use "-v" to see more information.')
Esempio n. 12
0
    def get_bo_config(self):
        if self.bo_config is None:
            self.bo_config = get_boot_config()
            
            dirs = self.dir_structure.get_config_directories()
            for dirname in dirs:
                if not os.path.exists(dirname):
                    msg = ('Warning, the config dir %r does not exist ' % 
                           friendly_path(dirname))
                    logger.info(msg)  
                else:
                    GlobalConfig.global_load_dir(dirname)
#                     self.bo_config.load(dirname)
        return self.bo_config
Esempio n. 13
0
    def observations(self, observations):
        self.id_episodes.add(observations['id_episode'].item())

        self.num_observations_episode += 1
        self.num_observations += 1
        if self.tracker.its_time():
            msg = ('simulating %d/%d episodes obs %d (%5.1f fps)' % 
                   (self.num_episodes_done,
                    self.num_episodes_todo,
                    self.num_observations, self.tracker.fps()))
            if self.num_episodes_done > 0:
                msg += (' (mean obs/ep: %.1f)' % 
                        (np.mean(self.observations_per_episode)))
            logger.info(msg)
    def update(self, update_data):
        timestamp = update_data["obs"]["timestamp"]
        for k, v in update_data.items():
            if not self.model.is_valid_input_name(k):
                if self.first_time:
                    logger.info("%r: not sending" % k)
            else:
                if self.first_time:
                    logger.info("%r: sending" % k)
                self.model.from_outside_set_input(k, v, timestamp)
        self.first_time = False

        while self.model.has_more():
            self.model.update()
Esempio n. 15
0
    def update(self, up):
        self.cur_stream_observations += 1
        progress = up['progress']
        state = up['state']
        
        if not self.tracker.its_time():
            return

        perc_obs = 100 * (float(progress.obs.done) / progress.obs.target)
        perc_obs_log = 100 * (float(self.cur_stream_observations) / self.stream.get_num_observations())
        msg = ('overall %.2f%% (log %3d%%) (eps: %4d/%d, obs: %4d/%d);'
               ' %5.1f fps' % 
               (perc_obs, perc_obs_log, len(state.id_episodes),
                 progress.eps.target, state.num_observations, progress.obs.target,
                self.tracker.fps()))
        logger.info(msg)
Esempio n. 16
0
def learn_log_base(data_central, id_agent, agent_state, id_robot, episodes,
                   live_plugins=[], ignore_learned=False):
    log_index = data_central.get_log_index()
    agent, state = agent_state
    
    if ignore_learned:
        episodes_learned = set()
    else:
        episodes_learned = state.id_episodes
        
    remain, progress = find_episodes_to_learn(log_index, id_robot,
                                              episodes_to_learn=episodes,
                                              episodes_learned=episodes_learned)

    logger.info('Progress for %r %r:\n%s' % (id_robot, id_agent, progress.summary()))
 
    # Initialize plugins
    init = dict(data_central=data_central, id_agent=id_agent, id_robot=id_robot)
    for plugin in live_plugins:
        plugin.init(init)

    for stream, to_learn in remain:
        
        # plugins
        for plugin in live_plugins:
            plugin.starting_stream(stream)
        
        for obs in stream.read(only_episodes=to_learn):
            state.num_observations += 1
            progress.obs.done += 1

            try:
                agent.process_observations(obs)
            except PassiveAgentInterface.LearningConverged as e:
                logger.info('Learning converged: %s' % e)
                break
            
            # Update plugins
            up = dict(agent=agent, robot=None, obs=obs, progress=progress,
                      state=state, stream=stream)
            for plugin in live_plugins:
                plugin.update(up)

        state.id_episodes.update(to_learn)
        progress.eps.done += len(to_learn)
        
    return agent, state 
Esempio n. 17
0
def bag_read(bag_file, topic, spec, substitute_id_episode, only_episodes=None):
    ''' 
        Reads the log in ROS bag file (only the
        topic given) and converts it to the structure we use internally.
        The ROS2Python object takes care to sanitize the logs;
        it makes lots of checks for data meant to come directly from
        real robots (so delay, wrong formats, etc. are expected. 
    '''
    from ros import rosbag  # @UnresolvedImport
    bag = rosbag.Bag(bag_file)
    
    hints = read_hints(bag_file)
    
    warned = False
    try:
        ros2python = None

        for msg in bag.read_messages(topics=[topic]):
            topic, ros_obs, t = msg  # @UnusedVariable

            if spec is None:
                spec = boot_spec_from_ros_message(ros_obs)
            if ros2python is None:
                bag_read_hints = hints.get('bag_read', {})
                if bag_read_hints:
                    logger.info('bag_read using hints: %s' % bag_read_hints)    
                ros2python = ROS2Python(spec=spec, **bag_read_hints)

            if only_episodes and not ros_obs.id_episode in only_episodes:
                continue

            if ros_obs.id_episode == 'id-episode-not-set':
                if not warned:
                    warned = True
                    # XXX
                    logger.info('Changing id_episode to %s'
                                % substitute_id_episode)
                ros_obs.id_episode = substitute_id_episode

            obs = ros2python.convert(ros_obs)
            if obs is not None:
                yield obs
    except:
        bag.close()
        raise
Esempio n. 18
0
def check_conversions_upper_lower(stream_spec1, nuisance):
    try:
        stream_spec2 = nuisance.transform_spec(stream_spec1)
    except UnsupportedSpec as e:
        logger.info('Skipping %s/%s because incompatible: %s' % 
                    (stream_spec1, nuisance, e))
        return
    
    streamels = stream_spec1.get_streamels() 
    upper = streamels['upper']
    lower = streamels['lower']
    stream_spec1.check_valid_value(lower)
    stream_spec1.check_valid_value(upper)

    upper2 = nuisance.transform_value(upper)
    lower2 = nuisance.transform_value(lower)
    stream_spec2.check_valid_value(upper2)
    stream_spec2.check_valid_value(lower2)
def load_agent_state(data_central, id_agent, id_robot,
                     reset_state=False,
                     raise_if_no_state=False):
    ''' 
        Load the agent, loading the agent state from the state_db directory.
        If the state is not available, then it initializes anew. 
        
        Returns a tuple (agent, state).
    '''
    logger.info('Loading state %s/%s reset=%s ' % (id_agent,
                                                   id_robot, reset_state))
    config = data_central.get_bo_config()
    agent = config.agents.instance(id_agent)  # @UndefinedVariable

    index = data_central.get_log_index()
    has_log = index.has_streams_for_robot(id_robot)
    has_instance = id_robot in config.robots 
    
    if has_log:
        boot_spec = index.get_robot_spec(id_robot)
    elif has_instance:
        robot = config.robots.instance(id_robot)
        boot_spec = robot.get_spec()
    else:
        msg = 'Cannot load agent state for %r ' % id_agent
        msg += 'because I cannot find any streams for robot %r ' % id_robot
        msg += 'and I need them to find the BootSpec. '
        msg += 'Also I could not instance the robot.'
        msg += x_not_found('robot', id_robot, index.list_robots())
        raise UserError(msg)
        
    agent.init(boot_spec)

    return load_agent_state_core(data_central, id_agent, agent, id_robot,
                                 reset_state=reset_state,
                                 raise_if_no_state=raise_if_no_state)
Esempio n. 20
0
def write_robot(index, id_robot, output_dir, skip_existing=True):
    streams = index.robots2streams[id_robot]
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
    
    filename = os.path.join(output_dir, '%s.h5' % id_robot)
    if skip_existing and os.path.exists(filename):
        logger.info('Skipping %s' % filename)
        return
    id_stream = '%s_all' % id_robot
    logger.info('Writing all to %r' % filename)
    
    boot_spec = streams[0].get_spec()
    logs_format = LogsFormat.get_reader_for(filename)
    with logs_format.write_stream(filename=filename,
                                  id_stream=id_stream,
                                  boot_spec=boot_spec) as writer:
        for stream in streams:
            logger.info('- reading stream %s' % stream)
            for observations in stream.read(read_extra=False):
                writer.push_observations(observations)
Esempio n. 21
0
def run_simulation_servonav(
    id_robot,
    robot,
    id_agent,
    agent,
    max_observations,
    max_time,
    id_episode,
    id_environment,
    check_valid_values=True,
    raise_error_on_collision=True,
):
    """ 
        Runs an episode of the simulation. The agent should already been
        init()ed. 
        
        yields robot.get_observations(), boot_observatoions
    """

    keeper = ObsKeeper(boot_spec=robot.get_spec(), id_robot=id_robot)

    obs_spec = robot.get_spec().get_observations()
    cmd_spec = robot.get_spec().get_commands()

    counter = 0
    while True:
        obs = robot.get_observations()

        if check_valid_values:
            obs_spec.check_valid_value(obs.observations)

        #        print('run_simulation_servonav: obs.obs %s' %
        #              (str(obs.observations.shape)))
        observations = keeper.push(
            timestamp=obs.timestamp,
            observations=obs.observations,
            commands=obs.commands,
            commands_source=obs.commands_source,
            id_episode=id_episode,
            id_world=id_environment,
        )
        #        print('then: obs.obs %s' %
        #              (str(observations['observations'].shape)))

        episode_end = obs.episode_end

        yield obs, observations

        now = "step %s" % counter

        if counter >= max_observations:
            logger.info("Finished at %s because %s >= %s" % (now, counter, max_observations))
            break

        if observations["time_from_episode_start"] > max_time:
            logger.info(
                "Finished at %s because of max_time: %s > %s" % (now, observations["time_from_episode_start"], max_time)
            )
            break

        if episode_end:  # Fishy
            msg = "Finished at %s because of robot driver." % now

            if raise_error_on_collision:
                raise Exception(msg)
            else:
                logger.info(msg)
                break

        agent.process_observations(observations)
        commands = agent.choose_commands()

        if check_valid_values:
            cmd_spec.check_valid_value(commands)

        robot.set_commands(commands, id_agent)

        counter += 1
Esempio n. 22
0
def boot_olympics_manager(arguments):
    usage = substitute(usage_pattern, commands_list=commands_list,
                       cmd='boot_olympics_manager')

    parser = OptionParser(prog='boot_olympics_manager', usage=usage)
    parser.disable_interspersed_args()
    parser.add_option("-d", dest='boot_root', default=None,
                      help='Root directory with logs, config, etc. [%default]')

    parser.add_option("-c", dest='extra_conf_dirs', action="append", default=[],
                      help='Adds an extra config dir.')

    parser.add_option("-l", dest='extra_log_dirs', action="append", default=[],
                      help='Adds an extra directory storing logs.')

    parser.add_option("--contracts", default=False, action='store_true',
                      help="Activate PyContracts (disabled by default)")

    parser.add_option("--seterr", dest='seterr', default="warn",
                      help="Sets np.seterr. "
                      "Possible values: ignore, warn, raise, print, log")

    parser.add_option("--profile", default=False, action='store_true',
                      help="Use Python profiler")

    available = LogsFormat.formats.keys()
    parser.add_option("--logformat", dest='log_format',
                      default=BootOlympicsConstants.DEFAULT_LOG_FORMAT,
                      help="Choose format for writing logs in %s. [%%default]"
                        % str(available))

    (options, args) = parser.parse_args(arguments)

    if not args:
        msg = ('Please supply command. Available: %s'
               % ", ".join(Storage.commands.keys()))
        raise UserError(msg)

    cmd = args[0]
    cmd_options = args[1:]

    if not cmd in Storage.commands:
        msg = ('Unknown command %r. Available: %s.' % 
               (cmd, ", ".join(Storage.commands.keys())))
        raise UserError(msg)

    np.seterr(all=options.seterr)
    # underflow is very common in all libraries (e.g. matplotlib)
    np.seterr(under='warn')

    if not options.contracts:
        contracts.disable_all()

    if options.boot_root is None:
        options.boot_root = DirectoryStructure.DEFAULT_ROOT
        logger.info('Using %r as default root directory '
                    '(use -d <dir> to change)' % options.boot_root)


    data_central = DataCentral(options.boot_root)
    
    GlobalConfig.global_load_dir('default')  # need skins
    for dirname in options.extra_conf_dirs:
        GlobalConfig.global_load_dir(dirname)
    
    dir_structure = data_central.get_dir_structure() 
    dir_structure.set_log_format(options.log_format)
    for dirname in options.extra_log_dirs:
        dir_structure.add_log_directory(dirname)

    def go():
        return Storage.commands[cmd](data_central, cmd_options)

    if not options.profile:
        go()
    else:
        logger.warning('Note: the profiler does not work when using '
                       'parallel execution. (use "make" instead of "parmake").')
        import cProfile
        cProfile.runctx('go()', globals(), locals(), 'bom_prof')
        import pstats
        p = pstats.Stats('bom_prof')
        p.sort_stats('cumulative').print_stats(30)
        p.sort_stats('time').print_stats(30)
Esempio n. 23
0
def servoing_episode(id_robot, robot,
                     id_servo_agent, servo_agent,
                     writer, id_episode,
                     displacement,
                     max_episode_len,
                     save_robot_state,
                     converged_dist_t_m=0.1,
                     converged_dist_th_deg=1,
                     max_tries=10000):
    '''
    
        :arg:displacement: Time in seconds to displace the robot.
    '''
    from geometry import SE3

    def mean_observations(n=10):
        obss = []
        for _ in range(n):  # XXX: fixed threshold
            obss.append(robot.get_observations().observations)
        return np.mean(obss, axis=0)

    def robot_pose():
        return robot.get_observations().robot_pose

    def timestamp():
        return robot.get_observations().timestamp

    def episode_ended():
        return robot.get_observations().episode_end

    def simulate_hold(cmd0, displacement):
        t0 = timestamp()
        nsteps = 0
        while timestamp() < t0 + displacement:
            nsteps += 1
            source = BootOlympicsConstants.CMD_SOURCE_SERVO_DISPLACEMENT
            robot.set_commands(cmd0, source)
            if episode_ended():
                logger.debug('Collision after %d steps' % ntries)
                return False

        logger.debug('%d steps of simulation to displace by %s' % 
                    (nsteps, displacement))
        return True

    for ntries in xrange(max_tries):
        # iterate until we can do this correctly
        episode = robot.new_episode()
        obs0 = mean_observations()
        cmd0 = robot.get_spec().get_commands().get_random_value()
        pose0 = robot_pose()
        ok = simulate_hold(cmd0, displacement)
        if ok:
            pose1 = robot_pose()
            logger.info('Displacement after %s tries.' % ntries)
            break
    else:
        msg = 'Could not do the displacement (%d tries).' % max_tries
        raise Exception(msg)

    servo_agent.set_goal_observations(obs0)

    for robot_observations, boot_observations in \
        run_simulation_servo(id_robot, robot,
                           id_servo_agent, servo_agent,
                           100000, max_episode_len,
                           id_episode=id_episode,
                           id_environment=episode.id_environment):

        def pose_to_yaml(x):
            ''' Converts to yaml, or sets None. '''
            if x is None:
                return None
            else:
                return SE3.to_yaml(x)

        extra = {}

        sensels_list = boot_observations['observations'].tolist()
        extra['servoing_base'] = dict(goal=obs0.tolist(), current=sensels_list)

        current_pose = robot_observations.robot_pose
        has_pose = current_pose is not None

        if has_pose:
            # Add extra pose information

            extra['servoing_poses'] = dict(goal=pose_to_yaml(pose0),
                                           current=pose_to_yaml(current_pose))

            delta = SE2_from_SE3(SE3.multiply(SE3.inverse(current_pose),
                                              pose0))
            dist_t_m = np.linalg.norm(translation_from_SE2(delta))
            dist_th_deg = np.abs(angle_from_SE2(delta))

            # TODO: make it not overlapping
            extra['servoing'] = dict(obs0=obs0.tolist(),
                                    pose0=pose_to_yaml(pose0),
                                    poseK=pose_to_yaml(current_pose),
                                    obsK=sensels_list,
                                    displacement=displacement,
                                    cmd0=cmd0.tolist(),
                                    pose1=pose_to_yaml(pose1))

        if save_robot_state:
            extra['robot_state'] = robot.get_state()

        writer.push_observations(observations=boot_observations,
                                 extra=extra)

        if has_pose:
            if ((dist_t_m <= converged_dist_t_m) and
                (dist_th_deg <= converged_dist_th_deg)):
                print('Converged!')
                break
        else:
            # TODO: write convergence criterion
            # without pose information
            pass
Esempio n. 24
0
def find_episodes_to_learn(log_index, id_robot, episodes_to_learn=None, episodes_learned=None):
    """ 
        Returns tuples of the kind:
        
            (id_stream,  episodes_to_learn), progress
    
    """
    logger.info('Finding episodes for %s' % id_robot)
    logger.info('To learn: %s' % episodes_to_learn)
    logger.info('Learned:  %s' % episodes_learned)
    
    if not log_index.has_streams_for_robot(id_robot):
        msg = ('No log for robot %r found.\nI have logs for: %s.'
               % (id_robot, ", ".join(log_index.robots2streams.keys())))
        dirnames = log_index.get_indexed_dirs()
        msg += '\nIndexed directories:\n'
        msg += '\n'.join('- %s' % d for d in dirnames)
        raise ValueError(msg)


    if episodes_learned is None:
        episodes_learned = set()
        
    if episodes_to_learn is None:
        episodes_to_learn = log_index.get_episodes_for_robot(id_robot)
 
    p = Progress()
 
    episodes_found = set()
    
    remain = []
    for stream in log_index.get_streams_for_robot(id_robot):
        to_learn_in_stream = []
        for episode_summary in stream.get_episodes():
            id_episode = episode_summary.get_id_episode()
            episodes_found.add(id_episode)
            
            ep_length = episode_summary.get_length()
            ep_nobs = episode_summary.get_num_observations()
            
            p.len.total += ep_length
            p.eps.total += 1
            p.obs.total += ep_nobs
        
            target = id_episode in episodes_to_learn
            done = id_episode in episodes_learned
            
            if target:
                p.len.target += ep_length
                p.eps.target += 1
                p.obs.target += ep_nobs
            
                if done:
                    p.len.done += ep_length
                    p.eps.done += 1
                    p.obs.done += ep_nobs
                else:                
                    p.len.todo += ep_length
                    p.eps.todo += 1
                    p.obs.todo += ep_nobs
                    
            if target and not done:
                to_learn_in_stream.append(id_episode)
        
        if to_learn_in_stream:
            remain.append((stream, to_learn_in_stream))

            
    # check to see that we found all episodes we needed to learn
    for e in episodes_to_learn:
        if not e in episodes_found:
            msg = 'I could not find the episode %r.' % e
            raise ValueError(msg)

    return remain, p
Esempio n. 25
0
def check_logs_formats(id_agent, agent, id_robot, robot):  # @UnusedVariable

    with create_tmp_dir() as root:
        os.mkdir(os.path.join(root, 'config'))
        data_central = DataCentral(root)

        # Simulate two episodes
        # NO! there is a bug in bag reading; the messages are read
        # in timestamp order; and for now different episodes can
        # have overlapping timestamps
        try:
            simulate(data_central, id_agent=id_agent, id_robot=id_robot,
                 max_episode_len=2,
                 num_episodes=1,  # changed from 2 (see above)
                 cumulative=False,
                 id_episodes=None,
                 stateful=False,
                 interval_print=None,
                 write_extra=True)
        except UnsupportedSpec:
            return

        log_index = data_central.get_log_index()
        log_index.reindex()

        streams = log_index.get_streams_for(id_robot, id_agent)
        if len(streams) != 1:
            msg = 'Expected to find 1 stream, not %d' % len(streams)
            raise Exception(msg)

        stream_orig = streams[0]

        for logs_format, interface in LogsFormat.formats.items():
            try:
                dirname = os.path.join(root, logs_format)
                safe_makedirs(dirname)
                filename = os.path.join(dirname, 'example.%s' % logs_format)
                written = []
                id_stream = 'example'
                with interface.write_stream(filename, id_stream,
                                            robot.get_spec()) as writer:
                    for observations in stream_orig.read():
                        logger.info('Writing %s:%s (%s)' % 
                              (observations['id_episode'],
                               observations['counter'],
                               observations['timestamp']))
                        writer.push_observations(observations)
                        written.append(observations)

                count = 0
                for obs_read in interface.read_from_stream(filename,
                                                           id_stream):
                    logger.info('Reading %s:%s (%s)' % 
                          (obs_read['id_episode'],
                           obs_read['counter'],
                           obs_read['timestamp']))

                    original = written[count]

                    try:
                        if obs_read['counter'] != original['counter']:
                            msg = ('Not even the counter is the same!'
                                   ' %s vs %s' % 
                                   (obs_read['counter'], original['counter']))
                            raise Exception(msg)

                        assert_allclose(obs_read['timestamp'],
                                        original['timestamp'])
                        assert_allclose(obs_read['observations'],
                                        original['observations'])
                        assert_allclose(obs_read['commands'],
                                        original['commands'])
                    except:
                        logger.error('Error at count = %d' % count)
                        logger.error('  original: %s' % original)
                        logger.error('  obs_read: %s' % obs_read)
                        raise
                    count += 1

                if count != len(written):
                    msg = ('I wrote %d entries, but obtained %d.' % 
                           (len(written), count))
                    raise Exception(msg)
            except:
                logger.error('Could not pass tests for format %r.'
                             % logs_format)
                raise
Esempio n. 26
0
def servonav_episode(
    id_robot,
    robot,
    id_servo_agent,
    servo_agent,
    writer,
    id_episode,
    max_episode_len,
    save_robot_state,
    interval_write=1,
    interval_print=5,
    resolution=0.5,  # grid resolution
    delta_t_threshold=0.2,  # when to switch
    MIN_PATH_LENGTH=8,
    MAX_TIME_FOR_SWITCH=20.0,
    fail_if_not_working=False,
    max_tries=10000,
):
    """
    
        :arg:displacement: Time in seconds to displace the robot.
    """
    from geometry import SE2_from_SE3, translation_from_SE2, angle_from_SE2, SE3

    stats_write = InAWhile(interval_print)

    # Access the vehicleSimulation interface
    vsim = get_vsim_from_robot(robot)

    for _ in xrange(max_tries):
        # iterate until we can do this correctly
        episode = robot.new_episode()
        locations = get_grid(robot=robot, vsim=vsim, resolution=resolution)

        if len(locations) < MIN_PATH_LENGTH:
            logger.info("Path too short, trying again.")
        else:
            break

    else:
        msg = "Could not find path in %d tries." % max_tries
        raise Exception(msg)

    locations_yaml = convert_to_yaml(locations)

    vsim.vehicle.set_pose(locations[0]["pose"])

    current_goal = 1
    current_goal_obs = locations[current_goal]["observations"]
    servo_agent.set_goal_observations(current_goal_obs)

    counter = 0
    time_last_switch = None

    num_written = 0
    for robot_observations, boot_observations in run_simulation_servonav(
        id_robot,
        robot,
        id_servo_agent,
        servo_agent,
        100000,
        max_episode_len,
        id_episode=id_episode,
        id_environment=episode.id_environment,
        raise_error_on_collision=fail_if_not_working,
    ):

        current_time = boot_observations["timestamp"].item()
        if time_last_switch is None:
            time_last_switch = current_time

        time_since_last_switch = float(current_time - time_last_switch)

        def obs_distance(obs1, obs2):
            return float(np.linalg.norm(obs1.flatten() - obs2.flatten()))

        curr_pose = robot_observations.robot_pose
        curr_obs = boot_observations["observations"]
        curr_goal = locations[current_goal]["observations"]
        prev_goal = locations[current_goal - 1]["observations"]
        curr_err = obs_distance(curr_goal, curr_obs)
        prev_err = obs_distance(prev_goal, curr_obs)
        current_goal_pose = locations[current_goal]["pose"]
        current_goal_obs = locations[current_goal]["observations"]

        delta = SE2_from_SE3(SE3.multiply(SE3.inverse(curr_pose), current_goal_pose))
        delta_t = np.linalg.norm(translation_from_SE2(delta))
        delta_th = np.abs(angle_from_SE2(delta))

        if stats_write.its_time():
            msg = "  deltaT: %.2fm  deltaTh: %.1fdeg" % (delta_t, np.rad2deg(delta_th))
            logger.debug(msg)

        # If at the final goal, go closer
        is_final_goal = current_goal == len(locations) - 1
        if is_final_goal:
            delta_t_threshold *= 0.3

        # TODO: should we care also about delta_th?
        time_to_switch = (delta_t < delta_t_threshold) or (time_since_last_switch > MAX_TIME_FOR_SWITCH)
        # does not work: curr_err < SWITCH_THRESHOLD * prev_err:

        if time_to_switch:
            current_goal += 1
            logger.info("Switched to goal %d." % current_goal)

            time_last_switch = current_time
            if current_goal >= len(locations):
                # finished
                logger.info("Finished :-)")
                break

        threshold_lost_m = 3
        if delta_t > threshold_lost_m:
            msg = "Breaking because too far away."
            if not (fail_if_not_working):
                logger.error(msg)
                break
            else:
                raise Exception(msg)

        servo_agent.set_goal_observations(current_goal_obs)

        extra = {}
        extra["servoing_base"] = dict(goal=curr_goal.tolist(), current=curr_obs.tolist())

        extra["servoing_poses"] = dict(goal=SE3.to_yaml(current_goal_pose), current=SE3.to_yaml(curr_pose))

        extra["servonav"] = dict(
            poseK=SE3.to_yaml(curr_pose),
            obsK=boot_observations["observations"].tolist(),
            pose1=SE3.to_yaml(current_goal_pose),
            locations=locations_yaml,
            current_goal=current_goal,
            curr_err=curr_err,
            prev_err=prev_err,
            time_last_switch=time_last_switch,
            time_since_last_switch=time_since_last_switch,
        )

        if counter % interval_write == 0:
            if save_robot_state:
                extra["robot_state"] = robot.get_state()

            writer.push_observations(observations=boot_observations, extra=extra)
            num_written += 1
        counter += 1

    if num_written == 0:
        msg = "This log was too short to be written (%d observations)" % counter
        raise Exception(msg)
Esempio n. 27
0
def task_servonav(
    data_central,
    id_agent,
    id_robot,
    max_episode_len,
    num_episodes,
    fail_if_not_working,
    id_episodes=None,  # if None, just use the ID given by the world
    cumulative=False,
    interval_print=None,
    interval_write=10,  # write every 10 frames
    num_episodes_with_robot_state=0,
    resolution=1,
):
    """ Returns the list of the episodes IDs simulated. """

    # Reseed the generator (otherwise multiprocessing will use the same)
    np.random.seed()

    if id_episodes is not None:
        if len(id_episodes) != num_episodes:
            raise ValueError("Expected correct number of IDs.")

    # Instance robot object
    robot = data_central.get_bo_config().robots.instance(id_robot)

    # TODO: check that this is a Vehicles simulation

    boot_spec = robot.get_spec()

    # Instance agent object
    agent, _ = load_agent_state(data_central, id_agent, id_robot, reset_state=False, raise_if_no_state=True)
    # TODO: check servo
    servo_agent = agent.get_servo()
    id_agent_servo = "%s_servo" % id_agent

    ds = data_central.get_dir_structure()
    id_stream = "%s_%s_%s_servonav" % (id_robot, id_agent, unique_timestamp_string())
    filename = ds.get_simlog_filename(id_robot=id_robot, id_agent=id_agent, id_stream=id_stream)
    logger.info("Creating stream %r\n in file %r" % (id_stream, filename))

    logs_format = LogsFormat.get_reader_for(filename)

    bk = BookkeepingServo(
        data_central=data_central,
        id_robot=id_robot,
        id_agent=id_agent_servo,
        num_episodes=num_episodes,
        cumulative=cumulative,
        interval_print=interval_print,
    )

    if not bk.another_episode_todo():
        return

    with logs_format.write_stream(filename=filename, id_stream=id_stream, boot_spec=boot_spec) as writer:
        counter = 0
        while bk.another_episode_todo():
            episode = robot.new_episode()

            if id_episodes is not None:
                id_episode = id_episodes.pop(0)
            else:
                id_episode = episode.id_episode

            save_robot_state = counter < num_episodes_with_robot_state

            servonav_episode(
                id_robot=id_robot,
                robot=robot,
                id_servo_agent=id_agent_servo,
                servo_agent=servo_agent,
                writer=writer,
                id_episode=id_episode,
                resolution=resolution,
                max_episode_len=max_episode_len,
                save_robot_state=save_robot_state,
                interval_write=interval_write,
                fail_if_not_working=fail_if_not_working,
                max_tries=10000,
            )

            bk.episode_done()
            counter += 1
Esempio n. 28
0
 def add_tasks_explore_modulus(self, robots, episodes_per_tranche=50, **explore_args):
     """ 
         Adds exploration tasks, making sure to use the same 
         data for robots up to nuisances. 
     """
     # ID robot -> (obsn, original, cmdn)
     robot2canonical = {}
     
     # (original, cmd) -> list(str)
     from collections import defaultdict
     core2robots = defaultdict(list)
     
     for id_robot in robots:
         canform = self.get_robot_modulus(id_robot)
         robot2canonical[id_robot] = canform
         _, original, cmdn = canform
         core2robots[(original, cmdn)].append(id_robot)
         
         logger.info('Canonical form of %r: %s' % 
                     (id_robot, robot2canonical[id_robot]))
         
     config = self.data_central.get_bo_config()
     
     for original, cmd in core2robots:
         derived = core2robots[(original, cmd)]
         logger.info('Core robot (%s,%s) corresponds to %s' % 
                     (original, cmd, derived))
         
         # XXX: not sure of order
         new_robot_name = "".join(['U%s' % x for x in cmd]) + original
         if not new_robot_name in config.robots:
             msg = 'Bug found, or bad naming practices.'
             msg += 'I want to instantiate a %r but not found.' % new_robot_name
             raise Exception(msg)
         
         explore_args['episodes_per_tranche'] = episodes_per_tranche
         id_episodes = self.add_tasks_explore(new_robot_name, **explore_args)
         logger.info('Defined %d episodes for %r.' % (len(id_episodes), new_robot_name))
         
         # Now convert one episode into the other
         for id_derived in derived:
             logger.info('Considering derived %s ' % id_derived)
             derived_obs, x, derived_cmd = robot2canonical[id_derived]
             assert x == original
             assert derived_cmd == cmd
             if not derived_obs:
                 logger.info(' ... skipping because pure.')
                 # this is a pure robot
                 continue
             
             episodes_tranches = self.get_tranches(id_episodes,
                                                   episodes_per_tranche)
             
             for i, tranche in enumerate(episodes_tranches):
                 job_id = 'derive-%s-%d' % (id_derived, i)
                 
                 extra_dep = []
                 for id_episode in tranche:
                     extra_dep.append(self.dep_episode_done(new_robot_name,
                                                            id_episode))
                     
                 job = self.compmake_job(nuislog_episodes,
                                         data_central=self.data_central,
                                         id_robot_original=new_robot_name,
                                         id_episodes=tranche,
                                         id_equiv=id_derived,
                                         obs_nuisances=derived_obs,
                                         cmd_nuisances=[],  # <- correct
                                         with_extras=True,
                                         job_id=job_id,
                                         extra_dep=extra_dep)
                 
                 for id_episode in tranche:
                     self.set_dep_episode_done(id_robot=id_derived,
                                               id_episode=id_episode, job=job)
Esempio n. 29
0
def simulate_agent_robot(
    data_central,
    id_agent,
    id_robot,
    max_episode_len,
    num_episodes,
    cumulative,
    id_episodes=None,  # if None, just use the ID given by the world
    stateful=False,
    interval_print=None,
    write_extra=True,
):
    """ If not cumulative, returns the list of the episodes IDs simulated,
        otherwise it returns all episodes. """

    # Reseed the generator (otherwise multiprocessing will use the same)
    np.random.seed()

    if id_episodes is not None:
        if len(id_episodes) != num_episodes:
            raise ValueError("Expected correct number of IDs.")

    # Instance agent object
    config = data_central.get_bo_config()
    agent = config.agents.instance(id_agent)  # @UndefinedVariable
    # Instance robot object
    robot = config.robots.instance(id_robot)  # @UndefinedVariable

    #     logger = logging.getLogger("BO:%s(%s)" % (id_agent, id_robot))
    #     logger.setLevel(logging.DEBUG)
    #     AgentInterface.logger = logger  # XXX

    boot_spec = robot.get_spec()

    # If --stateful is passed, we try to load a previous state.
    if stateful:
        db = data_central.get_agent_state_db()
        if db.has_state(id_agent=id_agent, id_robot=id_robot):
            logger.info("Using previous state.")
            db.reload_state_for_agent(id_agent=id_agent, id_robot=id_robot, agent=agent)
        else:
            logger.info("No previous state found.")
            agent.init(boot_spec)
    else:
        agent.init(boot_spec)

    ds = data_central.get_dir_structure()
    timestamp = unique_timestamp_string()
    timestamp = timestamp.replace("_", "")
    id_stream = "%s-%s-%s" % (id_robot, id_agent, timestamp)
    filename = ds.get_simlog_filename(id_robot=id_robot, id_agent=id_agent, id_stream=id_stream)
    logger.info("Creating stream %r\n in file %r" % (id_stream, filename))

    logs_format = LogsFormat.get_reader_for(filename)

    bk = Bookkeeping(
        data_central=data_central,
        id_robot=id_robot,
        num_episodes=num_episodes,
        cumulative=cumulative,
        interval_print=interval_print,
    )

    if bk.another_episode_todo():
        with logs_format.write_stream(filename=filename, id_stream=id_stream, boot_spec=boot_spec) as writer:
            while bk.another_episode_todo():
                if id_episodes is not None:
                    id_episode = id_episodes.pop(0)
                    logger.info("Simulating episode %s" % id_episode)
                else:
                    id_episode = None

                for observations in run_simulation(
                    id_robot, robot, id_agent, agent, 100000, max_episode_len, id_episode=id_episode
                ):
                    bk.observations(observations)
                    if write_extra:
                        extra = dict(robot_state=robot.get_state())
                    else:
                        extra = {}
                    writer.push_observations(observations=observations, extra=extra)

                bk.episode_done()
            logger.info("Peacefully done all episodes")
    else:
        logger.warn("No episodes to do?")

    logger.info("done")
    if cumulative:
        return bk.get_all_episodes()
    else:
        return bk.get_id_episodes()
Esempio n. 30
0
def check_conversions(stream_spec1, nuisance):
    # print('Checking %s / %s ' % (stream_spec1, nuisance))
    nuisance_inv = None
    try:
        try:
            stream_spec2 = nuisance.transform_spec(stream_spec1)
        except UnsupportedSpec as e:
            logger.info('Skipping %s/%s because incompatible: %s' % 
                        (stream_spec1, nuisance, e))
            return

        value1 = stream_spec1.get_random_value()
        stream_spec1.check_valid_value(value1)


        value2 = nuisance.transform_value(value1)
        stream_spec2.check_valid_value(value2)

        try:
            nuisance_inv = nuisance.inverse()
        except NuisanceNotInvertible as e:
            logger.info('Skipping some tests %s/%s because not invertible:'
                        ' %s' % (stream_spec1, nuisance, e))
            return

        try:
            stream_spec1b = nuisance_inv.transform_spec(stream_spec2)
        except UnsupportedSpec as e:
            msg = ('The inverse of the nuisance does not seem to be able '
                   'to handle the result:\n%s\n\n'
                   '  stream_spec1: %s\n'
                   '      nuisance: %s\n'
                   '  stream_spec2: %s\n'
                   '  nuisance_inv: %s\n' % 
                   (indent(str(e), '>'),
                    stream_spec1.to_yaml(), nuisance,
                    stream_spec2.to_yaml(), nuisance_inv))
            raise ValueError(msg)

        try:
            StreamSpec.check_same_spec(stream_spec1, stream_spec1b)
        except Exception as e:
            msg = ('The inverse of the nuisance does not recreate the '
                   'initial spec:\n%s\n\n'
                   '  stream_spec1: %s\n'
                   '      nuisance: %s\n'
                   '  stream_spec2: %s\n'
                   '  nuisance_inv: %s\n'
                   ' stream_spec1b: %s\n' % 
                   (indent(str(e), '>'),
                    stream_spec1.to_yaml(), nuisance,
                    stream_spec2.to_yaml(), nuisance_inv,
                    stream_spec1b.to_yaml()))
            raise ValueError(msg)

        value1b = nuisance_inv.transform_value(value2)
        stream_spec1.check_valid_value(value1b)

        # TODO: if exact
        assert_allclose(value1, value1b, rtol=1e-5)

    except:
        logger.error('Error while testing:')
        logger.error(' stream_spec:  %s ' % stream_spec1.to_yaml())
        logger.error(' nuisance:     %s' % nuisance)
        logger.error(' nuisance_inv: %s' % nuisance_inv)
        raise