예제 #1
0
def test_column_quantity():

    # Regression test for pull request #356
    # Previously Pipeline.__getitem__ would return column data from tables as
    # an astropy.table.Column object. However, most functions take either
    # numpy.ndarray or astropy.units.Quantity objects as arguments. As of
    # astropy version 4.1.0 Column does not support all of the same class
    # methods as Quantity e.g. to_value. This test ensures that column data in
    # a Pipeline is accessed as either an ndarray or Quantity (depending on
    # units). It also checks that functions using methods not supported by
    # Column can be called on column data inside a Pipeline.

    def value_in_cm(q):
        return q.to_value(unit='cm')

    config = {
        'tables': {
            'test_table': {
                'lengths': Quantity(np.random.uniform(size=50), unit='m'),
                'lengths_in_cm': Call(value_in_cm, [Ref('test_table.lengths')])}}}

    pipeline = Pipeline(config)
    pipeline.execute()

    assert isinstance(pipeline['test_table.lengths'], Quantity)
    assert isinstance(pipeline['test_table.lengths_in_cm'], np.ndarray)
    np.testing.assert_array_less(0, pipeline['test_table.lengths_in_cm'])
    np.testing.assert_array_less(pipeline['test_table.lengths_in_cm'], 100)
예제 #2
0
def test_pipeline_cosmology():

    # Define function for testing pipeline cosmology
    from skypy.utils import uses_default_cosmology
    @uses_default_cosmology
    def return_cosmology(cosmology):
        return cosmology

    # Initial default_cosmology
    initial_default = default_cosmology.get()

    # Test pipeline correctly sets default cosmology from parameters
    # N.B. astropy cosmology class has not implemented __eq__ for comparison
    H0, Om0 = 70, 0.3
    config = {'parameters': {'H0': H0, 'Om0': Om0},
              'cosmology': (FlatLambdaCDM, ['$H0', '$Om0']),
              'test': (return_cosmology, ),
              }
    pipeline = Pipeline(config)
    pipeline.execute()
    assert type(pipeline['test']) == FlatLambdaCDM
    assert pipeline['test'].H0.value == H0
    assert pipeline['test'].Om0 == Om0

    # Test pipeline correctly updates cosmology from new parameters
    H0_new, Om0_new = 75, 0.25
    pipeline.execute({'H0': H0_new, 'Om0': Om0_new})
    assert type(pipeline['test']) == FlatLambdaCDM
    assert pipeline['test'].H0.value == H0_new
    assert pipeline['test'].Om0 == Om0_new

    # Check that the astropy default cosmology is unchanged
    assert default_cosmology.get() == initial_default
예제 #3
0
파일: test_items.py 프로젝트: sutieng/skypy
def test_call():
    from skypy.pipeline import Pipeline
    from skypy.pipeline._items import Call, Ref

    # set up a mock pipeline
    pipeline = Pipeline({})

    # function we will call
    def tester(arg1, arg2, *, kwarg1, kwarg2):
        return arg1, arg2, kwarg1, kwarg2

    # invalid construction
    with pytest.raises(TypeError, match='function is not callable'):
        Call(None, [], {})
    with pytest.raises(TypeError, match='args is not a sequence'):
        Call(tester, None, {})
    with pytest.raises(TypeError, match='kwargs is not a mapping'):
        Call(tester, [], None)

    # good construction with no args or kwargs
    call = Call(tester, [], {})

    # call has incomplete args
    with pytest.raises(TypeError, match=r'tester\(\)'):
        call.evaluate(pipeline)

    # good construction with arg1 and kwarg1
    call = Call(tester, [1], {'kwarg1': 3})

    # call still has incomplete args
    with pytest.raises(TypeError, match=r'tester\(\)'):
        call.evaluate(pipeline)

    # infer required arg2 and kwarg2 from context
    context = {
        'arg2': 2,
        'kwarg2': 4,
    }
    call.infer(context)

    # call should be evaluatable now
    result = call.evaluate(pipeline)
    assert result == (1, 2, 3, 4)

    # set up a call with references
    call = Call(tester, [Ref('var1'), 2], {'kwarg1': Ref('var3'), 'kwarg2': 4})

    # set up a pipeline with variables and a call that references them
    pipeline = Pipeline({'var1': 1, 'var3': 3})

    # check dependencies are resolved
    deps = call.depend(pipeline)
    assert deps == ['var1', 'var3']

    # execute the pipeline (sets state) and evaluate the call
    pipeline.execute()
    result = call.evaluate(pipeline)
    assert result == (1, 2, 3, 4)
