Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
__MACOSX
*.swp
test/autofit/test_fit
# Byte-compiled / optimized / DLL files
Expand Down Expand Up @@ -114,4 +115,4 @@ test_autofit/optimize/test_fit/
test_autofit/test_files/text/psycopg2-binary==2.8.1
test_autofit/test_files/text/
fit/test_autofit/optimize/test_fit
.DS_Store
*.DS_Store
5 changes: 5 additions & 0 deletions autofit/aggregator/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
from .aggregator import *


class PickledChild:
def __init__(self, age):
self.age = age
16 changes: 10 additions & 6 deletions autofit/aggregator/phase_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def __init__(self, directory: str):
]
self.__dict__.update({pair[0]: pair[1] for pair in pairs})

@property
def pickle_path(self):
return f"{self.directory}/pickles"

@property
def output(self) -> AbstractOutput:
"""
Expand All @@ -58,7 +62,7 @@ def mask(self):
A pickled mask object
"""
with open(
os.path.join(self.directory, "mask.pickle"), "rb"
os.path.join(self.pickle_path, "mask.pickle"), "rb"
) as f:
return dill.load(f)

Expand All @@ -69,7 +73,7 @@ def __getattr__(self, item):
dataset.pickle, meta_dataset.pickle etc.
"""
with open(
os.path.join(self.directory, f"{item}.pickle"), "rb"
os.path.join(self.pickle_path, f"{item}.pickle"), "rb"
) as f:
return pickle.load(f)

Expand All @@ -86,17 +90,17 @@ def optimizer(self) -> autofit.optimize.non_linear.non_linear.NonLinearOptimizer
The optimizer object that was used in this phase
"""
if self.__optimizer is None:
with open(os.path.join(self.directory, "optimizer.pickle"), "r+b") as f:
with open(os.path.join(self.pickle_path, "optimizer.pickle"), "r+b") as f:
self.__optimizer = pickle.loads(f.read())
return self.__optimizer

@property
def model(self) -> autofit.optimize.non_linear.non_linear.NonLinearOptimizer:
def model(self):
"""
The optimizer object that was used in this phase
The model that was used in this phase
"""
if self.__model is None:
with open(os.path.join(self.directory, "model.pickle"), "r+b") as f:
with open(os.path.join(self.pickle_path, "model.pickle"), "r+b") as f:
self.__model = pickle.loads(f.read())
return self.__model

Expand Down
222 changes: 204 additions & 18 deletions autofit/aggregator/predicate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


class AttributePredicate:
def __init__(self, attribute: str):
def __init__(self, *path):
"""
Used to produce predicate objects for filtering in the aggregator.

Expand All @@ -15,21 +15,77 @@ def __init__(self, attribute: str):

Parameters
----------
attribute
The name of the attribute this predicate relates to.
path
A series of names of attributes that can be used to get a value.
For example, (mask, pixel_size) would get the pixel size of a mask
when evaluated for a given phase.
"""
self.attribute = attribute
self.path = path

def value_for_phase(
self,
phase: PhaseOutput
):
"""
Recurse the phase output by iterating the attributes in the path
and getting a value for each attribute.
"""
value = phase
for attribute in self.path:
value = getattr(
value,
attribute
)
return value

def __eq__(self, value):
"""
Create a predicate which asks whether the given value is equal to
the attribute of a phase.
"""
return EqualityPredicate(
self.attribute,
self,
value
)

def __le__(self, other):
return OrPredicate(
self == other,
self < other
)

def __ge__(self, other):
return OrPredicate(
self == other,
self > other
)

def __getattr__(self, item: str) -> "AttributePredicate":
"""
Adds another item to the path
"""
return AttributePredicate(
*self.path, item
)

def __gt__(self, other):
"""
Is the value of this attribute for a given phase greater than some
other value?
"""
return GreaterThanPredicate(
self, other
)

def __lt__(self, other):
"""
Is the value of this attribute for a given phase less than some
other value?
"""
return LessThanPredicate(
self, other
)

def __ne__(self, other):
"""
Create a predicate which asks whether the given value is not equal to
Expand All @@ -43,7 +99,7 @@ def contains(self, value):
the attribute of a phase.
"""
return ContainsPredicate(
self.attribute,
self,
value
)

Expand Down Expand Up @@ -82,31 +138,155 @@ def __invert__(self) -> "NotPredicate":
self
)

