<commit_before>"""
Provides the class PatternCoordinator and a family of FeatureCoordinator classes.

PatternCoordinator creates a set of pattern generators whose parameters are
related in some way, as controlled by a subclass of FeatureCoordinator.
"""

import os
import math
import json
import glob
import collections
import copy

import param
from param.parameterized import ParamOverrides

from imagen.patterngenerator import PatternGenerator
from imagen.image import FileImage
from imagen import Gaussian, Composite, Selector, CompositeBase

import numbergen

class FeatureCoordinator(param.ParameterizedFunction):
    """
    A FeatureCoordinator modifies a supplied PatternGenerator.

    The modification can depend on the string pattern_label and
    pattern_number supplied, in order to coordinate a set of patterns
    of the same type with systematic differences.

    FeatureCoordinators that introduce randomness should be seeded
    with a value based on the supplied master_seed, so that an
    entire set of patterns can be controlled with the one master_seed
    value.

    Subclasses of this class can accept parameters provided in params.

    This superclass ensures a common interface across all
    FeatureCoordinator subclasses, which is necessary because they are
    usually stored in a list, with each item called the same way.
    """

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        """
        'pattern' is the PatternGenerator to be modified
        'pattern_label' is the name to be given to this
        PatternGenerator, used to select different behaviors
        'pattern_number' is an integer value distinguishing between
        multiple patterns with the same pattern_label
        'master_seed' is to be used for any random number generator
        seeds used for this pattern
        'params' consists of optional keyword-value pairs to be
        provided for subclasses' parameters
        """
        raise NotImplementedError