예제 #4
0
def test_multi_column_assignment_failure(na, nt):

    # Test multi-column assignment failure with too few/many columns
    config = {'tables': {
                'multi_column_test_table': {
                  'a,b,c': Call(lambda nrows, ncols: np.ones((nrows, ncols)), [7, na]),
                  'd,e,f': Call(lambda nrows, ncols: (np.ones(nrows),) * ncols, [7, nt])}}}

    pipeline = Pipeline(config)
    with pytest.raises(ValueError):
        pipeline.execute()
예제 #5
0
def test_multi_column_assignment():

    # Test multi-column assignment from 2d arrays and tuples of 1d arrays
    config = {'tables': {
                'multi_column_test_table': {
                  'a,b ,c, d': Call(lambda nrows, ncols: np.ones((nrows, ncols)), [7, 4]),
                  'e , f,  g': Call(lambda nrows, ncols: (np.ones(nrows),) * ncols, [7, 3]),
                  'h': Call(list, [Ref('multi_column_test_table.a')]),
                  'i': Call(list, [Ref('multi_column_test_table.f')])}}}

    pipeline = Pipeline(config)
    pipeline.execute()
예제 #6
0
def test_hdf5():
    size = 100
    string = size*'a'
    config = {'tables': {
              'test_table': {
                'column1': Call(np.random.uniform, [], {
                  'size': size}),
                'column2': Call(np.random.uniform, [], {
                  'low': Ref('test_table.column1')}),
                'column3': Call(list, [string], {})}}}

    pipeline = Pipeline(config)
    pipeline.execute()
    pipeline.write('output.hdf5')
    hdf_table = read_table_hdf5('output.hdf5', 'tables/test_table', character_as_bytes=False)
    assert np.all(hdf_table == pipeline['test_table'])
예제 #7
0
def main(args=None):

    parser = argparse.ArgumentParser(description="SkyPy pipeline driver")
    parser.add_argument('--version', action='version', version=skypy_version)
    parser.add_argument('config', help='Config file name')
    parser.add_argument('output', help='Output file name')
    parser.add_argument('-o', '--overwrite', action='store_true',
                        help='Whether to overwrite existing files')
    parser.add_argument("-v", "--verbose", action="count", default=0,
                        help="Increase logging verbosity")
    parser.add_argument("-q", "--quiet", action="count", default=0,
                        help="Decrease logging verbosity")

    # get system args if none passed
    if args is None:
        args = sys.argv[1:]

    args = parser.parse_args(args or ['--help'])

    # Setup skypy logger
    default_level = logging._nameToLevel['WARNING']
    logging_level = default_level + 10 * (args.quiet - args.verbose)
    formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')
    stream_handler = logging.StreamHandler()
    stream_handler.setLevel(logging_level)
    stream_handler.setFormatter(formatter)
    logger = logging.getLogger('skypy')
    logger.setLevel(logging_level)
    logger.addHandler(stream_handler)

    try:
        config = load_skypy_yaml(args.config)
        pipeline = Pipeline(config)
        pipeline.execute()
        if args.output:
            logger.info(f"Writing {args.output}")
            pipeline.write(args.output, overwrite=args.overwrite)
    except Exception as e:
        logger.exception(e)
        raise SystemExit(2) from e

    return(0)