def __or__(self, other: "AbstractPredicate") -> "OrPredicate":
"""
Returns a predicate that is true if either predicate is true
for a given phase.
"""
return OrPredicate(self, other)

def __and__(self, other: "AbstractPredicate") -> "AndPredicate":
"""
Returns a predicate that is true if both predicates are true
for a given phase.
"""
return AndPredicate(self, other)

@abstractmethod
def __call__(self, phase: PhaseOutput) -> bool:
"""
Does the attribute of the phase match the requirement of this predicate?
"""


class CombinationPredicate(AbstractPredicate, ABC):
def __init__(
self,
one: AbstractPredicate,
two: AbstractPredicate
):
"""
Abstract predicate combining two other predicates.

Parameters
----------
one
two
Child predicates
"""
self.one = one
self.two = two


class OrPredicate(CombinationPredicate):
def __call__(self, phase: PhaseOutput):
"""
The disjunction of two predicates.

Parameters
----------
phase
An object representing the output of a given phase.

Returns
-------
True if either predicate is True for the phase
"""
return self.one(phase) or self.two(phase)


class AndPredicate(CombinationPredicate):
def __call__(self, phase: PhaseOutput):
"""
The conjunction of two predicates.

Parameters
----------
phase
An object representing the output of a given phase.

Returns
-------
True if both predicates are True for the phase
"""
return self.one(phase) and self.two(phase)


class ComparisonPredicate(AbstractPredicate, ABC):
def __init__(
self,
attribute: str,
attribute_predicate: AttributePredicate,
value
):
"""
Compare an attribute of a phase with a value.

Parameters
----------
attribute
An attribute of a phase
attribute_predicate
An attribute path of a phase
value
A value to which the attribute is compared
"""
self.attribute = attribute
self.value = value
self.attribute_predicate = attribute_predicate
self._value = value

def value(
self,
phase
):
if isinstance(self._value, AttributePredicate):
return self._value.value_for_phase(
phase
)
return self._value


class GreaterThanPredicate(ComparisonPredicate):
def __call__(
self,
phase: PhaseOutput
) -> bool:
"""
Parameters
----------
phase
An object representing the output of a given phase.

Returns
-------
True iff the value of the attribute of the phase is greater than
the value associated with this predicate
"""

return self.attribute_predicate.value_for_phase(
phase
) > self.value(
phase
)


class LessThanPredicate(ComparisonPredicate):
def __call__(
self,
phase: PhaseOutput
) -> bool:
"""
Parameters
----------
phase
An object representing the output of a given phase.

Returns
-------
True iff the value of the attribute of the phase is less than
the value associated with this predicate
"""
return self.attribute_predicate.value_for_phase(
phase
) < self.value(
phase
)


class ContainsPredicate(ComparisonPredicate):
Expand All @@ -125,9 +305,10 @@ def __call__(
True iff the value of the attribute of the phase contains
the value associated with this predicate
"""
return self.value in getattr(
phase,
self.attribute
return self.value(
phase
) in self.attribute_predicate.value_for_phase(
phase
)


Expand All @@ -144,10 +325,15 @@ def __call__(self, phase):
True iff the value of the attribute of the phase is equal to
the value associated with this predicate
"""
return getattr(
phase,
self.attribute
) == self.value
try:
value = self.value(
phase
)
except AttributeError:
value = self.value
return self.attribute_predicate.value_for_phase(
phase
) == value


class NotPredicate(AbstractPredicate):
Expand Down
12 changes: 8 additions & 4 deletions autofit/optimize/non_linear/paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,23 +171,27 @@ def pdf_path(self) -> str:
"""
return "{}pdf/".format(self.image_path)

@property
@make_path
def pickle_path(self) -> str:
return f"{self.make_path()}/pickles"

def make_optimizer_pickle_path(self) -> str:
"""
Create the path at which the optimizer pickle should be saved
"""
return "{}/optimizer.pickle".format(self.make_path())
return f"{self.pickle_path}/optimizer.pickle"

def make_model_pickle_path(self):
"""
Create the path at which the model pickle should be saved
"""
return "{}/model.pickle".format(self.make_path())
return f"{self.pickle_path}/model.pickle"

@make_path
def make_path(self) -> str:
"""
Create the path to the folder at which the metadata and optimizer pickle should
be saved
Create the path to the folder at which the metadata should be saved
"""
return "{}/{}/{}/{}/".format(
conf.instance.output_path, self.phase_path, self.phase_name, self.phase_tag
Expand Down
Loading