Example #1
0
class ComplexSector(ScalarSamplingSet):
    """
    Represents an annular sector in the complex plane from which to sample,
    based on a given range of modulus and argument.

    Config:
        modulus (list): Range for the modulus (default [1,3])
        argument (list): Range for the argument (default [0,pi/2])

    Usage
    =====
    Sample from the unit circle
    >>> sect = ComplexSector(modulus=[0,1], argument=[-np.pi,np.pi])
    """
    schema_config = Schema({
        Required('modulus', default=[1, 3]):
        NumberRange(),
        Required('argument', default=[0, np.pi / 2]):
        NumberRange()
    })

    def __init__(self, config=None, **kwargs):
        """
        Configure the class as normal, then set up the modulus and argument
        parts as RealInterval objects
        """
        super(ComplexSector, self).__init__(config, **kwargs)
        self.modulus = RealInterval(self.config['modulus'])
        self.argument = RealInterval(self.config['argument'])

    def gen_sample(self):
        """Generates a random sample in the defined annular sector in the complex plane"""
        return self.modulus.gen_sample() * np.exp(
            1j * self.argument.gen_sample())
Example #2
0
class ComplexRectangle(ScalarSamplingSet):
    """
    Represents a rectangle in the complex plane from which to sample.

    Config:
        re (list): Range for the real component (default [1,3])
        im (list): Range for the imaginary component (default [1,3])

    Usage
    =====
    >>> rect = ComplexRectangle(re=[1,4], im=[-5,0])
    """
    schema_config = Schema({
        Required('re', default=[1, 3]): NumberRange(),
        Required('im', default=[1, 3]): NumberRange()
    })

    def __init__(self, config=None, **kwargs):
        """
        Configure the class as normal, then set up the real and imaginary
        parts as RealInterval objects
        """
        super(ComplexRectangle, self).__init__(config, **kwargs)
        self.re = RealInterval(self.config['re'])
        self.im = RealInterval(self.config['im'])

    def gen_sample(self):
        """Generates a random sample in the defined rectangle in the complex plane"""
        return self.re.gen_sample() + self.im.gen_sample() * 1j
Example #3
0
class RealInterval(ScalarSamplingSet):
    """
    Represents an interval of real numbers from which to sample.

    Config:
        start (float): Lower end of the range (default 1)
        stop (float): Upper end of the range (default 5)

    Usage
    =====
    Generate random floats betweens -2 and 4
    >>> ri = RealInterval(start=-2, stop=4)

    You can also initialize with an interval as a list:
    >>> ri = RealInterval([-2,4])
    """
    schema_config = NumberRange()

    def __init__(self, config=None, **kwargs):
        """
        Validate the specified configuration.
        First apply the voluptuous validation.
        Then ensure that the start and stop are the right way around.
        """
        super(RealInterval, self).__init__(config, **kwargs)
        if self.config['start'] > self.config['stop']:
            self.config['start'], self.config['stop'] = self.config[
                'stop'], self.config['start']

    def gen_sample(self):
        """Returns a random real number in the range [start, stop]"""
        start, stop = self.config['start'], self.config['stop']
        return start + (stop - start) * np.random.random_sample()
Example #4
0
class IntegerRange(ScalarSamplingSet):
    """
    Represents an interval of integers from which to sample.

    Config:
        start (int): Lower end of the range (default 1)
        stop (int): Upper end of the range (default 5)

    Both start and stop are included in the interval.

    Usage
    =====
    Generate random integers betweens -2 and 4
    >>> integer = IntegerRange(start=-2, stop=4)

    You can also initialize with an interval:
    >>> integer = IntegerRange([-2,4])
    """
    schema_config = NumberRange(int)

    def __init__(self, config=None, **kwargs):
        """
        Validate the specified configuration.
        First apply the voluptuous validation.
        Then ensure that the start and stop are the right way around.
        """
        super(IntegerRange, self).__init__(config, **kwargs)
        if self.config['start'] > self.config['stop']:
            self.config['start'], self.config['stop'] = self.config[
                'stop'], self.config['start']

    def gen_sample(self):
        """Returns a random integer in range(start, stop)"""
        return np.random.randint(low=self.config['start'],
                                 high=self.config['stop'] + 1)