예제 #8
0
def test_pipeline_cosmology():

    def return_cosmology(cosmology):
        return cosmology

    # Test pipeline correctly sets default cosmology from parameters
    # N.B. astropy cosmology class has not implemented __eq__ for comparison
    H0, Om0 = 70, 0.3
    config = {'parameters': {'H0': H0, 'Om0': Om0},
              'cosmology': Call(FlatLambdaCDM, [Ref('H0'), Ref('Om0')]),
              'test': Call(return_cosmology),
              }
    pipeline = Pipeline(config)
    pipeline.execute()
    assert pipeline['test'] is pipeline['cosmology']

    # Test pipeline correctly updates cosmology from new parameters
    H0_new, Om0_new = 75, 0.25
    pipeline.execute({'H0': H0_new, 'Om0': Om0_new})
    assert pipeline['test'] is pipeline['cosmology']
예제 #9
0
def test_depends():

    # Regression test for GitHub Issue #464
    # Previously the .depends keyword was also being passed to functions as a
    # keyword argument. This was because Pipeline was executing Item.infer to
    # handle additional function arguments from context before handling
    # additional dependencies specified using the .depends keyword. The
    # .depends keyword is now handled first.

    config = {'tables': {
                'table_1': {
                  'column1': Call(np.random.uniform, [0, 1, 10])},
                'table_2': {
                    '.init': Call(vstack, [], {
                      'tables': [Ref('table_1')],
                      '.depends': ['table_1.complete']})}}}

    pipeline = Pipeline(config)
    pipeline.execute()
    assert np.all(pipeline['table_1'] == pipeline['table_2'])
예제 #10
0
def test_unknown_reference():
    config = {'param1': Ref('param2')}
    pipeline = Pipeline(config)
    with pytest.raises(KeyError):
        pipeline.execute()

    config = {'mydict': {
                'param1': Ref('mydict.param2')}}
    pipeline = Pipeline(config)
    with pytest.raises(KeyError):
        pipeline.execute()

    config = {'tables': {
                'mytable': {
                  'mycolumn': [0, 1, 2]}},
              'myvalue': Ref('mytable.myothercolumn')}
    pipeline = Pipeline(config)
    with pytest.raises(KeyError):
        pipeline.execute()
예제 #11
0
def test_pipeline():

    # Evaluate and store the default astropy cosmology.
    config = {'test_cosmology': (default_cosmology.get, )}

    pipeline = Pipeline(config)
    pipeline.execute()
    assert pipeline['test_cosmology'] == default_cosmology.get()

    # Generate a simple two column table with a dependency. Also write the
    # table to a fits file and check it's contents.
    size = 100
    string = size * 'a'
    config = {
        'tables': {
            'test_table': {
                'column1': (np.random.uniform, {
                    'size': size
                }),
                'column2': (np.random.uniform, {
                    'low': '$test_table.column1'
                }),
                'column3': (list, [string])
            }
        }
    }

    pipeline = Pipeline(config)
    pipeline.execute()
    pipeline.write(file_format='fits')
    assert len(pipeline['test_table']) == size
    assert np.all(
        pipeline['test_table.column1'] < pipeline['test_table.column2'])
    with fits.open('test_table.fits') as hdu:
        assert np.all(Table(hdu[1].data) == pipeline['test_table'])

    # Check for failure if output files already exist and overwrite is False
    pipeline = Pipeline(config)
    pipeline.execute()
    with pytest.raises(OSError):
        pipeline.write(file_format='fits', overwrite=False)

    # Check that the existing output files are modified if overwrite is True
    new_size = 2 * size
    new_string = new_size * 'a'
    config['tables']['test_table']['column1'][1]['size'] = new_size
    config['tables']['test_table']['column3'][1][0] = new_string
    pipeline = Pipeline(config)
    pipeline.execute()
    pipeline.write(file_format='fits', overwrite=True)
    with fits.open('test_table.fits') as hdu:
        assert len(hdu[1].data) == new_size

    # Check for failure if 'column1' requires itself creating a cyclic
    # dependency graph
    config['tables']['test_table']['column1'] = (list, '$test_table.column1')
    with pytest.raises(networkx.NetworkXUnfeasible):
        Pipeline(config).execute()

    # Check for failure if 'column1' and 'column2' both require each other
    # creating a cyclic dependency graph
    config['tables']['test_table']['column1'] = (list, '$test_table.column2')
    with pytest.raises(networkx.NetworkXUnfeasible):
        Pipeline(config).execute()

    # Check for failure if 'column1' is removed from the config so that the
    # requirements for 'column2' are not satisfied.
    del config['tables']['test_table']['column1']
    with pytest.raises(KeyError):
        Pipeline(config).execute()

    # Check variables intialised by value
    config = {
        'test_int': 1,
        'test_float': 1.0,
        'test_string': 'hello world',
        'test_list': [0, 'one', 2.],
        'test_dict': {
            'a': 'b'
        }
    }
    pipeline = Pipeline(config)
    pipeline.execute()
    assert isinstance(pipeline['test_int'], int)
    assert isinstance(pipeline['test_float'], float)
    assert isinstance(pipeline['test_string'], str)
    assert isinstance(pipeline['test_list'], list)
    assert isinstance(pipeline['test_dict'], dict)
    assert pipeline['test_int'] == 1
    assert pipeline['test_float'] == 1.0
    assert pipeline['test_string'] == 'hello world'
    assert pipeline['test_list'] == [0, 'one', 2.]
    assert pipeline['test_dict'] == {'a': 'b'}

    # Check variables intialised by function
    config = {
        'test_func': (list, 'hello world'),
        'len_of_test_func': (len, '$test_func'),
        'nested_references': (sum, [['$test_func', [' '], '$test_func'], []]),
        'nested_functions': (list, (range, (len, '$test_func')))
    }
    pipeline = Pipeline(config)
    pipeline.execute()
    assert pipeline['test_func'] == list('hello world')
    assert pipeline['len_of_test_func'] == len('hello world')
    assert pipeline['nested_references'] == list('hello world hello world')
    assert pipeline['nested_functions'] == list(range(len('hello world')))

    # Check parameter initialisation
    config = {'parameters': {'param1': 1.0}}
    pipeline = Pipeline(config)
    pipeline.execute()
    assert pipeline['param1'] == 1.0

    # Update parameter and re-run
    new_parameters = {'param1': 5.0}
    pipeline.execute(parameters=new_parameters)
    assert pipeline['param1'] == new_parameters['param1']
