diff --git a/autofit/__init__.py b/autofit/__init__.py index b5e1db99f..5e29ebd81 100644 --- a/autofit/__init__.py +++ b/autofit/__init__.py @@ -1,61 +1,63 @@ -from autoconf import conf -from autofit.mapper.model import path_instances_of_class -from autofit.mapper.prior_model.attribute_pair import ( - cast_collection, - AttributeNameValue, - PriorNameValue, - InstanceNameValue, -) - -dir(conf) +from . import conf from . import exc -from autofit.optimize.non_linear.samples import AbstractSamples, MCMCSamples, NestedSamplerSamples -from .aggregator import Aggregator, PhaseOutput -from .mapper import * +from .aggregator import Aggregator +from .aggregator import PhaseOutput from .mapper import link +from .mapper import prior from .mapper.model import AbstractModel from .mapper.model import ModelInstance from .mapper.model import ModelInstance as Instance +from .mapper.model import path_instances_of_class from .mapper.model_mapper import ModelMapper from .mapper.model_mapper import ModelMapper as Mapper from .mapper.model_object import ModelObject -from .mapper.prior_model import * +from .mapper.prior import AbstractPromise +from .mapper.prior import GaussianPrior +from .mapper.prior import LogUniformPrior +from .mapper.prior import Prior +from .mapper.prior import UniformPrior +from .mapper.prior import last +from .mapper.prior.deferred import DeferredArgument +from .mapper.prior.deferred import DeferredInstance from .mapper.prior_model.abstract import AbstractPriorModel from .mapper.prior_model.annotation import AnnotationPriorModel +from .mapper.prior_model.attribute_pair import AttributeNameValue +from .mapper.prior_model.attribute_pair import InstanceNameValue +from .mapper.prior_model.attribute_pair import PriorNameValue +from .mapper.prior_model.attribute_pair import cast_collection from .mapper.prior_model.collection import CollectionPriorModel from .mapper.prior_model.collection import CollectionPriorModel as Collection -from autofit.mapper.prior.deferred import DeferredArgument -from autofit.mapper.prior.deferred import DeferredInstance -from .mapper.prior_model.dimension_type import DimensionType, map_types +from .mapper.prior_model.dimension_type import DimensionType +from .mapper.prior_model.dimension_type import map_types from .mapper.prior_model.prior_model import PriorModel from .mapper.prior_model.prior_model import PriorModel as Model from .mapper.prior_model.util import PriorModelNameValue from .optimize.grid_search import GridSearch as OptimizerGridSearch -from .optimize import * -from .optimize.non_linear.downhill_simplex import DownhillSimplex -from .optimize.non_linear.nested_sampling.dynesty import DynestyStatic, DynestyDynamic from .optimize.grid_search import GridSearchResult -from .optimize.non_linear.nested_sampling.multi_nest import MultiNest +from .optimize.non_linear.downhill_simplex import DownhillSimplex +from .optimize.non_linear.emcee import Emcee from .optimize.non_linear.mock_nlo import MockNLO +from .optimize.non_linear.nested_sampling.dynesty import DynestyDynamic +from .optimize.non_linear.nested_sampling.dynesty import DynestyStatic +from .optimize.non_linear.nested_sampling.multi_nest import MultiNest from .optimize.non_linear.non_linear import Analysis from .optimize.non_linear.non_linear import NonLinearOptimizer -from .optimize.non_linear.emcee import Emcee -from autofit.optimize.non_linear.paths import Paths -from autofit.optimize.non_linear.paths import make_path -from autofit.optimize.non_linear.paths import convert_paths from .optimize.non_linear.non_linear import Result -from .text import formatter, samples_text -from .tools import * +from .optimize.non_linear.paths import Paths +from .optimize.non_linear.paths import convert_paths +from .optimize.non_linear.paths import make_path +from .optimize.non_linear.samples import AbstractSamples +from .optimize.non_linear.samples import MCMCSamples +from .optimize.non_linear.samples import NestedSamplerSamples +from .text import formatter +from .text import samples_text from .tools import path_util from .tools.phase import AbstractPhase -from .tools.phase import Phase from .tools.phase import Dataset +from .tools.phase import Phase from .tools.phase import as_grid_search from .tools.phase_property import PhaseProperty from .tools.pipeline import Pipeline from .tools.pipeline import ResultsCollection -from autofit.mapper.prior import AbstractPromise -from autofit.mapper.prior import last -from .mapper.prior import * __version__ = '0.58.0' diff --git a/autofit/conf.py b/autofit/conf.py new file mode 100644 index 000000000..e67960ff1 --- /dev/null +++ b/autofit/conf.py @@ -0,0 +1 @@ +from autoconf.conf import * \ No newline at end of file diff --git a/autofit/exc.py b/autofit/exc.py index 3709ab006..578c7d779 100644 --- a/autofit/exc.py +++ b/autofit/exc.py @@ -36,3 +36,7 @@ class DeferredInstanceException(Exception): """ pass + + +class AggregatorException(Exception): + pass \ No newline at end of file diff --git a/autofit/mapper/prior/prior.py b/autofit/mapper/prior/prior.py index 2e948d7e2..566460c2c 100644 --- a/autofit/mapper/prior/prior.py +++ b/autofit/mapper/prior/prior.py @@ -7,7 +7,7 @@ import numpy as np from scipy.special import erfcinv -from autoconf import conf +from autofit import conf from autofit import exc from autofit.mapper.model_object import ModelObject from autofit.mapper.prior.arithmetic import ArithmeticMixin diff --git a/autofit/mapper/prior_model/abstract.py b/autofit/mapper/prior_model/abstract.py index 360e0289a..35beb5305 100644 --- a/autofit/mapper/prior_model/abstract.py +++ b/autofit/mapper/prior_model/abstract.py @@ -10,7 +10,6 @@ import autofit.mapper.model import autofit.mapper.model_mapper import autofit.mapper.prior_model.collection -from autofit import cast_collection, PriorNameValue, InstanceNameValue from autofit import exc from autofit.mapper.model import AbstractModel from autofit.mapper.prior.deferred import DeferredArgument @@ -18,6 +17,7 @@ from autofit.mapper.prior.prior import TuplePrior, Prior, WidthModifier, Limits from autofit.mapper.prior_model import dimension_type as dim from autofit.mapper.prior_model.attribute_pair import DeferredNameValue +from autofit.mapper.prior_model.attribute_pair import cast_collection, PriorNameValue, InstanceNameValue from autofit.mapper.prior_model.recursion import DynamicRecursionCache from autofit.mapper.prior_model.util import PriorModelNameValue from autofit.text.formatter import TextFormatter @@ -429,7 +429,7 @@ def instance_from_prior_medians(self): def log_priors_from_vector( self, - vector : [float], + vector: [float], ): """ Compute the log priors of every parameter in a vector, using the Prior of every parameter. @@ -449,13 +449,12 @@ def log_priors_from_vector( """ return list( map( - lambda prior_tuple, value : prior_tuple.prior.log_prior_from_value(value=value), + lambda prior_tuple, value: prior_tuple.prior.log_prior_from_value(value=value), self.prior_tuples_ordered_by_id, vector, ) ) - def random_instance(self): """ Creates a random instance of the model. diff --git a/autofit/mapper/prior_model/util.py b/autofit/mapper/prior_model/util.py index c3a938f89..ac462cf72 100644 --- a/autofit/mapper/prior_model/util.py +++ b/autofit/mapper/prior_model/util.py @@ -1,4 +1,4 @@ -from autofit import AttributeNameValue +from autofit.mapper.prior_model.attribute_pair import AttributeNameValue class PriorModelNameValue(AttributeNameValue): diff --git a/autofit/optimize/non_linear/mock_nlo.py b/autofit/optimize/non_linear/mock_nlo.py index 2ad67cb83..251ff0129 100644 --- a/autofit/optimize/non_linear/mock_nlo.py +++ b/autofit/optimize/non_linear/mock_nlo.py @@ -1,7 +1,7 @@ import math import autofit as af -from autofit import AbstractSamples +from autofit.optimize.non_linear.samples import AbstractSamples from autofit.optimize.non_linear.non_linear import NonLinearOptimizer from autofit.optimize.non_linear.non_linear import Analysis diff --git a/autofit/tools/edenise.py b/autofit/tools/edenise.py new file mode 100644 index 000000000..4066cc147 --- /dev/null +++ b/autofit/tools/edenise.py @@ -0,0 +1,164 @@ +import re +import shutil +from os import walk +from uuid import uuid1 + + +class Line: + def __init__(self, string): + if "*" in string: + print("Please ensure no imports in the __init__ contain a *") + exit(1) + if "," in string: + print("Comma separated imports not allowed") + exit(1) + self.string = string.replace("\n", "") + self.id = str(uuid1()) + + def __str__(self): + return f"{self.source} -> {self.import_target}" + + def __repr__(self): + return f"<{self.__class__.__name__} {self}>" + + def __len__(self): + return len(self.source) + + def __lt__(self, other): + return len(self) < len(other) + + def __gt__(self, other): + return len(self) > len(other) + + def __eq__(self, other): + return self._target == other._target + + @property + def is_import(self): + return self.string.startswith("from") + + @property + def source(self): + match = re.match("from .* as (.+)", self.string) + if match is not None: + return match.group(1) + return re.match("from .* import (.+)", self.string).group(1) + + @property + def target(self): + return self._target.split(".")[-1] + + @property + def import_target(self): + target = ".".join( + self._target.split(".")[:-1] + ) + if len(target) > 0: + return f".{target}" + return target + + def __hash__(self): + return hash(self._target) + + @property + def _target(self): + return self.string.replace( + f" as {self.source}", + "" + ).replace( + "from ", + "" + ).replace( + " import ", + "." + ).lstrip( + "." + ) + + +class Converter: + def __init__(self, name, prefix, lines): + self.name = name + self.prefix = prefix + self.lines = sorted( + filter( + lambda line: line.is_import, + lines + ), + reverse=True + ) + + @classmethod + def from_prefix_and_source_directory( + cls, + name, + prefix, + source_directory + ): + source_directory = source_directory + with open( + f"{source_directory}/__init__.py" + ) as f: + lines = map(Line, f.readlines()) + return Converter(name, prefix, lines) + + def convert(self, string): + matched_lines = set() + string = string.replace(f"import {self.name} as {self.prefix}", "") + for line in self.lines: + source = f"{self.prefix}.{line.source}" + if source in string: + matched_lines.add( + line + ) + string = string.replace( + source, + line.id + ) + for line in self.lines: + string = string.replace( + line.id, + line.target + ) + + import_string = "\n".join( + f"from {self.name}{line.import_target} import {line.target}" + for line in matched_lines + ) + return f"{import_string}\n{string}" + + +def edenise( + root_directory, + name, + prefix +): + target_directory = f"{root_directory}/../{name}_eden" + + print(f"Creating {target_directory}...") + shutil.copytree( + root_directory, + target_directory, + symlinks=True + ) + + converter = Converter.from_prefix_and_source_directory( + name=name, + prefix=prefix, + source_directory=f"{root_directory}/{name}" + ) + + for root, _, files in walk(f"{target_directory}/test_{name}"): + for file in files: + if file.endswith(".py"): + with open(f"{root}/{file}", "r+") as f: + string = f.read() + f.seek(0) + f.write( + converter.convert( + string + ) + ) + f.truncate() + + open(f"{target_directory}/{name}/__init__.py", "w+").close() diff --git a/eden.ini b/eden.ini new file mode 100644 index 000000000..4cf8e45b0 --- /dev/null +++ b/eden.ini @@ -0,0 +1,3 @@ +[eden] +name = autofit +prefix = af \ No newline at end of file diff --git a/scripts/edenise.py b/scripts/edenise.py new file mode 100755 index 000000000..c27388310 --- /dev/null +++ b/scripts/edenise.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +from configparser import ConfigParser +from sys import argv + +from autofit.tools import edenise + + +def main( + root_directory +): + try: + config = ConfigParser() + config.read( + f"{root_directory}/eden.ini" + ) + + edenise.edenise( + root_directory, + config.get("eden", "name"), + config.get("eden", "prefix") + ) + except ValueError: + print("Usage: ./edenise.py root_directory") + exit(1) + + +if __name__ == "__main__": + main( + argv[1] + ) diff --git a/test_autofit/conftest.py b/test_autofit/conftest.py index 95833788c..b68a23e53 100644 --- a/test_autofit/conftest.py +++ b/test_autofit/conftest.py @@ -3,7 +3,7 @@ import pytest import autofit as af -from autoconf import conf +from autofit import conf import shutil directory = path.dirname(path.realpath(__file__)) diff --git a/test_autofit/unit/config/json_priors/test_autofit.json b/test_autofit/unit/config/json_priors/test_autofit.json index d918d8cdd..0aa95be32 100644 --- a/test_autofit/unit/config/json_priors/test_autofit.json +++ b/test_autofit/unit/config/json_priors/test_autofit.json @@ -135,6 +135,37 @@ "upper": 1.0 } } + }, + "Tracer": { + "lens_galaxy": { + "type": "Uniform", + "lower_limit": 0.0, + "upper_limit": 1.0, + "width_modifier": { + "type": "Absolute", + "value": 0.2 + }, + "gaussian_limits": { + "lower": 0.0, + "upper": 1.0 + } + }, + "source_galaxy": { + "type": "Uniform", + "lower_limit": 0.0, + "upper_limit": 1.0, + "width_modifier": { + "type": "Absolute", + "value": 0.2 + }, + "gaussian_limits": { + "lower": 0.0, + "upper": 1.0 + } + }, + "grid": { + "type": "Deferred" + } } } -} \ No newline at end of file +} diff --git a/test_autofit/unit/mapper/mock.py b/test_autofit/unit/mapper/mock.py new file mode 100644 index 000000000..6b3a5edeb --- /dev/null +++ b/test_autofit/unit/mapper/mock.py @@ -0,0 +1,631 @@ +import inspect +import math +import typing + +import autofit as af +# noinspection PyAbstractClass +import autofit.mapper.prior_model.attribute_pair +from autofit import Paths +from autofit.tools.phase import Dataset + + +class MockNonLinearOptimizer(object): + def __init__( + self, + phase_name, + phase_tag=None, + phase_folders=tuple(), + most_probable=None, + model_mapper=None, + max_log_likelihood=None, + model_upper_params=None, + model_lower_params=None, + ): + super(MockNonLinearOptimizer, self).__init__( + model_mapper or af.ModelMapper(), + Paths( + phase_name=phase_name, phase_tag=phase_tag, phase_folders=phase_folders + ), + ) + + self.most_probable = most_probable + self.max_log_likelihood = max_log_likelihood + self.model_upper_params = model_upper_params + self.model_lower_params = model_lower_params + + def _simple_fit(self, model, fitness_function): + raise NotImplementedError() + + def _fit(self, model, analysis): + raise NotImplementedError() + + @property + def most_probable_vector(self): + """ + Read the most probable or most likely model values from the 'obj_summary.txt' + file which nlo from a multinest lens. + + This file stores the parameters of the most probable model in the first half + of entries and the most likely model in the second half of entries. The + offset parameter is used to start at the desiredaf. + + """ + return self.most_probable + + @property + def max_log_likelihood_vector(self): + """ + Read the most probable or most likely model values from the 'obj_summary.txt' + file which nlo from a \ multinest lens. + + This file stores the parameters of the most probable model in the first half + of entries and the most likely model in the second half of entries. The + offset parameter is used to start at the desiredaf. + """ + return self.max_log_likelihood + + def vector_at_upper_sigma(self, sigma): + return self.model_upper_params + + def vector_at_lower_sigma(self, sigma): + return self.model_lower_params + + +class MockClassNLOx2: + def __init__(self, one=1, two=2): + self.one = one + self.two = two + + +class MockClassNLOx4: + def __init__(self, one=1, two=2, three=3, four=4): + self.one = one + self.two = two + self.three = three + self.four = four + + +class MockClassNLOx5: + def __init__(self, one=1, two=2, three=3, four=4, five=5): + self.one = one + self.two = two + self.three = three + self.four = four + self.five = five + + +class MockClassNLOx6: + def __init__(self, one=(1, 2), two=(3, 4), three=3, four=4): + self.one = one + self.two = two + self.three = three + self.four = four + + +class MockAnalysis: + def __init__(self): + self.kwargs = None + self.instance = None + self.visualize_instance = None + + def fit(self, instance): + self.instance = instance + return 1.0 + + # noinspection PyUnusedLocal + def visualize(self, instance, *args, **kwargs): + self.visualize_instance = instance + + +class Circle: + def __init__(self, radius): + self.radius = radius + + def with_circumference(self, circumference): + self.circumference = circumference + + @property + def circumference(self): + return self.radius * 2 * math.pi + + @circumference.setter + def circumference(self, circumference): + self.radius = circumference / (2 * math.pi) + + +class SimpleClass: + def __init__(self, one, two: float): + self.one = one + self.two = two + + +class ComplexClass: + def __init__(self, simple: SimpleClass): + self.simple = simple + + +class ListClass: + def __init__(self, ls: list): + self.ls = ls + + +class Distance(af.DimensionType): + pass + + +class DistanceClass: + @af.map_types + def __init__(self, first: Distance, second: Distance): + self.first = first + self.second = second + + +class PositionClass: + @af.map_types + def __init__(self, position: typing.Tuple[Distance, Distance]): + self.position = position + + +class DeferredClass: + def __init__(self, one, two): + self.one = one + self.two = two + + +class Galaxy: + def __init__( + self, + light_profiles: list = None, + mass_profiles: list = None, + redshift=None, + **kwargs + ): + self.redshift = redshift + self.light_profiles = light_profiles + self.mass_profiles = mass_profiles + self.kwargs = kwargs + + +class RelativeWidth: + def __init__(self, one, two, three): + self.one = one + self.two = two + self.three = three + + +class Redshift: + def __init__(self, redshift): + self.redshift = redshift + + +# noinspection PyAbstractClass +class GalaxyModel(af.AbstractPriorModel): + def instance_for_arguments(self, arguments): + try: + return Galaxy(redshift=self.redshift.instance_for_arguments(arguments)) + except AttributeError: + return Galaxy() + + def __init__(self, model_redshift=False, **kwargs): + super().__init__() + self.redshift = af.PriorModel(Redshift) if model_redshift else None + print(self.redshift) + self.__dict__.update( + { + key: af.PriorModel(value) if inspect.isclass(value) else value + for key, value in kwargs.items() + } + ) + + @property + def instance_tuples(self): + return [] + + @property + @autofit.mapper.prior_model.attribute_pair.cast_collection( + autofit.mapper.prior_model.attribute_pair.PriorNameValue + ) + def unique_prior_tuples(self): + return ( + [item for item in self.__dict__.items() if isinstance(item[1], af.Prior)] + + [("redshift", self.redshift.redshift)] + if self.redshift is not None + else [] + ) + + @property + @autofit.mapper.prior_model.attribute_pair.cast_collection(af.PriorModelNameValue) + def flat_prior_model_tuples(self): + return [ + item + for item in self.__dict__.items() + if isinstance(item[1], af.AbstractPriorModel) + ] + + +class GeometryProfile: + def __init__(self, centre=(0.0, 0.0)): + """Abstract GeometryProfile, describing an object with y, x cartesian + coordinates """ + self.centre = centre + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + +class SphericalProfile(GeometryProfile): + def __init__(self, centre=(0.0, 0.0)): + """ Generic circular profiles class to contain functions shared by light and + mass profiles. + + Parameters + ---------- + centre: (float, float) + The (y,x) coordinates of the origin of the profile. + """ + super(SphericalProfile, self).__init__(centre) + + +class EllipticalProfile(SphericalProfile): + def __init__(self, centre=(0.0, 0.0), axis_ratio=1.0, phi=0.0): + """ Generic elliptical profiles class to contain functions shared by light + and mass profiles. + + Parameters + ---------- + centre: (float, float) + The (y,x) coordinates of the origin of the profiles + axis_ratio : float + Ratio of profiles ellipse's minor and major axes (b/a) + phi : float + Rotational angle of profiles ellipse counter-clockwise from positive x-axis + """ + super(EllipticalProfile, self).__init__(centre) + self.axis_ratio = axis_ratio + self.phi = phi + + +class EllipticalLP(EllipticalProfile): + """Generic class for an elliptical light profiles""" + + def __init__(self, centre=(0.0, 0.0), axis_ratio=1.0, phi=0.0): + """ Abstract class for an elliptical light-profile. + + Parameters + ---------- + centre: (float, float) + The (y,x) coordinates of the origin of the profiles + axis_ratio : float + Ratio of light profiles ellipse's minor and major axes (b/a) + phi : float + Rotational angle of profiles ellipse counter-clockwise from positive x-axis + """ + super(EllipticalLP, self).__init__(centre, axis_ratio, phi) + + +class AbstractEllipticalSersic(EllipticalProfile): + def __init__( + self, + centre=(0.0, 0.0), + axis_ratio=1.0, + phi=0.0, + intensity=0.1, + effective_radius=0.6, + sersic_index=4.0, + ): + """ Abstract base class for an elliptical Sersic profile, used for computing + its effective radius and Sersic instance. + + Parameters + ---------- + centre: (float, float) + The (y,x) coordinates of the origin of the profiles + axis_ratio : float + Ratio of light profiles ellipse's minor and major axes (b/a) + phi : float + Rotational angle of profiles ellipse counter-clockwise from positive x-axis + intensity : float + Overall intensity normalisation in the light profiles (electrons per second) + effective_radius : float + The circular radius containing half the light of this model_mapper + sersic_index : Int + The Sersic index, which controls the light profile concentration + """ + super(AbstractEllipticalSersic, self).__init__(centre, axis_ratio, phi) + self.intensity = intensity + self.effective_radius = effective_radius + self.sersic_index = sersic_index + + +class MassProfile: + def surface_density_func(self, eta): + raise NotImplementedError("surface_density_at_radius should be overridden") + + def surface_density_from_grid(self, grid): + pass + + def potential_from_grid(self, grid): + pass + + def deflections_from_grid(self, grid): + raise NotImplementedError("deflections_from_grid should be overridden") + + +# noinspection PyAbstractClass +class EllipticalMassProfile(EllipticalProfile, MassProfile): + def __init__(self, centre=(0.0, 0.0), axis_ratio=1.0, phi=0.0): + """ + Abstract class for elliptical mass profiles. + + Parameters + ---------- + centre: (float, float) + The origin of the profile + axis_ratio : float + Ellipse's minor-to-major axis ratio (b/a) + phi : float + Rotation angle of profile's ellipse counter-clockwise from positive x-axis + """ + super(EllipticalMassProfile, self).__init__(centre, axis_ratio, phi) + self.axis_ratio = axis_ratio + self.phi = phi + + +# noinspection PyAbstractClass +class EllipticalCoredPowerLaw(EllipticalMassProfile, MassProfile): + def __init__( + self, + centre=(0.0, 0.0), + axis_ratio=1.0, + phi=0.0, + einstein_radius=1.0, + slope=2.0, + core_radius=0.01, + ): + """ + Represents a cored elliptical power-law density distribution + + Parameters + ---------- + centre: (float, float) + The image_grid of the origin of the profiles + axis_ratio : float + Elliptical mass profile's minor-to-major axis ratio (b/a) + phi : float + Rotation angle of mass profile's ellipse counter-clockwise from positive + x-axis + einstein_radius : float + Einstein radius of power-law mass profiles + slope : float + power-law density slope of mass profiles + core_radius : float + The radius of the inner core + """ + super(EllipticalCoredPowerLaw, self).__init__(centre, axis_ratio, phi) + self.einstein_radius = einstein_radius + self.slope = slope + self.core_radius = core_radius + + +# noinspection PyAbstractClass +class EllipticalCoredIsothermal(EllipticalCoredPowerLaw): + def __init__( + self, + centre=(0.0, 0.0), + axis_ratio=1.0, + phi=0.0, + einstein_radius=1.0, + core_radius=0.05, + ): + """ + Represents a cored elliptical isothermal density distribution, which is + equivalent to the elliptical power-law + density distribution for the value slope=2.0 + + Parameters + ---------- + centre: (float, float) + The image_grid of the origin of the profiles + axis_ratio : float + Elliptical mass profile's minor-to-major axis ratio (b/a) + phi : float + Rotation angle of mass profile's ellipse counter-clockwise from positive + x-axis + einstein_radius : float + Einstein radius of power-law mass profiles + core_radius : float + The radius of the inner core + """ + + super(EllipticalCoredIsothermal, self).__init__( + centre, axis_ratio, phi, einstein_radius, 2.0, core_radius + ) + + +class EllipticalSersic(AbstractEllipticalSersic, EllipticalLP): + def __init__( + self, + centre=(0.0, 0.0), + axis_ratio=1.0, + phi=0.0, + intensity=0.1, + effective_radius=0.6, + sersic_index=4.0, + ): + """ The elliptical Sersic profile, used for fitting a model_galaxy's light. + + Parameters + ---------- + centre: (float, float) + The (y,x) origin of the light profile. + axis_ratio : float + Ratio of light profiles ellipse's minor and major axes (b/a). + phi : float + Rotation angle of light profile counter-clockwise from positive x-axis. + intensity : float + Overall intensity normalisation of the light profiles (electrons per + second). + effective_radius : float + The circular radius containing half the light of this profile. + sersic_index : Int + Controls the concentration of the of the light profile. + """ + super(EllipticalSersic, self).__init__( + centre, axis_ratio, phi, intensity, effective_radius, sersic_index + ) + + +class EllipticalCoreSersic(EllipticalSersic): + def __init__( + self, + centre=(0.0, 0.0), + axis_ratio=1.0, + phi=0.0, + intensity=0.1, + effective_radius=0.6, + sersic_index=4.0, + radius_break=0.01, + intensity_break=0.05, + gamma=0.25, + alpha=3.0, + ): + """ The elliptical cored-Sersic profile, used for fitting a model_galaxy's + light. + + Parameters + ---------- + centre: (float, float) + The (y,x) origin of the light profile. + axis_ratio : float + Ratio of light profiles ellipse's minor and major axes (b/a). + phi : float + Rotation angle of light profile counter-clockwise from positive x-axis. + intensity : float + Overall intensity normalisation of the light profiles (electrons per + second). + effective_radius : float + The circular radius containing half the light of this profile. + sersic_index : Int + Controls the concetration of the of the light profile. + radius_break : Float + The break radius separating the inner power-law (with logarithmic slope + gamma) and outer Sersic function. + intensity_break : Float + The intensity at the break radius. + gamma : Float + The logarithmic power-law slope of the inner core profiles + alpha : + Controls the sharpness of the transition between the inner core / outer + Sersic profiles. + """ + super(EllipticalCoreSersic, self).__init__( + centre, axis_ratio, phi, intensity, effective_radius, sersic_index + ) + self.radius_break = radius_break + self.intensity_break = intensity_break + self.alpha = alpha + self.gamma = gamma + + +class EllipticalExponential(EllipticalSersic): + def __init__( + self, + centre=(0.0, 0.0), + axis_ratio=1.0, + phi=0.0, + intensity=0.1, + effective_radius=0.6, + ): + """ The elliptical exponential profile, used for fitting a model_galaxy's light. + + This is a subset of the elliptical Sersic profile, specific to the case that + sersic_index = 1.0. + + Parameters + ---------- + centre: (float, float) + The (y,x) origin of the light profile. + axis_ratio : float + Ratio of light profiles ellipse's minor and major axes (b/a). + phi : float + Rotation angle of light profile counter-clockwise from positive x-axis. + intensity : float + Overall intensity normalisation of the light profiles (electrons per + second). + effective_radius : float + The circular radius containing half the light of this profile. + """ + super(EllipticalExponential, self).__init__( + centre, axis_ratio, phi, intensity, effective_radius, 1.0 + ) + + +class EllipticalGaussian(EllipticalLP): + def __init__( + self, centre=(0.0, 0.0), axis_ratio=1.0, phi=0.0, intensity=0.1, sigma=0.01 + ): + """ The elliptical Gaussian profile. + + Parameters + ---------- + centre: (float, float) + The (y,x) origin of the light profile. + axis_ratio : float + Ratio of light profiles ellipse's minor and major axes (b/a). + phi : float + Rotation angle of light profile counter-clockwise from positive x-axis. + intensity : float + Overall intensity normalisation of the light profiles (electrons per + second). + sigma : float + The full-width half-maximum of the Gaussian. + """ + super(EllipticalGaussian, self).__init__(centre, axis_ratio, phi) + + self.intensity = intensity + self.sigma = sigma + + +class Tracer: + def __init__(self, lens_galaxy: Galaxy, source_galaxy: Galaxy, grid): + self.lens_galaxy = lens_galaxy + self.source_galaxy = source_galaxy + self.grid = grid + + +class Result: + def __init__(self, instance=None, model=None): + self.instance = instance + self.model = model + + def model_absolute(self, absolute): + return self.model + + def model_relative(self, relative): + return self.model + + +class HyperGalaxy: + pass + + +class MockDataset(Dataset): + @property + def metadata(self) -> dict: + return dict() + + @property + def name(self) -> str: + return "name" + + +class MockClassGaussian: + def __init__(self, one, two): + self.one = one + self.two = two + + +class MockClassInf: + def __init__(self, one, two): + self.one = one + self.two = two diff --git a/test_autofit/unit/mapper/model/test_model_mapper.py b/test_autofit/unit/mapper/model/test_model_mapper.py index 77f4d90cc..56b54c738 100644 --- a/test_autofit/unit/mapper/model/test_model_mapper.py +++ b/test_autofit/unit/mapper/model/test_model_mapper.py @@ -4,12 +4,12 @@ import pytest import autofit as af -from test_autofit.mock import MockClassGaussian import test_autofit.mock from autofit import exc from autofit.text import formatter as frm from test_autofit import mock from test_autofit.mock import GeometryProfile +from test_autofit.mock import MockClassGaussian dataset_path = "{}/../".format(os.path.dirname(os.path.realpath(__file__))) @@ -108,7 +108,7 @@ def test_with_instance(self): def test_with_promise(self): mm = af.ModelMapper() - mm.promise = af.Promise( + mm.promise = af.prior.Promise( af.Phase( phase_name="phase", analysis_class=None @@ -528,7 +528,6 @@ def test_log_priors_from_vector(self): assert log_priors == [0.125, 0.2] def test_random_vector_from_prior_within_limits(self): - np.random.seed(1) mapper = af.ModelMapper() @@ -864,7 +863,7 @@ def make_promise_mapper(): mapper = af.ModelMapper() mapper.galaxy = af.PriorModel( mock.Galaxy, - redshift=af.Promise( + redshift=af.prior.Promise( None, None, is_instance=False, diff --git a/test_autofit/unit/mapper/promise/test_iteration.py b/test_autofit/unit/mapper/promise/test_iteration.py index a59b16ed4..667b8d436 100644 --- a/test_autofit/unit/mapper/promise/test_iteration.py +++ b/test_autofit/unit/mapper/promise/test_iteration.py @@ -46,8 +46,8 @@ def test_index_type(self, phase): promise_0 = phase.result.model.collection[0] promise_1 = phase.result.model.collection[1] - assert isinstance(promise_0, af.Promise) - assert isinstance(promise_1, af.Promise) + assert isinstance(promise_0, af.prior.Promise) + assert isinstance(promise_1, af.prior.Promise) def test_index_populate_model(self, phase, prior_0, prior_1, results_collection): promise_0 = phase.result.model.collection[0] @@ -73,4 +73,4 @@ def test_iteration(self, phase): promises = list(phase.result.model.collection) assert len(promises) == 2 - assert all([isinstance(promise, af.Promise) for promise in promises]) + assert all([isinstance(promise, af.prior.Promise) for promise in promises]) diff --git a/test_autofit/unit/mapper/promise/test_promise.py b/test_autofit/unit/mapper/promise/test_promise.py index bf541ae76..b654a8dab 100644 --- a/test_autofit/unit/mapper/promise/test_promise.py +++ b/test_autofit/unit/mapper/promise/test_promise.py @@ -174,7 +174,7 @@ def test_does_not_contribute_to_prior_count( assert model.prior_count == 0 def test_model_promise(self, model_promise, phase): - assert isinstance(model_promise, af.Promise) + assert isinstance(model_promise, af.prior.Promise) assert model_promise.path == ("one", "redshift") assert model_promise.is_instance is False assert model_promise._phase is phase @@ -190,7 +190,7 @@ def test_optional_in_sub(self, collection, phase): assert result is None def test_instance_promise(self, instance_promise, phase): - assert isinstance(instance_promise, af.Promise) + assert isinstance(instance_promise, af.prior.Promise) assert instance_promise.path == ("one", "redshift") assert instance_promise.is_instance is True assert instance_promise._phase is phase @@ -239,13 +239,13 @@ def test_kwarg_promise(self, profile_promise, collection): def test_embedded_results(self, phase, collection): hyper_result = phase.result.hyper_result - assert isinstance(hyper_result, af.PromiseResult) + assert isinstance(hyper_result, af.prior.PromiseResult) model_promise = hyper_result.model instance_promise = hyper_result.instance - assert isinstance(model_promise.hyper_galaxy, af.Promise) - assert isinstance(instance_promise.hyper_galaxy, af.Promise) + assert isinstance(model_promise.hyper_galaxy, af.prior.Promise) + assert isinstance(instance_promise.hyper_galaxy, af.prior.Promise) model = model_promise.populate(collection) instance = instance_promise.populate(collection) diff --git a/test_autofit/unit/mapper/test_assertion.py b/test_autofit/unit/mapper/test_assertion.py index 21c995347..7cfe4156b 100644 --- a/test_autofit/unit/mapper/test_assertion.py +++ b/test_autofit/unit/mapper/test_assertion.py @@ -119,29 +119,29 @@ def make_model(collection): class TestPromiseAssertion: def test_less_than(self, promise_model, collection, model): promise = promise_model.axis_ratio < promise_model.phi - assert isinstance(promise, af.GreaterThanLessThanAssertion) + assert isinstance(promise, af.prior.GreaterThanLessThanAssertion) assertion = promise.populate(collection) - assert isinstance(assertion, af.GreaterThanLessThanAssertion) + assert isinstance(assertion, af.prior.GreaterThanLessThanAssertion) def test_greater_than(self, promise_model, collection, model): promise = promise_model.axis_ratio > promise_model.phi - assert isinstance(promise, af.GreaterThanLessThanAssertion) + assert isinstance(promise, af.prior.GreaterThanLessThanAssertion) def test_greater_than_equal(self, promise_model, collection, model): promise = promise_model.axis_ratio >= promise_model.phi - assert isinstance(promise, af.GreaterThanLessThanEqualAssertion) + assert isinstance(promise, af.prior.GreaterThanLessThanEqualAssertion) def test_integer_promise_assertion(self, promise_model, collection, model): promise = promise_model.axis_ratio > 1.0 - assert isinstance(promise, af.GreaterThanLessThanAssertion) + assert isinstance(promise, af.prior.GreaterThanLessThanAssertion) def test_compound_assertion(self, promise_model, collection, model): promise = (1.0 < promise_model.axis_ratio) < 1.0 - assert isinstance(promise, af.CompoundAssertion) + assert isinstance(promise, af.prior.CompoundAssertion) assertion = promise.populate(collection) - assert isinstance(assertion, af.CompoundAssertion) + assert isinstance(assertion, af.prior.CompoundAssertion) class TestModel: diff --git a/test_autofit/unit/mapper/test_prior_parsing.py b/test_autofit/unit/mapper/test_prior_parsing.py index 6aa4df165..9e6704865 100644 --- a/test_autofit/unit/mapper/test_prior_parsing.py +++ b/test_autofit/unit/mapper/test_prior_parsing.py @@ -52,21 +52,21 @@ def make_absolute_width_dict(): @pytest.fixture(name="relative_width_modifier") def make_relative_width_modifier(relative_width_dict): - return af.WidthModifier.from_dict(relative_width_dict) + return af.prior.WidthModifier.from_dict(relative_width_dict) @pytest.fixture(name="absolute_width_modifier") def make_absolute_width_modifier(absolute_width_dict): - return af.WidthModifier.from_dict(absolute_width_dict) + return af.prior.WidthModifier.from_dict(absolute_width_dict) class TestWidth: def test_relative(self, relative_width_modifier): - assert isinstance(relative_width_modifier, af.RelativeWidthModifier) + assert isinstance(relative_width_modifier, af.prior.RelativeWidthModifier) assert relative_width_modifier.value == 1.0 def test_absolute(self, absolute_width_modifier): - assert isinstance(absolute_width_modifier, af.AbsoluteWidthModifier) + assert isinstance(absolute_width_modifier, af.prior.AbsoluteWidthModifier) assert absolute_width_modifier.value == 2.0 diff --git a/test_autofit/unit/optimize/files/optimizer_grid_search/grid_search_result.pickle b/test_autofit/unit/optimize/files/optimizer_grid_search/grid_search_result.pickle new file mode 100644 index 000000000..77048cd4c Binary files /dev/null and b/test_autofit/unit/optimize/files/optimizer_grid_search/grid_search_result.pickle differ diff --git a/test_autofit/unit/optimize/nested_sampler/files/multinest/config/json_priors/mock.json b/test_autofit/unit/optimize/nested_sampler/files/multinest/config/json_priors/mock.json index 67a857e96..38d902074 100644 --- a/test_autofit/unit/optimize/nested_sampler/files/multinest/config/json_priors/mock.json +++ b/test_autofit/unit/optimize/nested_sampler/files/multinest/config/json_priors/mock.json @@ -612,9 +612,7 @@ } }, "Tracer": { - "grid": { - "type": "Deferred" - } + "grid.type": "Deferred" }, "Circle": { "circumference": { diff --git a/test_autofit/unit/optimize/nested_sampler/files/multinest/config/json_priors/test_autofit.json b/test_autofit/unit/optimize/nested_sampler/files/multinest/config/json_priors/test_autofit.json index b4b14be08..3bca9903a 100644 --- a/test_autofit/unit/optimize/nested_sampler/files/multinest/config/json_priors/test_autofit.json +++ b/test_autofit/unit/optimize/nested_sampler/files/multinest/config/json_priors/test_autofit.json @@ -1,33 +1,63 @@ { - "mock.Tracer": { - "lens_galaxy": { - "type": "Uniform", - "lower_limit": 0.0, - "upper_limit": 1.0, - "width_modifier": { - "type": "Absolute", - "value": 0.2 + "mock": { + "Tracer": { + "lens_galaxy": { + "type": "Uniform", + "lower_limit": 0.0, + "upper_limit": 1.0, + "width_modifier": { + "type": "Absolute", + "value": 0.2 + }, + "gaussian_limits": { + "lower": 0.0, + "upper": 1.0 + } }, - "gaussian_limits": { - "lower": 0.0, - "upper": 1.0 + "source_galaxy": { + "type": "Uniform", + "lower_limit": 0.0, + "upper_limit": 1.0, + "width_modifier": { + "type": "Absolute", + "value": 0.2 + }, + "gaussian_limits": { + "lower": 0.0, + "upper": 1.0 + } + }, + "grid": { + "type": "Deferred" } }, - "source_galaxy": { - "type": "Uniform", - "lower_limit": 0.0, - "upper_limit": 1.0, - "width_modifier": { - "type": "Absolute", - "value": 0.2 + "MockClassNLOx2": { + "one": { + "type": "Uniform", + "lower_limit": 0.0, + "upper_limit": 1.0, + "width_modifier": { + "type": "Absolute", + "value": 0.2 + }, + "gaussian_limits": { + "lower": 0.0, + "upper": 1.0 + } }, - "gaussian_limits": { - "lower": 0.0, - "upper": 1.0 + "two": { + "type": "Uniform", + "lower_limit": 0.0, + "upper_limit": 1.0, + "width_modifier": { + "type": "Absolute", + "value": 0.2 + }, + "gaussian_limits": { + "lower": 0.0, + "upper": 1.0 + } } - }, - "grid": { - "type": "Deferred" } } -} +} \ No newline at end of file diff --git a/test_autofit/unit/optimize/nested_sampler/files/multinest/config/non_linear/NestedSampler.ini b/test_autofit/unit/optimize/nested_sampler/files/multinest/config/non_linear/NestedSampler.ini new file mode 100644 index 000000000..5ad5db19c --- /dev/null +++ b/test_autofit/unit/optimize/nested_sampler/files/multinest/config/non_linear/NestedSampler.ini @@ -0,0 +1,3 @@ +[settings] +terminate_at_acceptance_ratio = False +acceptance_ratio_threshold = 1.0 \ No newline at end of file diff --git a/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/output.log b/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/output.log new file mode 100644 index 000000000..e69de29bb diff --git a/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/samples_backup/multinest.txt b/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/samples_backup/multinest.txt new file mode 100644 index 000000000..e47816787 --- /dev/null +++ b/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/samples_backup/multinest.txt @@ -0,0 +1,10 @@ + 0.020000000000000000E+00 0.999999990000000000E+07 0.110000000000000000E+01 0.210000000000000000E+01 0.310000000000000000E+01 0.410000000000000000E+01 + 0.020000000000000000E+00 0.999999990000000000E+07 0.090000000000000000E+01 0.190000000000000000E+01 0.290000000000000000E+01 0.390000000000000000E+01 + 0.010000000000000000E+00 0.999999990000000000E+07 0.100000000000000000E+01 0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 + 0.050000000000000000E+00 0.999999990000000000E+07 0.100000000000000000E+01 0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 + 0.100000000000000000E+00 0.999999990000000000E+07 0.100000000000000000E+01 0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 + 0.100000000000000000E+00 0.999999990000000000E+07 0.100000000000000000E+01 0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 + 0.100000000000000000E+00 0.999999990000000000E+07 0.100000000000000000E+01 0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 + 0.100000000000000000E+00 0.999999990000000000E+07 0.100000000000000000E+01 0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 + 0.200000000000000000E+00 0.999999990000000000E+07 0.100000000000000000E+01 0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 + 0.300000000000000000E+00 0.999999990000000000E+07 0.100000000000000000E+01 0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 \ No newline at end of file diff --git a/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/samples_backup/multinestresume.dat b/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/samples_backup/multinestresume.dat new file mode 100644 index 000000000..75a68955c --- /dev/null +++ b/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/samples_backup/multinestresume.dat @@ -0,0 +1,7 @@ + F + 3000 12345 1 50 + 0.502352236277967168E+05 0.502900436569068333E+05 + T + 0 + T F 0 50 + 0.648698272260014622E-26 0.502352236277967168E+05 0.502900436569068333E+05 diff --git a/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/samples_backup/multinestsummary.txt b/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/samples_backup/multinestsummary.txt new file mode 100644 index 000000000..7d452fec6 --- /dev/null +++ b/test_autofit/unit/optimize/nested_sampler/files/multinest/samples/1_class/multinest/samples_backup/multinestsummary.txt @@ -0,0 +1,2 @@ + 0.100000000000000000E+01 -0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 -0.500000000000000000E+01 0.600000000000000000E+01 0.700000000000000000E+01 0.800000000000000000E+01 0.900000000000000000E+01 -1.000000000000000000E+01 -1.100000000000000000E+01 1.200000000000000000E+01 1.300000000000000000E+01 -1.400000000000000000E+01 -1.500000000000000000E+01 1.600000000000000000E+01 0.020000000000000000E+00 0.999999990000000000E+07 0.020000000000000000E+00 0.999999990000000000E+07 + 0.100000000000000000E+01 -0.200000000000000000E+01 0.300000000000000000E+01 0.400000000000000000E+01 -0.500000000000000000E+01 0.600000000000000000E+01 0.700000000000000000E+01 0.800000000000000000E+01 0.900000000000000000E+01 -1.000000000000000000E+01 -1.100000000000000000E+01 1.200000000000000000E+01 1.300000000000000000E+01 -1.400000000000000000E+01 -1.500000000000000000E+01 1.600000000000000000E+01 0.020000000000000000E+00 0.999999990000000000E+07 \ No newline at end of file diff --git a/test_autofit/unit/optimize/nested_sampler/test_dynesty.py b/test_autofit/unit/optimize/nested_sampler/test_dynesty.py index 35a085b25..fda4772fc 100644 --- a/test_autofit/unit/optimize/nested_sampler/test_dynesty.py +++ b/test_autofit/unit/optimize/nested_sampler/test_dynesty.py @@ -3,7 +3,7 @@ import pytest from autofit import Paths -from autoconf import conf +from autofit import conf import autofit as af import pickle from test_autofit.mock import MockClassNLOx4 diff --git a/test_autofit/unit/optimize/nested_sampler/test_multi_nest.py b/test_autofit/unit/optimize/nested_sampler/test_multi_nest.py index 5470c7e43..11e70992a 100644 --- a/test_autofit/unit/optimize/nested_sampler/test_multi_nest.py +++ b/test_autofit/unit/optimize/nested_sampler/test_multi_nest.py @@ -4,7 +4,7 @@ import pytest -from autoconf import conf +from autofit import conf import autofit as af from autofit import Paths from autofit.optimize.non_linear.nested_sampling import multi_nest as mn diff --git a/test_autofit/unit/optimize/test_emcee.py b/test_autofit/unit/optimize/test_emcee.py index 60f907afb..a1073e3dd 100644 --- a/test_autofit/unit/optimize/test_emcee.py +++ b/test_autofit/unit/optimize/test_emcee.py @@ -1,7 +1,7 @@ import os import pytest -from autoconf import conf +from autofit import conf import autofit as af from autofit import Paths from test_autofit.mock import MockClassNLOx4 diff --git a/test_autofit/unit/optimize/test_non_linear.py b/test_autofit/unit/optimize/test_non_linear.py index 1f4ea2913..c8c77199e 100644 --- a/test_autofit/unit/optimize/test_non_linear.py +++ b/test_autofit/unit/optimize/test_non_linear.py @@ -4,7 +4,7 @@ import pytest import autofit as af -from autoconf import conf +from autofit import conf from autofit import Paths from autofit.optimize.non_linear.mock_nlo import MockSamples from test_autofit.mock import ( diff --git a/test_autofit/unit/tools/test_edenise.py b/test_autofit/unit/tools/test_edenise.py new file mode 100644 index 000000000..482abcf2e --- /dev/null +++ b/test_autofit/unit/tools/test_edenise.py @@ -0,0 +1,55 @@ +import pytest + +from autofit.tools.edenise import Line, Converter + + +@pytest.fixture( + name="as_line" +) +def make_as_line(): + return Line( + "from .mapper.model import ModelInstance as Instance" + ) + + +@pytest.fixture( + name="line" +) +def make_line(): + return Line( + "from .mapper.model import ModelInstance" + ) + + +class Test: + def test_line_is_import(self, as_line): + assert as_line.is_import + assert not Line(".mapper.model").is_import + + def test_source(self, as_line, line): + assert as_line.source == "Instance" + assert line.source == "ModelInstance" + + def test_target(self, as_line, line): + assert as_line._target == "mapper.model.ModelInstance" + assert line._target == "mapper.model.ModelInstance" + + def test_hash(self, as_line, line): + assert hash(as_line) == hash(line) + + def test_phase_property_line(self): + line = Line( + "from .tools.phase_property import PhaseProperty" + ) + assert line.source == "PhaseProperty" + assert line._target == "tools.phase_property.PhaseProperty" + + def test_replace(self, as_line, line): + converter = Converter( + "testfit", + "tf", + [as_line, line] + ) + assert converter.convert( + "import testfit as tf\n\ntf.ModelInstance\ntf.Instance" + ) == "from testfit.mapper.model import ModelInstance\n\n\nModelInstance\nModelInstance"