Example #5
0
class RealMathArrays(VariableSamplingSet):
    """
    Represents a collection of real arrays with specified norm from which to
    draw random samples.

    The norm used is standard Euclidean norm: root-sum of all entries in the array.

    Config:
    =======
        shape (int|(int)|[int]): the array shape
        norm ([start, stop]): Real interval from which to sample the array's norm
            defaults to [1, 5]

    Usage
    ========
    Sample tensors with shape [4, 2, 5]:
    >>> real_tensors = RealMathArrays(shape=[4, 2, 5])
    >>> sample = real_tensors.gen_sample()
    >>> sample.shape
    (4, 2, 5)

    Samples are of class MathArray:
    >>> isinstance(sample, MathArray)
    True

    Specify a range for the tensor's norm:
    >>> real_tensors = RealMathArrays(shape=[4, 2, 5], norm=[10, 20])
    >>> sample = real_tensors.gen_sample()
    >>> 10 < np.linalg.norm(sample) < 20
    True
    """

    schema_config = Schema({
        Required('shape'): is_shape_specification(min_dim=1),
        Required('norm', default=[1, 5]): NumberRange()
    })

    def __init__(self, config=None, **kwargs):
        """
        Configure the class as normal, then set up norm as a RealInterval
        """
        super(RealMathArrays, self).__init__(config, **kwargs)
        self.norm = RealInterval(self.config['norm'])

    def gen_sample(self):
        """
        Generates a random matrix of shape and norm determined by config.
        """
        desired_norm = self.norm.gen_sample()
        # construct an array with entries in [-0.5, 0.5)
        array = np.random.random_sample(self.config['shape']) - 0.5
        actual_norm = np.linalg.norm(array)
        # convert the array to a matrix with desired norm
        return MathArray(array) * desired_norm / actual_norm
Example #6
0
class ArraySamplingSet(VariableSamplingSet):
    """
    Represents a set from which random array variable samples are taken.

    The norm used is standard Euclidean norm: root-square-sum of all entries in the array.

    This is the most low-level array sampling set we have, and is subclassed for various
    specific purposes. While we cannot make this class abstract, we strongly discourage
    its use.

    Config:
    =======
        - shape (int|(int)|[int]): Dimensions of the array, specified as a list or tuple of
            the dimensions in each index as (n_1, n_2, ...). Can also use an integer
            to select a vector of that length. (required; no default)
        - norm ([start, stop]): Range for the overall norm of the array. Can be a
            list [start, stop] or a dictionary {'start':start, 'stop':stop}.
            (default [1, 5])
        - complex (bool): Whether or not the matrix is complex (default False)
    """

    schema_config = Schema({
        Required('shape'): is_shape_specification(min_dim=1),
        Required('norm', default=[1, 5]): NumberRange(),
        Required('complex', default=False): bool
    })

    def __init__(self, config=None, **kwargs):
        """
        Configure the class as normal, then set up norm as a RealInterval
        """
        super(ArraySamplingSet, self).__init__(config, **kwargs)
        self.norm = RealInterval(self.config['norm'])

    def gen_sample(self):
        """
        Generates an array sample and returns it as a MathArray.

        This calls generate_sample, which is the routine that should be subclassed if
        needed, rather than this one.
        """
        array = self.generate_sample()
        return MathArray(array)

    def generate_sample(self):
        """
        Generates a random array of shape and norm determined by config. After
        generation, the apply_symmetry and normalize functions are applied to the result.
        These functions may be shadowed by a subclass.

        If apply_symmetry or normalize raise the Retry exception, a new sample is
        generated, and the procedure starts anew.

        Returns a numpy array.
        """
        # Loop until a good sample is found
        loops = 0
        while loops < 100:
            loops += 1

            # Construct an array with entries in [-0.5, 0.5)
            array = np.random.random_sample(self.config['shape']) - 0.5
            # Make the array complex if needed
            if self.config['complex']:
                imarray = np.random.random_sample(self.config['shape']) - 0.5
                array = array + 1j*imarray

            try:
                # Apply any symmetries to the array
                array = self.apply_symmetry(array)

                # Normalize the result
                array = self.normalize(array)

                # Return the result
                return array
            except Retry:
                continue

        raise ValueError('Unable to construct sample for {}'
                         .format(type(self).__name__))  # pragma: no cover

    def apply_symmetry(self, array):
        """
        Applies the required symmetries to the array.

        This method exists to be shadowed by subclasses.
        """
        return array

    def normalize(self, array):
        """
        Normalizes the array to fall into the desired norm.

        This method can be shadowed by subclasses.
        """
        actual_norm = np.linalg.norm(array)
        desired_norm = self.norm.gen_sample()
        return array * desired_norm / actual_norm