예제 #12
0
    def execute(self, parameters={}):

        # Lightcone parameters
        z_min = self.lightcone_config['z_min']
        z_max = self.lightcone_config['z_max']
        n_slice = self.lightcone_config['n_slice']

        params = {
            'z_min': z_min,
            'z_max': z_min,
            'slice_z_min': None,
            'slice_z_max': None,
            'slice_z_mid': None,
        }

        # Additional user-defined parameters
        params.update(parameters)

        # Update config with ligthcone parameters and user parameters
        if 'parameters' in self.config:
            self.config['parameters'].update(params)
        else:
            self.config['parameters'] = params

        # SkyPy Pipeline object
        pipeline = Pipeline(self.config)

        # Initialise empty tables
        self.tables = {k: Table() for k in pipeline.table_config.keys()}

        # Cosmology from pipeline
        if pipeline.cosmology:
            self.cosmology = pipeline.get_value(pipeline.cosmology)
        else:
            self.cosmology = default_cosmology.get()

        # Calculate equispaced comoving distance slices in redshift space
        chi_min = self.cosmology.comoving_distance(z_min)
        chi_max = self.cosmology.comoving_distance(z_max)
        chi = np.linspace(chi_min, chi_max, n_slice + 1)
        chi_mid = (chi[:-1] + chi[1:]) / 2
        z = [
            z_at_value(self.cosmology.comoving_distance, c, z_min, z_max)
            for c in chi[1:-1]
        ]
        z_mid = [
            z_at_value(self.cosmology.comoving_distance, c, z_min, z_max)
            for c in chi_mid
        ]
        redshift_slices = zip([z_min] + z, z + [z_max], z_mid)

        # Simulate redshift slices and append results to tables
        for slice_z_min, slice_z_max, slice_z_mid in redshift_slices:
            slice_params = {
                'slice_z_min': slice_z_min,
                'slice_z_max': slice_z_max,
                'slice_z_mid': slice_z_mid
            }
            pipeline.execute(parameters=slice_params)
            for k, v in self.tables.items():
                self.tables[k] = vstack((v, pipeline[k]))