class XCoordinator(FeatureCoordinator):
    """
    Chooses a random value for the x coordinate, subject to the
    provided position_bound_x.
    """

    position_bound_x = param.Number(default=0.8,doc="""
        Left/rightmost position of the pattern center on the x axis.""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        new_pattern=copy.copy(pattern)
        new_pattern.x = pattern.get_value_generator('x')+\
        numbergen.UniformRandom(lbound=-p.position_bound_x,
                                ubound=p.position_bound_x,
                                seed=master_seed+12+pattern_number,
                                name="XCoordinator"+str(pattern_number))
        return new_pattern

class YCoordinator(FeatureCoordinator):
    """
    Chooses a random value for the y coordinate, subject to the
    provided position_bound_y.
    """

    position_bound_y = param.Number(default=0.8,doc="""
        Upper/lowermost position of the pattern center on the y axis.""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        new_pattern=copy.copy(pattern)
        new_pattern.y = pattern.get_value_generator('y')+\
            numbergen.UniformRandom(lbound=-p.position_bound_y,
                                    ubound=p.position_bound_y,
                                    seed=master_seed+35+pattern_number,
                                    name="YCoordinator"+str(pattern_number))
        return new_pattern

class OrientationCoordinator(FeatureCoordinator):
    """
    Chooses a random orientation within the specified
    orientation_bound in each direction.
    """

    orientation_bound = param.Number(default=math.pi,doc="""
        Rotate pattern around the origin by at most orientation_bound radians (in both directions).""")

    align_orientations = param.Boolean(default=False, doc="""
        Whether or not to align pattern orientations when composing
        multiple patterns together.

        Alignment may be useful to prevent crossing stimuli or may be
        appropriate for moving patterns sweeped in a single direction
        of motion.  """)

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        new_pattern=copy.copy(pattern)
        new_pattern.orientation = pattern.get_value_generator('orientation')+\
            numbergen.UniformRandom(lbound=-p.orientation_bound,
                                    ubound=p.orientation_bound,
                                    seed=master_seed+21+(0 if p.align_orientations else pattern_number),
                                    name=("OrientationCoordinator"
                                          + ('' if p.align_orientations else str(pattern_number)))
                                )
        return new_pattern

class PatternCoordinator(param.Parameterized):
    """
    Returns a set of coordinated PatternGenerators, named according to
    pattern_labels.

    The features to be modified are specified with the
    features_to_vary parameter. A feature is something coordinated
    between the PatternGenerators, either:

    a. one of the existing parameters of the PatternGenerators
       (such as size), or
    b. a variable from which values for one of the existing parameters
       can be calculated (such as a position offset between two
       PatternGenerators), or
    c. a value inherent to a particular existing image dataset
       (due to how the dataset was collected or generated).

    Each PatternGenerator is first instantiated with the supplied
    pattern_parameters, and then subclasses of FeatureCoordinator are
    applied sequentially to modify the specified or default parameter
    values of each PatternGenerator.
    """

    pattern_type = param.ClassSelector(PatternGenerator,default=Gaussian,is_instance=False,doc="""
        PatternGenerator type to be used.""")

    pattern_parameters = param.Dict(default={'size': 0.088388, 'aspect_ratio': 4.66667},doc="""
        Parameter values to be passed to the PatternGenerator specified in pattern_type.""")

    patterns_per_label = param.Integer(default=2,doc="""
        Number of patterns to generate and combine for a given label.""")

    features_to_vary = param.List(default=['xy','or'],class_=str,doc="""
        Stimulus features that the caller wishes to be varied, such as:
          :'xy': Position in x and y coordinates
          :'or': Orientation

        Subclasses and callers may extend this list to include any
        other features for which a coordinator has been defined in
        feature_coordinators.""")

    pattern_labels = param.List(default=['Input'],class_=str,bounds=(1,None),doc="""
        For each string in this list, a PatternGenerator of the
        requested pattern_type will be returned, with parameters whose
        values may depend on the string label supplied. For instance,
        if the list ["Pattern1","Pattern2"] is supplied, a metafeature
        function might inspect those pattern_labels and set parameters
        differently for Pattern1 and Pattern2, returning two different
        PatternGenerators with those pattern_labels.""")

    master_seed = param.Integer(default=0,doc="""
        Base seed for all pattern parameter values. Each numbered
        pattern on each of the various pattern_labels will normally
        use a different random seed, but all of these seeds should
        include this master_seed value, so that changing it will
        change all of the random pattern parameter streams.""")

    composite_type = param.ClassSelector(CompositeBase,default=Composite,is_instance=False,doc="""
        Class that combines the patterns_per_label individual patterns
        and creates a single combined pattern that it returns for a
        given label.  For instance, imagen.Composite can merge the
        individual patterns into a single pattern using a variety of
        operators like add or maximum, while imagen.Selector can
        choose one out of a given set of patterns.""")

    composite_parameters = param.Dict(default={},doc="""
        If present, these parameter values will be passed to the composite specified in composite_type.""")

    feature_coordinators = param.Dict(default=collections.OrderedDict([
        ('xy', [XCoordinator,YCoordinator]),
        ('or', OrientationCoordinator)]),doc="""
        Mapping from the feature name (key) to the method(s) to be
        applied to the pattern generators.  The value can either be a
        single method or a list of methods.""")

    def _create_patterns(self, properties=None):
        """
        Return a list (of length patterns_per_label) of
        PatternGenerator instances.  Should use pattern_type and
        pattern_parameters to create each pattern.

        properties is a dictionary, e.g. {'pattern_label':
        pattern_label}, which can be used to create PatternGenerators
        depending on the requested pattern_label
        """
        return [self.pattern_type(**self.pattern_parameters) for i in range(self.patterns_per_label)]

    def __init__(self,inherent_features=[],**params):

        """
        If a dataset already and inherently includes certain features,
        a list with the inherent feature names should be supplied.

        Any extra parameter values supplied here will be passed down
        to the feature_coordinators requested in features_to_vary.
        """
        p=ParamOverrides(self,params,allow_extra_keywords=True)

        super(PatternCoordinator, self).__init__(**p.param_keywords())

        self._feature_params = p.extra_keywords()

        self._inherent_features = inherent_features

        # TFALERT: Once spatial frequency (sf) is added, this will
        # cause warnings, because all image datasets will have a
        # spatial frequency inherent feature, but mostly we just
        # ignore that by having only a single size of DoG, which
        # discards all but a narrow range of sf.  So the dataset will
        # have sf inherently, but that won't be an error or even
        # worthy of a warning.
        if(len(set(self._inherent_features) - set(self.features_to_vary))):
            self.warning('Inherent feature present which is not requested in features')

        self._feature_coordinators_to_apply = []
        for feature, feature_coordinator in self.feature_coordinators.items():
            if feature in self.features_to_vary and feature not in self._inherent_features:
                # if it is a list, append each list item individually
                if isinstance(feature_coordinator,list):
                    for individual_feature_coordinator in feature_coordinator:
                        self._feature_coordinators_to_apply.append(individual_feature_coordinator)
                else:
                    self._feature_coordinators_to_apply.append(feature_coordinator)

    def __call__(self):
        coordinated_pattern_generators={}
        for pattern_label in self.pattern_labels:
            patterns=self._create_patterns({'pattern_label': pattern_label})

            # Apply _feature_coordinators_to_apply
            for i in range(len(patterns)):
                for fn in self._feature_coordinators_to_apply:
                    patterns[i]=fn(patterns[i],pattern_label,i,self.master_seed,**self._feature_params)

            combined_patterns=self.composite_type(generators=patterns,**self.composite_parameters)
            coordinated_pattern_generators.update({pattern_label:combined_patterns})
        return coordinated_pattern_generators

class PatternCoordinatorImages(PatternCoordinator):
    pattern_type = param.ClassSelector(PatternGenerator,default=FileImage,is_instance=False)

    pattern_parameters = param.Dict(default={'size': 10})

    composite_type = param.ClassSelector(CompositeBase,default=Selector,is_instance=False)

    def __init__(self,dataset_name,**params):
        """
        dataset_name is the path to a folder containing a
        MANIFEST_json (https://docs.python.org/2/library/json.html),
        which contains a description for a dataset.  If no
        MANIFEST_json is present, all image files in the specified
        folder are used.

        Any extra parameter values supplied here will be passed down
        to the feature_coordinators requested in features_to_vary.

        The JSON file can contain any of the following entries, if an
        entry is not present, the default is used:

            :'dataset_name': Name of the dataset (string,
            default=filepath)
            :'length': Number of images in the dataset (integer,
            default=number of files in directory matching
            filename_template)
            :'description': Description of the dataset (string,
            default="")
            :'source': Citation of paper for which the dataset was
            created (string, default=name)
            :'filename_template': Path to the images with placeholders
            ({placeholder_name}) for inherent features and the image
            number, e.g. "filename_template": "images/image{i}.png".
            The placeholders are replaced according to
            placeholder_mapping.  Alternatively, glob patterns such as
            * or ? can be used, e.g. "filename_template":
            "images/*.png" (default=path_to_dataset_name/*.*)
            :'placeholder_mapping': Dictionary specifying the
            replacement of placeholders in filename_template; value is
            used in eval() (default={}).
            :'inherent_features': Features for which the corresponding
            feature_coordinators should not be applied
            (default=['sf','or','cr'])

            Currently, the label of the pattern generator
            ('pattern_label') as well as the image number
            ('current_image') are given as parameters to each callable
            supplied in placeholder_mapping, where current_image
            varies from 0 to length-1 and pattern_label is one of the
            items of pattern_labels.
            (python code, default={'i': lambda params: '%02d' %
            (params['current_image']+1)}

            Example 1: Imagine having images without any inherent
            features named as follows: "images/image01.png",
            "images/image02.png" and so on. Then, filename_template:
            "images/image{i}.png" and "placeholder_mapping":
            "{'i': lambda params: '%02d' % (params['current_image']+1)}"
            This replaces {i} in the template with the current image
            number + 1

            Example 2: Imagine having image pairs from a stereo webcam
            named as follows: "images/image01_left.png",

            "images/image01_right.png" and so on. If
            pattern_labels=['Left','Right'], then filename_template:
            "images/image{i}_{dy}" and "placeholder_mapping":
            "{'i': lambda params: '%02d' % (params['current_image']+1),
            'dy':lambda params: 'left' if
            params['pattern_label']=='Left' else 'right'}"

            Here, additionally {dy} gets replaced by either 'left' if
            the pattern_label is 'Left' or 'right' otherwise

        If the directory does not contain a MANIFEST_json file, the
        defaults are as follows:
            :'filename_template': filepath/*.*, whereas filepath is
            the path given in dataset_name
            :'patterns_per_label': Number of image files in filepath,
            whereas filepath is the path given in dataset_name
            :'inherent_features': []
            :'placeholder_mapping': {}
        """

        filepath=param.resolve_path(dataset_name,path_to_file=False)
        self.dataset_name=filepath
        self.filename_template=filepath+"/*.*"
        self.patterns_per_label = len(glob.glob(self.filename_template))
        self.description=""
        self.source=self.dataset_name
        self.placeholder_mapping={}
        inherent_features=['sf','cr']
        try:
            filename=param.resolve_path(dataset_name+'/MANIFEST_json')
            filepath=os.path.dirname(filename)
            dataset=json.loads(open(filename).read())

            self.dataset_name=dataset.get('dataset_name', self.dataset_name)
            self.patterns_per_label=dataset.get('length', len(glob.glob(self.filename_template)))
            self.description=dataset.get('description', self.description)
            self.filename_template=dataset.get('filename_template', self.filename_template)
            self.source=dataset.get('source', self.source)
            self.placeholder_mapping=eval(dataset['placeholder_mapping']) if 'placeholder_mapping' in dataset else self.placeholder_mapping
            inherent_features=dataset.get('inherent_features', inherent_features)
        except IOError:
            pass

        super(PatternCoordinatorImages, self).__init__(inherent_features,**params)

    def _generate_filenames(self, params):
        if(len(self.placeholder_mapping)>0):
            filenames = [self.filename_template]*self.patterns_per_label

            for placeholder in self.placeholder_mapping:
                filenames = [filename.replace('{'+placeholder+'}', self.placeholder_mapping[placeholder](params))
                                for filename,params['current_image'] in zip(filenames,range(self.patterns_per_label))]
        else:
            filenames = sorted(glob.glob(self.filename_template))

        return filenames

    def _create_patterns(self, properties):
        return [self.pattern_type(
                    filename=f,
                    cache_image=False,
                    **self.pattern_parameters)
                for f,i in zip(self._generate_filenames(properties),range(self.patterns_per_label))]
<commit_msg>Docstring and code formatting fixes<commit_after>"""
Provides the class PatternCoordinator and a family of
FeatureCoordinator classes.

PatternCoordinator creates a set of pattern generators whose
parameters are related in some way, as controlled by a subclass of
FeatureCoordinator.
"""

import os
import math
import json
import glob
import collections
import copy

import param
from param.parameterized import ParamOverrides

from imagen.patterngenerator import PatternGenerator
from imagen.image import FileImage
from imagen import Gaussian, Composite, Selector, CompositeBase

import numbergen

class FeatureCoordinator(param.ParameterizedFunction):
    """
    A FeatureCoordinator modifies a supplied PatternGenerator.

    The modification can depend on the string pattern_label and
    pattern_number supplied, in order to coordinate a set of patterns
    of the same type with systematic differences.

    FeatureCoordinators that introduce randomness should be seeded
    with a value based on the supplied master_seed, so that an
    entire set of patterns can be controlled with the one master_seed
    value.

    Subclasses of this class can accept parameters provided in params.

    This superclass ensures a common interface across all
    FeatureCoordinator subclasses, which is necessary because they are
    usually stored in a list, with each item called the same way.
    """

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        """
        'pattern' is the PatternGenerator to be modified
        'pattern_label' is the name to be given to this
        PatternGenerator, used to select different behaviors
        'pattern_number' is an integer value distinguishing between
        multiple patterns with the same pattern_label
        'master_seed' is to be used for any random number generator
        seeds used for this pattern
        'params' consists of optional keyword-value pairs to be
        provided for subclasses' parameters
        """
        raise NotImplementedError

class XCoordinator(FeatureCoordinator):
    """
    Chooses a random value for the x coordinate, subject to the
    provided position_bound_x.
    """

    position_bound_x = param.Number(default=0.8,doc="""
        Left/rightmost position of the pattern center on the x axis.""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        new_pattern=copy.copy(pattern)
        new_pattern.x = pattern.get_value_generator('x')+\
        numbergen.UniformRandom(lbound=-p.position_bound_x,
                                ubound=p.position_bound_x,
                                seed=master_seed+12+pattern_number,
                                name="XCoordinator"+str(pattern_number))
        return new_pattern

class YCoordinator(FeatureCoordinator):
    """
    Chooses a random value for the y coordinate, subject to the
    provided position_bound_y.
    """

    position_bound_y = param.Number(default=0.8,doc="""
        Upper/lowermost position of the pattern center on the y axis.""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        new_pattern=copy.copy(pattern)
        new_pattern.y = pattern.get_value_generator('y')+\
            numbergen.UniformRandom(lbound=-p.position_bound_y,
                                    ubound=p.position_bound_y,
                                    seed=master_seed+35+pattern_number,
                                    name="YCoordinator"+str(pattern_number))
        return new_pattern

class OrientationCoordinator(FeatureCoordinator):
    """
    Chooses a random orientation within the specified
    orientation_bound in each direction.
    """

    orientation_bound = param.Number(default=math.pi,doc="""
        Rotate pattern around the origin by at most orientation_bound
        radians (in both directions).""")

    align_orientations = param.Boolean(default=False, doc="""
        Whether or not to align pattern orientations when composing
        multiple patterns together.

        Alignment may be useful to prevent crossing stimuli or may be
        appropriate for moving patterns sweeped in a single direction
        of motion.  """)

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        new_pattern=copy.copy(pattern)
        new_pattern.orientation = pattern.get_value_generator('orientation')+\
            numbergen.UniformRandom(lbound=-p.orientation_bound,
                                    ubound=p.orientation_bound,
                                    seed=master_seed+21+(0 if p.align_orientations else pattern_number),
                                    name=("OrientationCoordinator"
                                          + ('' if p.align_orientations else str(pattern_number)))
                                )
        return new_pattern

class PatternCoordinator(param.Parameterized):
    """
    Returns a set of coordinated PatternGenerators, named according to
    pattern_labels.

    The features to be modified are specified with the
    features_to_vary parameter. A feature is something coordinated
    between the PatternGenerators, either:

    a. one of the existing parameters of the PatternGenerators
       (such as size), or
    b. a variable from which values for one of the existing parameters
       can be calculated (such as a position offset between two
       PatternGenerators), or
    c. a value inherent to a particular existing image dataset
       (due to how the dataset was collected or generated).

    Each PatternGenerator is first instantiated with the supplied
    pattern_parameters, and then subclasses of FeatureCoordinator are
    applied sequentially to modify the specified or default parameter
    values of each PatternGenerator.
    """

    pattern_type = param.ClassSelector(PatternGenerator,default=Gaussian,is_instance=False,doc="""
        PatternGenerator type to be used.""")

    pattern_parameters = param.Dict(default={'size': 0.088388, 'aspect_ratio': 4.66667},doc="""
        Parameter values to be passed to the PatternGenerator specified in pattern_type.""")

    patterns_per_label = param.Integer(default=2,doc="""
        Number of patterns to generate and combine for a given label.""")

    features_to_vary = param.List(default=['xy','or'],class_=str,doc="""
        Stimulus features that the caller wishes to be varied, such as:
          :'xy': Position in x and y coordinates
          :'or': Orientation

        Subclasses and callers may extend this list to include any
        other features for which a coordinator has been defined in
        feature_coordinators.""")

    pattern_labels = param.List(default=['Input'],class_=str,bounds=(1,None),doc="""
        For each string in this list, a PatternGenerator of the
        requested pattern_type will be returned, with parameters whose
        values may depend on the string label supplied. For instance,
        if the list ["Pattern1","Pattern2"] is supplied, a metafeature
        function might inspect those pattern_labels and set parameters
        differently for Pattern1 and Pattern2, returning two different
        PatternGenerators with those pattern_labels.""")

    master_seed = param.Integer(default=0,doc="""
        Base seed for all pattern parameter values. Each numbered
        pattern on each of the various pattern_labels will normally
        use a different random seed, but all of these seeds should
        include this master_seed value, so that changing it will
        change all of the random pattern parameter streams.""")

    composite_type = param.ClassSelector(CompositeBase,default=Composite,
                                         is_instance=False,doc="""
        Class that combines the patterns_per_label individual patterns
        and creates a single combined pattern that it returns for a
        given label.  For instance, imagen.Composite can merge the
        individual patterns into a single pattern using a variety of
        operators like add or maximum, while imagen.Selector can
        choose one out of a given set of patterns.""")

    composite_parameters = param.Dict(default={},doc="""
        If present, these parameter values will be passed to the
        composite specified in composite_type.""")

    feature_coordinators = param.Dict(default=collections.OrderedDict([
        ('xy', [XCoordinator,YCoordinator]),
        ('or', OrientationCoordinator)]),doc="""
        Mapping from the feature name (key) to the method(s) to be
        applied to the pattern generators.  The value can either be a
        single method or a list of methods.""")

    def _create_patterns(self, properties=None):
        """
        Return a list (of length patterns_per_label) of
        PatternGenerator instances.  Should use pattern_type and
        pattern_parameters to create each pattern.

        properties is a dictionary, e.g. {'pattern_label':
        pattern_label}, which can be used to create PatternGenerators
        depending on the requested pattern_label
        """
        return [self.pattern_type(**self.pattern_parameters)
                for i in range(self.patterns_per_label)]

    def __init__(self,inherent_features=[],**params):

        """
        If a dataset already and inherently includes certain features,
        a list with the inherent feature names should be supplied.

        Any extra parameter values supplied here will be passed down
        to the feature_coordinators requested in features_to_vary.
        """
        p=ParamOverrides(self,params,allow_extra_keywords=True)

        super(PatternCoordinator, self).__init__(**p.param_keywords())

        self._feature_params = p.extra_keywords()

        self._inherent_features = inherent_features

        # TFALERT: Once spatial frequency (sf) is added, this will
        # cause warnings, because all image datasets will have a
        # spatial frequency inherent feature, but mostly we just
        # ignore that by having only a single size of DoG, which
        # discards all but a narrow range of sf.  So the dataset will
        # have sf inherently, but that won't be an error or even
        # worthy of a warning.
        if(len(set(self._inherent_features) - set(self.features_to_vary))):
            self.warning('Inherent feature present which is not requested in features')

        self._feature_coordinators_to_apply = []
        for feature, feature_coordinator in self.feature_coordinators.items():
            if feature in self.features_to_vary and feature not in self._inherent_features:
                # if it is a list, append each list item individually
                if isinstance(feature_coordinator,list):
                    for individual_feature_coordinator in feature_coordinator:
                        self._feature_coordinators_to_apply.append(individual_feature_coordinator)
                else:
                    self._feature_coordinators_to_apply.append(feature_coordinator)

    def __call__(self):
        coordinated_pattern_generators={}
        for pattern_label in self.pattern_labels:
            patterns=self._create_patterns({'pattern_label': pattern_label})

            # Apply _feature_coordinators_to_apply
            for i in range(len(patterns)):
                for fn in self._feature_coordinators_to_apply:
                    patterns[i]=fn(patterns[i],pattern_label, i,
                                   self.master_seed, **self._feature_params)

            combined_patterns=self.composite_type(generators=patterns,
                                                  **self.composite_parameters)
            coordinated_pattern_generators.update({pattern_label:combined_patterns})
        return coordinated_pattern_generators

class PatternCoordinatorImages(PatternCoordinator):

    pattern_type = param.ClassSelector(PatternGenerator,
                                       default=FileImage,is_instance=False)

    pattern_parameters = param.Dict(default={'size': 10})

    composite_type = param.ClassSelector(CompositeBase,
                                         default=Selector,is_instance=False)

    def __init__(self,dataset_name,**params):
        """
        dataset_name is the path to a folder containing a
        MANIFEST_json (https://docs.python.org/2/library/json.html),
        which contains a description for a dataset.  If no
        MANIFEST_json is present, all image files in the specified
        folder are used.

        Any extra parameter values supplied here will be passed down
        to the feature_coordinators requested in features_to_vary.

        The JSON file can contain any of the following entries, if an
        entry is not present, the default is used:

            :'dataset_name': Name of the dataset (string,
            default=filepath)
            :'length': Number of images in the dataset (integer,
            default=number of files in directory matching
            filename_template)
            :'description': Description of the dataset (string,
            default="")
            :'source': Citation of paper for which the dataset was
            created (string, default=name)
            :'filename_template': Path to the images with placeholders
            ({placeholder_name}) for inherent features and the image
            number, e.g. "filename_template": "images/image{i}.png".
            The placeholders are replaced according to
            placeholder_mapping.  Alternatively, glob patterns such as
            * or ? can be used, e.g. "filename_template":
            "images/*.png" (default=path_to_dataset_name/*.*)
            :'placeholder_mapping': Dictionary specifying the
            replacement of placeholders in filename_template; value is
            used in eval() (default={}).
            :'inherent_features': Features for which the corresponding
            feature_coordinators should not be applied
            (default=['sf','or','cr'])

            Currently, the label of the pattern generator
            ('pattern_label') as well as the image number
            ('current_image') are given as parameters to each callable
            supplied in placeholder_mapping, where current_image
            varies from 0 to length-1 and pattern_label is one of the
            items of pattern_labels.
            (python code, default={'i': lambda params: '%02d' %
            (params['current_image']+1)}

            Example 1: Imagine having images without any inherent
            features named as follows: "images/image01.png",
            "images/image02.png" and so on. Then, filename_template:
            "images/image{i}.png" and "placeholder_mapping":
            "{'i': lambda params: '%02d' % (params['current_image']+1)}"
            This replaces {i} in the template with the current image
            number + 1

            Example 2: Imagine having image pairs from a stereo webcam
            named as follows: "images/image01_left.png",

            "images/image01_right.png" and so on. If
            pattern_labels=['Left','Right'], then filename_template:
            "images/image{i}_{dy}" and "placeholder_mapping":
            "{'i': lambda params: '%02d' % (params['current_image']+1),
            'dy':lambda params: 'left' if
            params['pattern_label']=='Left' else 'right'}"

            Here, additionally {dy} gets replaced by either 'left' if
            the pattern_label is 'Left' or 'right' otherwise

        If the directory does not contain a MANIFEST_json file, the
        defaults are as follows:
            :'filename_template': filepath/*.*, whereas filepath is
            the path given in dataset_name
            :'patterns_per_label': Number of image files in filepath,
            whereas filepath is the path given in dataset_name
            :'inherent_features': []
            :'placeholder_mapping': {}
        """

        filepath=param.resolve_path(dataset_name,path_to_file=False)
        self.dataset_name=filepath
        self.filename_template=filepath+"/*.*"
        self.patterns_per_label = len(glob.glob(self.filename_template))
        self.description=""
        self.source=self.dataset_name
        self.placeholder_mapping={}
        inherent_features=['sf','cr']
        try:
            filename=param.resolve_path(dataset_name+'/MANIFEST_json')
            filepath=os.path.dirname(filename)
            dataset=json.loads(open(filename).read())

            self.dataset_name=dataset.get('dataset_name', self.dataset_name)
            self.patterns_per_label=dataset.get('length',
                                                len(glob.glob(self.filename_template)))
            self.description=dataset.get('description', self.description)
            self.filename_template=dataset.get('filename_template', self.filename_template)
            self.source=dataset.get('source', self.source)
            self.placeholder_mapping=(eval(dataset['placeholder_mapping'])
                                      if 'placeholder_mapping' in dataset
                                      else self.placeholder_mapping)
            inherent_features=dataset.get('inherent_features', inherent_features)
        except IOError:
            pass

        super(PatternCoordinatorImages, self).__init__(inherent_features,**params)

    def _generate_filenames(self, params):
        if(len(self.placeholder_mapping)>0):
            filenames = [self.filename_template]*self.patterns_per_label

            for placeholder in self.placeholder_mapping:
                filenames = [filename.replace('{'+placeholder+'}',
                                              self.placeholder_mapping[placeholder](params))
                             for filename,params['current_image'] in
                             zip(filenames,range(self.patterns_per_label))]
        else:
            filenames = sorted(glob.glob(self.filename_template))

        return filenames

    def _create_patterns(self, properties):
        return [self.pattern_type(
                    filename=f,
                    cache_image=False,
                    **self.pattern_parameters)
                for f,i in zip(self._generate_filenames(properties),
                               range(self.patterns_per_label))]
<|endoftext|>


<commit_before>"""
Provides the class PatternCoordinator and a family of FeatureCoordinator classes.

PatternCoordinator creates a set of pattern generators whose parameters are
related in some way, as controlled by a subclass of FeatureCoordinator.
"""

import os
import math
import json
import glob

import param
from param.parameterized import ParamOverrides

from imagen.patterngenerator import PatternGenerator
from imagen.image import FileImage
from imagen import Gaussian, Composite, Selector, CompositeBase

import numbergen

class FeatureCoordinator(param.ParameterizedFunction):
    """
    A FeatureCoordinator modifies a supplied PatternGenerator.

    The modification can depend on the string pattern_label and
    pattern_number supplied, in order to coordinate a set of patterns
    of the same type with systematic differences.

    FeatureCoordinators that introduce randomness should be seeded
    with a value based on the supplied master_seed, so that an
    entire set of patterns can be controlled with the one master_seed
    value.

    Subclasses of this class can accept parameters provided in params.

    This superclass ensures a common interface across all
    FeatureCoordinator subclasses, which is necessary because they are
    usually stored in a list, with each item called the same way.
    """

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        """
        'pattern' is the PatternGenerator to be modified
        'pattern_label' is the name to be given to this PatternGenerator, used to select different behaviors
        'pattern_number' is an integer value distinguishing between multiple patterns with the same pattern_label
        'master_seed' is to be used for any random number generator seeds used for this pattern
        'params' consists of optional keyword-value pairs to be provided for subclasses' parameters
        """
        raise NotImplementedError

class XCoordinator(FeatureCoordinator):
    """
    Chooses a random value for the x coordinate, subject to the provided position_bound_x.
    """

    position_bound_x = param.Number(default=0.8,doc="""
        Left/rightmost position of the pattern center on the x axis.""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        pattern.x += numbergen.UniformRandom(lbound=-p.position_bound_x,ubound=p.position_bound_x,seed=master_seed+12+pattern_number)

class YCoordinator(FeatureCoordinator):
    """
    Chooses a random value for the y coordinate, subject to the provided position_bound_y.
    """

    position_bound_y = param.Number(default=0.8,doc="""
        Upper/lowermost position of the pattern center on the y axis.""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        pattern.y += numbergen.UniformRandom(lbound=-p.position_bound_y,ubound=p.position_bound_y,seed=master_seed+35+pattern_number)

class OrientationCoordinator(FeatureCoordinator):
    """
    Chooses a random orientation within the specified orientation_bound in each direction.
    """

    orientation_bound = param.Number(default=math.pi,doc="""
        Rotate pattern around the origin by at most orientation_bound radians (in both directions).""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        pattern.orientation = numbergen.UniformRandom(lbound=-p.orientation_bound,ubound=p.orientation_bound,seed=master_seed+21+pattern_number)

class PatternCoordinator(param.Parameterized):
    """
    Returns a set of coordinated PatternGenerators, named according to pattern_labels.

    The features to be modified are specified with the features_to_vary
    parameter. A feature is something coordinated between the
    PatternGenerators, either:

    a. one of the existing parameters of the PatternGenerators
       (such as size), or
    b. a variable from which values for one of the existing parameters
       can be calculated (such as a position offset between two
       PatternGenerators), or
    c. a value inherent to a particular existing image dataset
       (due to how the dataset was collected or generated).

    Each PatternGenerator is first instantiated with the supplied
    pattern_parameters, and then subclasses of FeatureCoordinator are
    applied sequentially to modify the specified or default parameter
    values of each PatternGenerator.
    """

    pattern_type = param.ClassSelector(PatternGenerator,default=Gaussian,is_instance=False,doc="""
        PatternGenerator type to be used.""")

    pattern_parameters = param.Dict(default={'size': 0.088388, 'aspect_ratio': 4.66667},doc="""
        Parameter values to be passed to the PatternGenerator specified in pattern_type.""")

    patterns_per_label = param.Integer(default=2,doc="""
        Number of patterns to generate and combine for a given label.""")

    features_to_vary = param.List(default=['xy','or'],class_=str,doc="""
        Stimulus features that the caller wishes to be varied, such as:
          :'xy': Position in x and y coordinates
          :'or': Orientation

        Subclasses and callers may extend this list to include any
        other features for which a coordinator has been defined in
        feature_coordinators.""")

    pattern_labels = param.List(default=['Input'],class_=str,bounds=(1,None),doc="""
        For each string in this list, a PatternGenerator of the
        requested pattern_type will be returned, with parameters whose
        values may depend on the string label supplied. For instance,
        if the list ["Pattern1","Pattern2"] is supplied, a metafeature
        function might inspect those pattern_labels and set parameters
        differently for Pattern1 and Pattern2, returning two different
        PatternGenerators with those pattern_labels.""")

    master_seed = param.Integer(default=0,doc="""
        Base seed for all pattern parameter values. Each numbered
        pattern on each of the various pattern_labels will normally
        use a different random seed, but all of these seeds should
        include this master_seed value, so that changing it will
        change all of the random pattern parameter streams.""")

    composite_type = param.ClassSelector(CompositeBase,default=Composite,is_instance=False,doc="""
        Class that combines the patterns_per_label individual patterns
        and creates a single combined pattern that it returns for a
        given label.  For instance, imagen.Composite can merge the
        individual patterns into a single pattern using a variety of
        operators like add or maximum, while imagen.Selector can
        choose one out of a given set of patterns.""")

    composite_parameters = param.Dict(default={},doc="""
        If present, these parameter values will be passed to the composite specified in composite_type.""")

    feature_coordinators = param.Dict(default={
        'xy': [XCoordinator,YCoordinator],
        'or': OrientationCoordinator},doc="""
        Mapping from the feature name (key) to the method(s) to be
        applied to the pattern generators.  The value can either be a
        single method or a list of methods.""")

    def _create_patterns(self, properties=None):
        """
        Return a list (of length patterns_per_label) of PatternGenerator instances.
        Should use pattern_type and pattern_parameters to create each pattern.

        properties is a dictionary, e.g. {'pattern_label':
        pattern_label}, which can be used to create PatternGenerators
        depending on the requested pattern_label
        """
        return [self.pattern_type(**self.pattern_parameters) for i in range(self.patterns_per_label)]

    def __init__(self,inherent_features=[],**params):

        """
        If a dataset already and inherently includes certain features, a list
        with the inherent feature names should be supplied.

        Any extra parameter values supplied here will be passed down to the
        feature_coordinators requested in features_to_vary.
        """
        p=ParamOverrides(self,params,allow_extra_keywords=True)

        super(PatternCoordinator, self).__init__(**p.param_keywords())

        self._feature_params = p.extra_keywords()

        self._inherent_features = inherent_features

        # TFALERT: Once spatial frequency (sf) is added, this will
        # cause warnings, because all image datasets will have a
        # spatial frequency inherent feature, but mostly we just
        # ignore that by having only a single size of DoG, which
        # discards all but a narrow range of sf.  So the dataset will
        # have sf inherently, but that won't be an error or even
        # worthy of a warning.
        if(len(set(self._inherent_features) - set(self.features_to_vary))):
            self.warning('Inherent feature present which is not requested in features')

        self._feature_coordinators_to_apply = []
        for feature, feature_coordinator in self.feature_coordinators.items():
            if feature in self.features_to_vary and feature not in self._inherent_features:
                # if it is a list, append each list item individually
                if isinstance(feature_coordinator,list):
                    for individual_feature_coordinator in feature_coordinator:
                        self._feature_coordinators_to_apply.append(individual_feature_coordinator)
                else:
                    self._feature_coordinators_to_apply.append(feature_coordinator)

    def __call__(self):
        coordinated_pattern_generators={}
        for pattern_label in self.pattern_labels:
            patterns=self._create_patterns({'pattern_label': pattern_label})

            # Apply _feature_coordinators_to_apply
            for i in range(len(patterns)):
                for fn in self._feature_coordinators_to_apply:
                    fn(patterns[i],pattern_label,i,self.master_seed,**self._feature_params)

            combined_patterns=self.composite_type(generators=patterns,**self.composite_parameters)
            coordinated_pattern_generators.update({pattern_label:combined_patterns})
        return coordinated_pattern_generators

class PatternCoordinatorImages(PatternCoordinator):
    pattern_type = param.ClassSelector(PatternGenerator,default=FileImage,is_instance=False)

    pattern_parameters = param.Dict(default={'size': 10})

    composite_type = param.ClassSelector(CompositeBase,default=Selector,is_instance=False)

    def __init__(self,dataset_name,**params):
        """
        dataset_name is the path to a JSON file (https://docs.python.org/2/library/json.html)
        containing a description for a dataset. Alternatively, it can be a path to a folder
        containing image files.

        Any extra parameter values supplied here will be passed down to the
        feature_coordinators requested in features_to_vary.

        The JSON file should contain the following entries:

            :'name': Name of the dataset (string, default=basename(dataset_name))
            :'length': Number of images in the dataset (integer, default=number of files in directory of dataset_name minus 1)
            :'description': Description of the dataset (string, default="")
            :'source': Citation of paper for which the dataset was created (string, default=name)
            :'filename_template': Path to the images with placeholders ({placeholder_name})
            for inherent features and the image number, e.g. "filename_template": "images/image{i}.png".
            The placeholders are replaced according to placeholder_mapping.
            Alternatively, glob patterns such as * or ? can be used, e.g. "filename_template": "images/*.png"
            (default=path_to_dataset_name/{i}.png if dataset_name is a JSON file, path_to_dataset_name/*.png otherwise)
            :'placeholder_mapping': Dictionary specifying the replacement of placeholders in filename_template; value is used in eval().
            :'inherent_features': Features for which the corresponding feature_coordinators should not be applied

            Currently, the label of the pattern generator
            ('pattern_label') as well as the image number
            ('current_image') are given as parameters to each callable
            supplied in placeholder_mapping, where current_image varies
            from 0 to length-1 and pattern_label is one of the items
            of pattern_labels. (python code, default={'i': lambda params: '%02d' % (params['current_image']+1)}

            Example 1: Imagine having images without any inherent
            features named as follows: "images/image01.png",
            "images/image02.png" and so on. Then, filename_template:
            "images/image{i}.png" and "placeholder_mapping":
            "{'i': lambda params: '%02d' % (params['current_image']+1)}"
            This replaces {i} in the template with the current image number + 1

            Example 2: Imagine having image pairs from a stereo webcam named as follows: "images/image01_left.png",
            "images/image01_right.png" and so on. If pattern_labels=['Left','Right'], then
            filename_template: "images/image{i}_{dy}" and
            "placeholder_mapping": "{'i': lambda params: '%02d' % (params['current_image']+1),
                                   'dy':lambda params: 'left' if params['pattern_label']=='Left' else 'right'}"

            Here, additionally {dy} gets replaced by either 'left' if the pattern_label is 'Left' or 'right' otherwise

        If a directory to image files rather than a JSON file is given in dataset_name,
        the defaults are as follows:
            :'filename_template': filepath/*.png, whereas filepath is the path given in dataset_name
            :'patterns_per_label': Number of png files in filepath, whereas filepath is the path given in dataset_name
            :'inherent_features': []
            :'placeholder_mapping': {}
        """

        try:
            filename=param.resolve_path(dataset_name)
            filepath=os.path.dirname(filename)
            dataset=json.loads(open(filename).read())

            self.dataset_name=dataset.get('name', os.path.basename(dataset_name))
            length = len([ f for f in os.listdir(filepath) if os.path.isfile(os.path.join(filepath,f)) ]) - 1
            self.patterns_per_label=dataset.get('length', length)
            self.description=dataset.get('description', "")
            self.filename_template=dataset.get('filename_template', filepath+"/{i}.png")
            self.source=dataset.get('source', self.dataset_name)
            self.placeholder_mapping=eval(dataset['placeholder_mapping']) if 'placeholder_mapping' in dataset else {'i': lambda params: '%02d' % (params['current_image']+1)}
            inherent_features=dataset.get('inherent_features', [])
        except IOError:
            filepath=param.resolve_path(dataset_name,path_to_file=False)
            self.dataset_name=filepath
            self.filename_template=filepath+"/*.png"
            self.patterns_per_label = len(glob.glob(self.filename_template))
            self.description=""
            self.source=filepath
            self.placeholder_mapping={}
            inherent_features=[]

        super(PatternCoordinatorImages, self).__init__(inherent_features,**params)

    def _generate_filenames(self, params):
        if(len(self.placeholder_mapping)>0):
            filenames = [self.filename_template]*self.patterns_per_label

            for placeholder in self.placeholder_mapping:
                filenames = [filename.replace('{'+placeholder+'}', self.placeholder_mapping[placeholder](params))
                                for filename,params['current_image'] in zip(filenames,range(self.patterns_per_label))]
        else:
            filenames = sorted(glob.glob(self.filename_template))

        return filenames

    def _create_patterns(self, properties):
        return [self.pattern_type(
                    filename=f,
                    cache_image=False,
                    **self.pattern_parameters)
                for f,i in zip(self._generate_filenames(properties),range(self.patterns_per_label))]
<commit_msg>Updated inherent_features<commit_after>"""
Provides the class PatternCoordinator and a family of FeatureCoordinator classes.

PatternCoordinator creates a set of pattern generators whose parameters are
related in some way, as controlled by a subclass of FeatureCoordinator.
"""

import os
import math
import json
import glob

import param
from param.parameterized import ParamOverrides

from imagen.patterngenerator import PatternGenerator
from imagen.image import FileImage
from imagen import Gaussian, Composite, Selector, CompositeBase

import numbergen

class FeatureCoordinator(param.ParameterizedFunction):
    """
    A FeatureCoordinator modifies a supplied PatternGenerator.

    The modification can depend on the string pattern_label and
    pattern_number supplied, in order to coordinate a set of patterns
    of the same type with systematic differences.

    FeatureCoordinators that introduce randomness should be seeded
    with a value based on the supplied master_seed, so that an
    entire set of patterns can be controlled with the one master_seed
    value.

    Subclasses of this class can accept parameters provided in params.

    This superclass ensures a common interface across all
    FeatureCoordinator subclasses, which is necessary because they are
    usually stored in a list, with each item called the same way.
    """

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        """
        'pattern' is the PatternGenerator to be modified
        'pattern_label' is the name to be given to this PatternGenerator, used to select different behaviors
        'pattern_number' is an integer value distinguishing between multiple patterns with the same pattern_label
        'master_seed' is to be used for any random number generator seeds used for this pattern
        'params' consists of optional keyword-value pairs to be provided for subclasses' parameters
        """
        raise NotImplementedError

class XCoordinator(FeatureCoordinator):
    """
    Chooses a random value for the x coordinate, subject to the provided position_bound_x.
    """

    position_bound_x = param.Number(default=0.8,doc="""
        Left/rightmost position of the pattern center on the x axis.""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        pattern.x += numbergen.UniformRandom(lbound=-p.position_bound_x,ubound=p.position_bound_x,seed=master_seed+12+pattern_number)

class YCoordinator(FeatureCoordinator):
    """
    Chooses a random value for the y coordinate, subject to the provided position_bound_y.
    """

    position_bound_y = param.Number(default=0.8,doc="""
        Upper/lowermost position of the pattern center on the y axis.""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        pattern.y += numbergen.UniformRandom(lbound=-p.position_bound_y,ubound=p.position_bound_y,seed=master_seed+35+pattern_number)

class OrientationCoordinator(FeatureCoordinator):
    """
    Chooses a random orientation within the specified orientation_bound in each direction.
    """

    orientation_bound = param.Number(default=math.pi,doc="""
        Rotate pattern around the origin by at most orientation_bound radians (in both directions).""")

    def __call__(self, pattern, pattern_label, pattern_number, master_seed, **params):
        p = ParamOverrides(self,params,allow_extra_keywords=True)
        pattern.orientation = numbergen.UniformRandom(lbound=-p.orientation_bound,ubound=p.orientation_bound,seed=master_seed+21+pattern_number)

class PatternCoordinator(param.Parameterized):
    """
    Returns a set of coordinated PatternGenerators, named according to pattern_labels.

    The features to be modified are specified with the features_to_vary
    parameter. A feature is something coordinated between the
    PatternGenerators, either:

    a. one of the existing parameters of the PatternGenerators
       (such as size), or
    b. a variable from which values for one of the existing parameters
       can be calculated (such as a position offset between two
       PatternGenerators), or
    c. a value inherent to a particular existing image dataset
       (due to how the dataset was collected or generated).

    Each PatternGenerator is first instantiated with the supplied
    pattern_parameters, and then subclasses of FeatureCoordinator are
    applied sequentially to modify the specified or default parameter
    values of each PatternGenerator.
    """

    pattern_type = param.ClassSelector(PatternGenerator,default=Gaussian,is_instance=False,doc="""
        PatternGenerator type to be used.""")

    pattern_parameters = param.Dict(default={'size': 0.088388, 'aspect_ratio': 4.66667},doc="""
        Parameter values to be passed to the PatternGenerator specified in pattern_type.""")

    patterns_per_label = param.Integer(default=2,doc="""
        Number of patterns to generate and combine for a given label.""")

    features_to_vary = param.List(default=['xy','or'],class_=str,doc="""
        Stimulus features that the caller wishes to be varied, such as:
          :'xy': Position in x and y coordinates
          :'or': Orientation

        Subclasses and callers may extend this list to include any
        other features for which a coordinator has been defined in
        feature_coordinators.""")

    pattern_labels = param.List(default=['Input'],class_=str,bounds=(1,None),doc="""
        For each string in this list, a PatternGenerator of the
        requested pattern_type will be returned, with parameters whose
        values may depend on the string label supplied. For instance,
        if the list ["Pattern1","Pattern2"] is supplied, a metafeature
        function might inspect those pattern_labels and set parameters
        differently for Pattern1 and Pattern2, returning two different
        PatternGenerators with those pattern_labels.""")

    master_seed = param.Integer(default=0,doc="""
        Base seed for all pattern parameter values. Each numbered
        pattern on each of the various pattern_labels will normally
        use a different random seed, but all of these seeds should
        include this master_seed value, so that changing it will
        change all of the random pattern parameter streams.""")

    composite_type = param.ClassSelector(CompositeBase,default=Composite,is_instance=False,doc="""
        Class that combines the patterns_per_label individual patterns
        and creates a single combined pattern that it returns for a
        given label.  For instance, imagen.Composite can merge the
        individual patterns into a single pattern using a variety of
        operators like add or maximum, while imagen.Selector can
        choose one out of a given set of patterns.""")

    composite_parameters = param.Dict(default={},doc="""
        If present, these parameter values will be passed to the composite specified in composite_type.""")

    feature_coordinators = param.Dict(default={
        'xy': [XCoordinator,YCoordinator],
        'or': OrientationCoordinator},doc="""
        Mapping from the feature name (key) to the method(s) to be
        applied to the pattern generators.  The value can either be a
        single method or a list of methods.""")

    def _create_patterns(self, properties=None):
        """
        Return a list (of length patterns_per_label) of PatternGenerator instances.
        Should use pattern_type and pattern_parameters to create each pattern.

        properties is a dictionary, e.g. {'pattern_label':
        pattern_label}, which can be used to create PatternGenerators
        depending on the requested pattern_label
        """
        return [self.pattern_type(**self.pattern_parameters) for i in range(self.patterns_per_label)]

    def __init__(self,inherent_features=[],**params):

        """
        If a dataset already and inherently includes certain features, a list
        with the inherent feature names should be supplied.

        Any extra parameter values supplied here will be passed down to the
        feature_coordinators requested in features_to_vary.
        """
        p=ParamOverrides(self,params,allow_extra_keywords=True)

        super(PatternCoordinator, self).__init__(**p.param_keywords())

        self._feature_params = p.extra_keywords()

        self._inherent_features = inherent_features

        # TFALERT: Once spatial frequency (sf) is added, this will
        # cause warnings, because all image datasets will have a
        # spatial frequency inherent feature, but mostly we just
        # ignore that by having only a single size of DoG, which
        # discards all but a narrow range of sf.  So the dataset will
        # have sf inherently, but that won't be an error or even
        # worthy of a warning.
        if(len(set(self._inherent_features) - set(self.features_to_vary))):
            self.warning('Inherent feature present which is not requested in features')

        self._feature_coordinators_to_apply = []
        for feature, feature_coordinator in self.feature_coordinators.items():
            if feature in self.features_to_vary and feature not in self._inherent_features:
                # if it is a list, append each list item individually
                if isinstance(feature_coordinator,list):
                    for individual_feature_coordinator in feature_coordinator:
                        self._feature_coordinators_to_apply.append(individual_feature_coordinator)
                else:
                    self._feature_coordinators_to_apply.append(feature_coordinator)

    def __call__(self):
        coordinated_pattern_generators={}
        for pattern_label in self.pattern_labels:
            patterns=self._create_patterns({'pattern_label': pattern_label})

            # Apply _feature_coordinators_to_apply
            for i in range(len(patterns)):
                for fn in self._feature_coordinators_to_apply:
                    fn(patterns[i],pattern_label,i,self.master_seed,**self._feature_params)

            combined_patterns=self.composite_type(generators=patterns,**self.composite_parameters)
            coordinated_pattern_generators.update({pattern_label:combined_patterns})
        return coordinated_pattern_generators

class PatternCoordinatorImages(PatternCoordinator):
    pattern_type = param.ClassSelector(PatternGenerator,default=FileImage,is_instance=False)

    pattern_parameters = param.Dict(default={'size': 10})

    composite_type = param.ClassSelector(CompositeBase,default=Selector,is_instance=False)

    def __init__(self,dataset_name,**params):
        """
        dataset_name is the path to a JSON file (https://docs.python.org/2/library/json.html)
        containing a description for a dataset. Alternatively, it can be a path to a folder
        containing image files.

        Any extra parameter values supplied here will be passed down to the
        feature_coordinators requested in features_to_vary.

        The JSON file should contain the following entries:

            :'name': Name of the dataset (string, default=basename(dataset_name))
            :'length': Number of images in the dataset (integer, default=number of files in directory of dataset_name minus 1)
            :'description': Description of the dataset (string, default="")
            :'source': Citation of paper for which the dataset was created (string, default=name)
            :'filename_template': Path to the images with placeholders ({placeholder_name})
            for inherent features and the image number, e.g. "filename_template": "images/image{i}.png".
            The placeholders are replaced according to placeholder_mapping.
            Alternatively, glob patterns such as * or ? can be used, e.g. "filename_template": "images/*.png"
            (default=path_to_dataset_name/{i}.png if dataset_name is a JSON file, path_to_dataset_name/*.png otherwise)
            :'placeholder_mapping': Dictionary specifying the replacement of placeholders in filename_template; value is used in eval().
            :'inherent_features': Features for which the corresponding feature_coordinators should not be applied

            Currently, the label of the pattern generator
            ('pattern_label') as well as the image number
            ('current_image') are given as parameters to each callable
            supplied in placeholder_mapping, where current_image varies
            from 0 to length-1 and pattern_label is one of the items
            of pattern_labels. (python code, default={'i': lambda params: '%02d' % (params['current_image']+1)}

            Example 1: Imagine having images without any inherent
            features named as follows: "images/image01.png",
            "images/image02.png" and so on. Then, filename_template:
            "images/image{i}.png" and "placeholder_mapping":
            "{'i': lambda params: '%02d' % (params['current_image']+1)}"
            This replaces {i} in the template with the current image number + 1

            Example 2: Imagine having image pairs from a stereo webcam named as follows: "images/image01_left.png",
            "images/image01_right.png" and so on. If pattern_labels=['Left','Right'], then
            filename_template: "images/image{i}_{dy}" and
            "placeholder_mapping": "{'i': lambda params: '%02d' % (params['current_image']+1),
                                   'dy':lambda params: 'left' if params['pattern_label']=='Left' else 'right'}"

            Here, additionally {dy} gets replaced by either 'left' if the pattern_label is 'Left' or 'right' otherwise

        If a directory to image files rather than a JSON file is given in dataset_name,
        the defaults are as follows:
            :'filename_template': filepath/*.png, whereas filepath is the path given in dataset_name
            :'patterns_per_label': Number of png files in filepath, whereas filepath is the path given in dataset_name
            :'inherent_features': []
            :'placeholder_mapping': {}
        """

        try:
            filename=param.resolve_path(dataset_name)
            filepath=os.path.dirname(filename)
            dataset=json.loads(open(filename).read())

            self.dataset_name=dataset.get('name', os.path.basename(dataset_name))
            length = len([ f for f in os.listdir(filepath) if os.path.isfile(os.path.join(filepath,f)) ]) - 1
            self.patterns_per_label=dataset.get('length', length)
            self.description=dataset.get('description', "")
            self.filename_template=dataset.get('filename_template', filepath+"/{i}.png")
            self.source=dataset.get('source', self.dataset_name)
            self.placeholder_mapping=eval(dataset['placeholder_mapping']) if 'placeholder_mapping' in dataset else {'i': lambda params: '%02d' % (params['current_image']+1)}
            inherent_features=dataset.get('inherent_features', ['sf','or','cr'])
        except IOError:
            filepath=param.resolve_path(dataset_name,path_to_file=False)
            self.dataset_name=filepath
            self.filename_template=filepath+"/*.png"
            self.patterns_per_label = len(glob.glob(self.filename_template))
            self.description=""
            self.source=filepath
            self.placeholder_mapping={}
            inherent_features=['sf','or','cr']

        super(PatternCoordinatorImages, self).__init__(inherent_features,**params)

    def _generate_filenames(self, params):
        if(len(self.placeholder_mapping)>0):
            filenames = [self.filename_template]*self.patterns_per_label

            for placeholder in self.placeholder_mapping:
                filenames = [filename.replace('{'+placeholder+'}', self.placeholder_mapping[placeholder](params))
                                for filename,params['current_image'] in zip(filenames,range(self.patterns_per_label))]
        else:
            filenames = sorted(glob.glob(self.filename_template))

        return filenames

    def _create_patterns(self, properties):
        return [self.pattern_type(
                    filename=f,
                    cache_image=False,
                    **self.pattern_parameters)
                for f,i in zip(self._generate_filenames(properties),range(self.patterns_per_label))]
<|endoftext|>


<commit_before>import pygrate

class CreateEmployeeTable(pygrate.Pygration):
    def __init__( self ):
        self._sql = \
                """
                CREATE TABLE employee
                ( a number
                , b varchar2(10)
                );
                """

class CreateJobTable(pygrate.Pygration):
    def pre_up( self, step ):
        step.execute_sql( \
                """
                CREATE TABLE job
                ( id number
                , name varchar2(20)
                );
                """ )

    def pre_down( self, step ):
        step.execute_sql( "DROP TABLE job" );

    def execute_sql( self, sql ):
        print "executed on the db:\n%s\n" % ( sql )

<commit_msg>added temporary debug msg to r1.py<commit_after>import pygrate

class CreateEmployeeTable(pygrate.Pygration):
    def add( self, db ):
        print "sample add"
        self._sql = \
                """
                CREATE TABLE employee
                ( a number
                , b varchar2(10)
                );
                """

class CreateJobTable(pygrate.Pygration):
    def pre_up( self, step ):
        step.execute_sql( \
                """
                CREATE TABLE job
                ( id number
                , name varchar2(20)
                );
                """ )

    def pre_down( self, step ):
        step.execute_sql( "DROP TABLE job" );

    def execute_sql( self, sql ):
        print "executed on the db:\n%s\n" % ( sql )

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import os
import sys

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage
from django.core.management.base import BaseCommand
from django_js_reverse.core import generate_js
from django_js_reverse.js_reverse_settings import JS_OUTPUT_PATH

try:
    from django.urls import get_resolver
except ImportError:
    from django.core.urlresolvers import get_resolver

class Command(BaseCommand):
    help = 'Creates a static urls-js file for django-js-reverse'

    def get_location(self):
        output_path = getattr(settings, 'JS_REVERSE_OUTPUT_PATH', JS_OUTPUT_PATH)
        if output_path:
            return output_path

        if not hasattr(settings, 'STATIC_ROOT') or not settings.STATIC_ROOT:
            raise ImproperlyConfigured(
                'The collectstatic_js_reverse command needs settings.JS_REVERSE_OUTPUT_PATH or settings.STATIC_ROOT to be set.')

        return os.path.join(settings.STATIC_ROOT, 'django_js_reverse', 'js')

    def handle(self, *args, **options):
        location = self.get_location()
        file = 'reverse.js'
        fs = FileSystemStorage(location=location)
        if fs.exists(file):
            fs.delete(file)

        default_urlresolver = get_resolver(None)
        content = generate_js(default_urlresolver)
        fs.save(file, ContentFile(content))
        if len(sys.argv) > 1 and sys.argv[1] in ['collectstatic_js_reverse']:
            self.stdout.write('js-reverse file written to %s' % (location))  # pragma: no cover
<commit_msg>Resolves issue #73<commit_after># -*- coding: utf-8 -*-
import os
import sys

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage
from django.core.management.base import BaseCommand
from django_js_reverse.core import generate_js
from django_js_reverse.js_reverse_settings import JS_OUTPUT_PATH

try:
    from django.urls import get_resolver
except ImportError:
    from django.core.urlresolvers import get_resolver

class Command(BaseCommand):
    help = 'Creates a static urls-js file for django-js-reverse'

    def get_location(self):
        output_path = getattr(settings, 'JS_REVERSE_OUTPUT_PATH', JS_OUTPUT_PATH)
        if output_path:
            return output_path

        if not hasattr(settings, 'STATIC_ROOT') or not settings.STATIC_ROOT:
            raise ImproperlyConfigured(
                'The collectstatic_js_reverse command needs settings.JS_REVERSE_OUTPUT_PATH or settings.STATIC_ROOT to be set.')

        return os.path.join(settings.STATIC_ROOT, 'django_js_reverse', 'js')

    def handle(self, *args, **options):
        location = self.get_location()
        file = 'reverse.js'
        fs = FileSystemStorage(location=location)
        if fs.exists(file):
            fs.delete(file)

        try:
            urlconf = settings.ROOT_URLCONF
        except AttributeError:
            urlconf = None
        default_urlresolver = get_resolver(urlconf)
        content = generate_js(default_urlresolver)
        fs.save(file, ContentFile(content))
        if len(sys.argv) > 1 and sys.argv[1] in ['collectstatic_js_reverse']:
            self.stdout.write('js-reverse file written to %s' % (location))  # pragma: no cover
<|endoftext|>


<commit_before>import unittest
import os
from tests.setup_scripts import ring_road_exp_setup
from flow.core.params import EnvParams
from flow.core.vehicles import Vehicles
from flow.controllers import RLController
from flow.core.rewards import average_velocity, total_velocity, min_delay, \
    desired_velocity, max_edge_velocity, penalize_standstill, \
    penalize_near_standstill, punish_small_rl_headways

os.environ["TEST_FLAG"] = "True"

class TestRewards(unittest.TestCase):
    """Tests for all methods in flow/core/rewards.py."""

    def test_desired_velocity(self):
        """Test the desired_velocity method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env_params = EnvParams(additional_params={
            "target_velocity": 10, "max_accel": 1, "max_decel": 1})

        env, scenario = ring_road_exp_setup(vehicles=vehicles,
                                            env_params=env_params)

        # check that the fail attribute leads to a zero return
        self.assertEqual(desired_velocity(env, fail=True), 0)

        # check the average speed upon reset
        self.assertEqual(desired_velocity(env, fail=False), 0)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the new average speed
        self.assertAlmostEqual(desired_velocity(env, fail=False), 0.05131670)

    def test_average_velocity(self):
        """Test the average_velocity method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check that the fail attribute leads to a zero return
        self.assertEqual(average_velocity(env, fail=True), 0)

        # check the average speed upon reset
        self.assertEqual(average_velocity(env, fail=False), 0)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the new average speed
        self.assertEqual(average_velocity(env, fail=False), 1)

        # recreate the environment with no vehicles
        vehicles = Vehicles()
        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check that the reward function return 0 in the case of no vehicles
        self.assertEqual(average_velocity(env, fail=False), 0)

    def test_total_velocity(self):
        """Test the average_velocity method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check that the fail attribute leads to a zero return
        self.assertEqual(total_velocity(env, fail=True), 0)

        # check the average speed upon reset
        self.assertEqual(total_velocity(env, fail=False), 0)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the new average speed
        self.assertEqual(total_velocity(env, fail=False), 10)

    def test_max_edge_velocity(self):
        """Test the max_edge_velocity method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env_params = EnvParams(additional_params={
            "target_velocity": 10, "max_accel": 1, "max_decel": 1})

        env, scenario = ring_road_exp_setup(vehicles=vehicles,
                                            env_params=env_params)

        # check that the fail attribute leads to a zero return
        self.assertEqual(max_edge_velocity(env, edge_list=[], fail=True), 0)

        # check the average speed upon reset
        self.assertEqual(max_edge_velocity(env, edge_list=["bottom"],
                                           fail=False), 0)

        # change the speed of one vehicle in the edge list
        env.vehicles.test_set_speed("test_0", 10)

        # check the new average speed
        self.assertAlmostEqual(max_edge_velocity(env, edge_list=["bottom"],
                                                 fail=False), 3.178372452)

        # change the speed of one of the vehicles outside the edge list
        env.vehicles.test_set_speed("test_8", 10)

        # check that the new average speed is the same as before
        self.assertAlmostEqual(max_edge_velocity(env, edge_list=["bottom"],
                                                 fail=False), 3.178372452)

    def test_min_delay(self):
        """Test the min_delay method."""
        # try the case of an environment with no vehicles
        vehicles = Vehicles()
        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check that the reward function return 0 in the case of no vehicles
        self.assertEqual(min_delay(env), 0)

        # try the case of multiple vehicles
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)
        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check the min_delay upon reset
        self.assertAlmostEqual(min_delay(env), 0)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the min_delay with the new speed
        self.assertAlmostEqual(min_delay(env), 0.0333333333333)

    def test_penalize_standstill(self):
        """Test the penalize_standstill method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env_params = EnvParams(additional_params={
            "target_velocity": 10, "max_accel": 1, "max_decel": 1})

        env, scenario = ring_road_exp_setup(vehicles=vehicles,
                                            env_params=env_params)

        # check the penalty is acknowledging all vehicles
        self.assertEqual(penalize_standstill(env, gain=1), -10)
        self.assertEqual(penalize_standstill(env, gain=2), -20)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the penalty is acknowledging all vehicles but one
        self.assertEqual(penalize_standstill(env, gain=1), -9)
        self.assertEqual(penalize_standstill(env, gain=2), -18)

    def test_penalize_near_standstill(self):
        """Test the penalize_near_standstill method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env_params = EnvParams(additional_params={
            "target_velocity": 10, "max_accel": 1, "max_decel": 1})

        env, scenario = ring_road_exp_setup(vehicles=vehicles,
                                            env_params=env_params)

        # check the penalty is acknowledging all vehicles
        self.assertEqual(penalize_near_standstill(env, gain=1), -10)
        self.assertEqual(penalize_near_standstill(env, gain=2), -20)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 1)

        # check the penalty with good and bad thresholds
        self.assertEqual(penalize_near_standstill(env, thresh=2), -10)
        self.assertEqual(penalize_near_standstill(env, thresh=0.5), -9)

    def test_punish_small_rl_headways(self):
        vehicles = Vehicles()
        vehicles.add("test", acceleration_controller=(RLController, {}),
                     num_vehicles=10)

        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # set the headways to 0
        for veh_id in env.vehicles.get_rl_ids():
            env.vehicles.set_headway(veh_id, 0)

        # test penalty when headways of all vehicles are currently 0
        self.assertEqual(punish_small_rl_headways(env, headway_threshold=1),
                         -10)
        self.assertEqual(punish_small_rl_headways(env, headway_threshold=2),
                         -10)
        self.assertEqual(punish_small_rl_headways(env,
                                                  headway_threshold=2,
                                                  penalty_gain=2),
                         -20)
        self.assertEqual(punish_small_rl_headways(env,
                                                  headway_threshold=2,
                                                  penalty_gain=2,
                                                  penalty_exponent=2),
                         -20)

if __name__ == '__main__':
    unittest.main()
<commit_msg>test for boolean action reward<commit_after>import unittest
import os
from tests.setup_scripts import ring_road_exp_setup
from flow.core.params import EnvParams
from flow.core.vehicles import Vehicles
from flow.controllers import RLController
from flow.core.rewards import average_velocity, total_velocity, min_delay, \
    desired_velocity, max_edge_velocity, penalize_standstill, \
    penalize_near_standstill, punish_small_rl_headways, \
    boolean_action_penalty

os.environ["TEST_FLAG"] = "True"

class TestRewards(unittest.TestCase):
    """Tests for all methods in flow/core/rewards.py."""

    def test_desired_velocity(self):
        """Test the desired_velocity method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env_params = EnvParams(additional_params={
            "target_velocity": 10, "max_accel": 1, "max_decel": 1})

        env, scenario = ring_road_exp_setup(vehicles=vehicles,
                                            env_params=env_params)

        # check that the fail attribute leads to a zero return
        self.assertEqual(desired_velocity(env, fail=True), 0)

        # check the average speed upon reset
        self.assertEqual(desired_velocity(env, fail=False), 0)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the new average speed
        self.assertAlmostEqual(desired_velocity(env, fail=False), 0.05131670)

    def test_average_velocity(self):
        """Test the average_velocity method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check that the fail attribute leads to a zero return
        self.assertEqual(average_velocity(env, fail=True), 0)

        # check the average speed upon reset
        self.assertEqual(average_velocity(env, fail=False), 0)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the new average speed
        self.assertEqual(average_velocity(env, fail=False), 1)

        # recreate the environment with no vehicles
        vehicles = Vehicles()
        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check that the reward function return 0 in the case of no vehicles
        self.assertEqual(average_velocity(env, fail=False), 0)

    def test_total_velocity(self):
        """Test the average_velocity method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check that the fail attribute leads to a zero return
        self.assertEqual(total_velocity(env, fail=True), 0)

        # check the average speed upon reset
        self.assertEqual(total_velocity(env, fail=False), 0)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the new average speed
        self.assertEqual(total_velocity(env, fail=False), 10)

    def test_max_edge_velocity(self):
        """Test the max_edge_velocity method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env_params = EnvParams(additional_params={
            "target_velocity": 10, "max_accel": 1, "max_decel": 1})

        env, scenario = ring_road_exp_setup(vehicles=vehicles,
                                            env_params=env_params)

        # check that the fail attribute leads to a zero return
        self.assertEqual(max_edge_velocity(env, edge_list=[], fail=True), 0)

        # check the average speed upon reset
        self.assertEqual(max_edge_velocity(env, edge_list=["bottom"],
                                           fail=False), 0)

        # change the speed of one vehicle in the edge list
        env.vehicles.test_set_speed("test_0", 10)

        # check the new average speed
        self.assertAlmostEqual(max_edge_velocity(env, edge_list=["bottom"],
                                                 fail=False), 3.178372452)

        # change the speed of one of the vehicles outside the edge list
        env.vehicles.test_set_speed("test_8", 10)

        # check that the new average speed is the same as before
        self.assertAlmostEqual(max_edge_velocity(env, edge_list=["bottom"],
                                                 fail=False), 3.178372452)

    def test_min_delay(self):
        """Test the min_delay method."""
        # try the case of an environment with no vehicles
        vehicles = Vehicles()
        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check that the reward function return 0 in the case of no vehicles
        self.assertEqual(min_delay(env), 0)

        # try the case of multiple vehicles
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)
        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # check the min_delay upon reset
        self.assertAlmostEqual(min_delay(env), 0)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the min_delay with the new speed
        self.assertAlmostEqual(min_delay(env), 0.0333333333333)

    def test_penalize_standstill(self):
        """Test the penalize_standstill method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env_params = EnvParams(additional_params={
            "target_velocity": 10, "max_accel": 1, "max_decel": 1})

        env, scenario = ring_road_exp_setup(vehicles=vehicles,
                                            env_params=env_params)

        # check the penalty is acknowledging all vehicles
        self.assertEqual(penalize_standstill(env, gain=1), -10)
        self.assertEqual(penalize_standstill(env, gain=2), -20)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 10)

        # check the penalty is acknowledging all vehicles but one
        self.assertEqual(penalize_standstill(env, gain=1), -9)
        self.assertEqual(penalize_standstill(env, gain=2), -18)

    def test_penalize_near_standstill(self):
        """Test the penalize_near_standstill method."""
        vehicles = Vehicles()
        vehicles.add("test", num_vehicles=10)

        env_params = EnvParams(additional_params={
            "target_velocity": 10, "max_accel": 1, "max_decel": 1})

        env, scenario = ring_road_exp_setup(vehicles=vehicles,
                                            env_params=env_params)

        # check the penalty is acknowledging all vehicles
        self.assertEqual(penalize_near_standstill(env, gain=1), -10)
        self.assertEqual(penalize_near_standstill(env, gain=2), -20)

        # change the speed of one vehicle
        env.vehicles.test_set_speed("test_0", 1)

        # check the penalty with good and bad thresholds
        self.assertEqual(penalize_near_standstill(env, thresh=2), -10)
        self.assertEqual(penalize_near_standstill(env, thresh=0.5), -9)

    def test_punish_small_rl_headways(self):
        """Test the punish_small_rl_headways method."""
        vehicles = Vehicles()
        vehicles.add("test", acceleration_controller=(RLController, {}),
                     num_vehicles=10)

        env, scenario = ring_road_exp_setup(vehicles=vehicles)

        # set the headways to 0
        for veh_id in env.vehicles.get_rl_ids():
            env.vehicles.set_headway(veh_id, 0)

        # test penalty when headways of all vehicles are currently 0
        self.assertEqual(punish_small_rl_headways(env, headway_threshold=1),
                         -10)
        self.assertEqual(punish_small_rl_headways(env, headway_threshold=2),
                         -10)
        self.assertEqual(punish_small_rl_headways(env,
                                                  headway_threshold=2,
                                                  penalty_gain=2),
                         -20)
        self.assertEqual(punish_small_rl_headways(env,
                                                  headway_threshold=2,
                                                  penalty_gain=2,
                                                  penalty_exponent=2),
                         -20)

    def test_boolean_action_penalty(self):
        """Test the boolean_action_penalty method."""
        actions = [False, False, False, False, False]
        self.assertEqual(boolean_action_penalty(actions, gain=1), 0)
        self.assertEqual(boolean_action_penalty(actions, gain=2), 0)

        actions = [True, False, False, False, False]
        self.assertEqual(boolean_action_penalty(actions, gain=1), 1)
        self.assertEqual(boolean_action_penalty(actions, gain=2), 2)

        actions = [True, False, False, True, False]
        self.assertEqual(boolean_action_penalty(actions, gain=1), 2)
        self.assertEqual(boolean_action_penalty(actions, gain=2), 4)

if __name__ == '__main__':
    unittest.main()
<|endoftext|>


<commit_before>import discord
import duckbot_settings
import os
import random
import re
from discord.ext import commands

_DESCRIPTION = '''quack'''

bot = commands.Bot(command_prefix='/', description=_DESCRIPTION)
rand = random.SystemRandom()

range_regex = re.compile('\d+-\d+')

@bot.command(pass_context=True)
async def roll(ctx, bound1=None, bound2=None):
    lower_bound = 1
    upper_bound = 100

    if bound2 is not None:
        lower_bound = int(bound1)
        upper_bound = int(bound2)
    elif bound1 is not None:
        if (range_regex.match(bound1)):
            range_split = bound1.split('-')
            lower_bound = int(range_split[0])
            upper_bound = int(range_split[1])
        else:
            upper_bound = int(bound1)

    lower_bound = max(lower_bound, 1)
    upper_bound = max(lower_bound, upper_bound)

    num_digits = len(str(upper_bound))
    format_string = '**%s** rolls (%d-%d): **%0' + str(num_digits) + 'd**'

    display_name = ctx.message.author.nick or ctx.message.author.name

    await bot.say(format_string % (display_name, lower_bound, upper_bound,
                                   rand.randint(lower_bound, upper_bound)))

@bot.command()
async def dbg(expr):
    if 'ALLOW_EVAL' in os.environ and os.environ['ALLOW_EVAL']:
        try:
            await bot.say('`%s`' % eval(expr))
        except Exception as e:
            await bot.say('%s: `%s`' % (type(e), e))
    else:
        await bot.say('Sorry, no eval!')

@bot.event
async def on_ready():
    print('logged in: %s (%s)' % (bot.user.name, bot.user.id))

    oauth_url = discord.utils.oauth_url(
        duckbot_settings.CLIENT_ID, permissions=discord.Permissions.text())
    print('invite me: %s' % oauth_url)

bot.run(duckbot_settings.TOKEN)
<commit_msg>Remove dbg/eval command<commit_after>import discord
import duckbot_settings
import random
import re
from discord.ext import commands

_DESCRIPTION = '''quack'''

bot = commands.Bot(command_prefix='/', description=_DESCRIPTION)
rand = random.SystemRandom()

range_regex = re.compile('\d+-\d+')

@bot.command(pass_context=True)
async def roll(ctx, bound1=None, bound2=None):
    lower_bound = 1
    upper_bound = 100

    if bound2 is not None:
        lower_bound = int(bound1)
        upper_bound = int(bound2)
    elif bound1 is not None:
        if (range_regex.match(bound1)):
            range_split = bound1.split('-')
            lower_bound = int(range_split[0])
            upper_bound = int(range_split[1])
        else:
            upper_bound = int(bound1)

    lower_bound = max(lower_bound, 1)
    upper_bound = max(lower_bound, upper_bound)

    num_digits = len(str(upper_bound))
    format_string = '**%s** rolls (%d-%d): **%0' + str(num_digits) + 'd**'

    display_name = ctx.message.author.nick or ctx.message.author.name

    await bot.say(format_string % (display_name, lower_bound, upper_bound,
                                   rand.randint(lower_bound, upper_bound)))

@bot.event
async def on_ready():
    print('logged in: %s (%s)' % (bot.user.name, bot.user.id))

    oauth_url = discord.utils.oauth_url(
        duckbot_settings.CLIENT_ID, permissions=discord.Permissions.text())
    print('invite me: %s' % oauth_url)

bot.run(duckbot_settings.TOKEN)
<|endoftext|>


<commit_before>class Solution(object):
    def canCross(self, stones):
        """
        :type stones: List[int]
        :rtype: bool
        """
        if stones[1] != 1:
            return False
        can_jump = [set() for __ in xrange(len(stones))]
        can_jump[0] = {0}
        farthest = 1
        for i in xrange(1, len(stones)):
            if stones[i] > farthest:
                return False
            m = 0
            for j in xrange(i):
                d = stones[i] - stones[j]
                if d in can_jump[j] or d - 1 in can_jump[j] or d + 1 in can_jump[j]:
                    can_jump[i].add(d)
                    m = max(m, d + 1)
            farthest = max(farthest, stones[i] + m)
        return bool(can_jump[-1])
<commit_msg>Simplify the code<commit_after>from collections import defaultdict

class Solution(object):
    def canCross(self, stones):
        """
        :type stones: List[int]
        :rtype: bool
        """
        stones_set = set(stones)
        incoming_jumps = defaultdict(set)
        incoming_jumps[1] = {1}
        for stone in stones[1:]:
            for jump in incoming_jumps[stone]:
                for d in xrange(-1 if jump > 1 else 0, +2):
                    if stone + jump + d in stones_set:
                        incoming_jumps[stone + jump + d].add(jump + d)
        return bool(incoming_jumps[stones[-1]])
<|endoftext|>