예제 #1
0
    def __init__(self, *args, **kwargs):
        my_kwargs = self.pop_kwargs(kwargs,
            "refine_method_index", "refine_method", "refine_options"
        )
        super(Refinement, self).__init__(*args, **kwargs)
        kwargs = my_kwargs

        # Setup the refinables treestore
        self.refinables = TreeNode()
        self.update_refinement_treestore()

        # Setup the refine methods
        try:
            self.refine_method_index = int(self.get_kwarg(kwargs, None, "refine_method_index", "refine_method"))
        except ValueError:
            self.refine_method_index = self.refine_method_index
            pass # ignore faulty values, these indices change from time to time.

        self.refine_methods = RefineMethodManager.initialize_methods(
            self.get_kwarg(kwargs, None, "refine_options")
        )
예제 #2
0
    def __init__(self, *args, **kwargs):
        my_kwargs = self.pop_kwargs(kwargs,
            "refine_method", "refine_options"
        )
        super(Refiner, self).__init__(*args, **kwargs)
        kwargs = my_kwargs

        # Setup the refinables treestore
        self.refinables = TreeNode()

        # Setup the refine methods
        try:
            self.refine_method = int(self.get_kwarg(kwargs, None, "refine_method"))
        except ValueError:
            self.refine_method = self.refine_method
            pass # ignore faulty values, these indices change from time to time.

        self.refine_methods = self.create_refine_methods(self.get_kwarg(kwargs, None, "refine_options"))

        self.update_refinement_treestore()
예제 #3
0
class Refinement(ChildModel):
    """
        A simple model that plugs onto the Mixture model. It provides
        the functionality related to refinement of parameters.
    """

    # MODEL INTEL:
    class Meta(ChildModel.Meta):
        properties = [ # TODO add labels
            PropIntel(name="refinables", label="", has_widget=True, data_type=object, is_column=True, widget_type="object_tree_view", class_type=RefinableWrapper),
            PropIntel(name="refine_options", label="", data_type=dict, is_column=False),
            OptionPropIntel(name="refine_method_index", label="Refinement method index", has_widget=True, data_type=int, options={ key: method.name for key, method in RefineMethodManager.get_all_methods().items() }),
            PropIntel(name="make_psp_plots", label="", data_type=bool, is_colum=False, has_widget=True, storable=False),
        ]
        store_id = "Refinement"

    mixture = property(ChildModel.parent.fget, ChildModel.parent.fset)

    #: Flag, True if after refinement plots should be generated of the parameter space
    make_psp_plots = False

    #: TreeNode containing the refinable properties
    refinables = None

    #: A dict containing an instance of each refinement method
    refine_methods = None

    _refine_method_index = 0
    @property
    def refine_method_index(self):
        """ An integer describing which method to use for the refinement """
        return self._refine_method_index
    @refine_method_index.setter
    def refine_method_index(self, value): self._refine_method_index = int(value)

    #: A dict containing the current refinement options
    @property
    def refine_options(self):
        return self.get_refinement_method().get_options()

    #: A dict containing all refinement options
    @property
    def all_refine_options(self):
        return {
            method.index : method.get_options()
            for method in list(self.refine_methods.values())
        }

    def __init__(self, *args, **kwargs):
        my_kwargs = self.pop_kwargs(kwargs,
            "refine_method_index", "refine_method", "refine_options"
        )
        super(Refinement, self).__init__(*args, **kwargs)
        kwargs = my_kwargs

        # Setup the refinables treestore
        self.refinables = TreeNode()
        self.update_refinement_treestore()

        # Setup the refine methods
        try:
            self.refine_method_index = int(self.get_kwarg(kwargs, None, "refine_method_index", "refine_method"))
        except ValueError:
            self.refine_method_index = self.refine_method_index
            pass # ignore faulty values, these indices change from time to time.

        self.refine_methods = RefineMethodManager.initialize_methods(
            self.get_kwarg(kwargs, None, "refine_options")
        )

    # ------------------------------------------------------------
    #      Refiner methods
    # ------------------------------------------------------------
    def get_refiner(self):
        """
            This returns a Refiner object which can be used to refine the
            selected properties using the selected algorithm.
            Just call 'refine(stop)' on the returned object, with stop a
            threading.Event or multiprocessing.Event which you can use to stop
            the refinement before completion.
            The Refiner object also has a RefineHistory and RefineStatus object
            that can be used to track the status and history of the refinement.
        """ 
       
        return Refiner(
            method            = self.get_refinement_method(),
            residual_callback = get_optimized_residual,
            data_callback     = lambda: self.mixture.data_object,
            refinables        = self.refinables,
            event_cmgr        = EventContextManager(self.mixture.needs_update, self.mixture.data_changed),
        )

    # ------------------------------------------------------------
    #      Refinement Methods Management
    # ------------------------------------------------------------
    def get_refinement_method(self):
        """
            Returns the actual refinement method by translating the 
            `refine_method` attribute
        """
        return self.refine_methods[self.refine_method_index]

    # ------------------------------------------------------------
    #      Refinables Management
    # ------------------------------------------------------------
    # TODO set a restrict range attribute on the PropIntels, so we can use custom ranges for each property
    def auto_restrict(self):
        """
            Convenience function that restricts the selected properties 
            automatically by setting their minimum and maximum values.
        """
        with self.mixture.needs_update.hold():
            for node in self.refinables.iter_children():
                ref_prop = node.object
                if ref_prop.refine and ref_prop.refinable:
                    ref_prop.value_min = ref_prop.value * 0.8
                    ref_prop.value_max = ref_prop.value * 1.2

    def randomize(self):
        """
            Convenience function that randomize the selected properties.
            Respects the current minimum and maximum values.
            Executes an optimization after the randomization.
        """
        with self.mixture.data_changed.hold_and_emit():
            with self.mixture.needs_update.hold_and_emit():
                for node in self.refinables.iter_children():
                    ref_prop = node.object
                    if ref_prop.refine and ref_prop.refinable:
                        ref_prop.value = random.uniform(ref_prop.value_min, ref_prop.value_max)

    def update_refinement_treestore(self):
        """
            This creates a tree store with all refinable properties and their
            minimum, maximum and current value.
        """
        if self.parent is not None: # not linked so no valid phases!
            self.refinables.clear()

            def add_property(parent_node, obj, prop, is_grouper):
                rp = RefinableWrapper(obj=obj, prop=prop, parent=self.mixture, is_grouper=is_grouper)
                return parent_node.append(TreeNode(rp))

            def parse_attribute(obj, prop, root_node):
                """
                    obj: the object
                    attr: the attribute of obj or None if obj contains attributes
                    root_node: the root TreeNode new iters should be put under
                """
                if prop is not None:
                    if hasattr(obj, "get_uninherited_property_value"):
                        value = obj.get_uninherited_property_value(prop)
                    else:
                        value = getattr(obj, prop.name)
                else:
                    value = obj

                if isinstance(value, RefinementValue): # AtomRelation and UnitCellProperty
                    new_node = add_property(root_node, value, prop, False)
                elif hasattr(value, "__iter__"): # List or similar
                    for new_obj in value:
                        parse_attribute(new_obj, None, root_node)
                elif isinstance(value, RefinementGroup): # Phase, Component, Probability
                    if len(value.refinables) > 0:
                        new_node = add_property(root_node, value, prop, True)
                        for prop in value.refinables:
                            parse_attribute(value, prop, new_node)
                else: # regular values
                    new_node = add_property(root_node, obj, prop, False)

            for phase in self.mixture.project.phases:
                if phase in self.mixture.phase_matrix:
                    parse_attribute(phase, None, self.refinables)


    pass # end of class
예제 #4
0
 def add_property(parent_node, obj, prop, is_grouper):
     rp = RefinableWrapper(obj=obj, prop=prop, parent=self.mixture, is_grouper=is_grouper)
     return parent_node.append(TreeNode(rp))
예제 #5
0
class Refiner(ChildModel):
    """
        A simple model that plugs onto the Mixture model. It provides
        the functionality related to refinement of parameters.
    """

    # MODEL INTEL:
    class Meta(ChildModel.Meta):
        properties = [ # TODO add labels
            PropIntel(name="refinables", label="", has_widget=True, data_type=object, is_column=True, widget_type="object_tree_view", class_type=RefinableWrapper),
            PropIntel(name="refine_options", label="", data_type=dict, is_column=False),
            OptionPropIntel(name="refine_method", label="Refinement method", has_widget=True, data_type=int, options={ key: method.name for key, method in get_all_refine_methods().iteritems() }),
            PropIntel(name="make_psp_plots", label="", data_type=bool, is_colum=False, has_widget=True, storable=False),
        ]
        store_id = "Refiner"

    mixture = property(ChildModel.parent.fget, ChildModel.parent.fset)

    #: Refinement context
    context = None

    #: Refinement thread (or None if not running)
    thread = None

    #: Flag, True if after refinement plots should be generated of the parameter space
    make_psp_plots = False

    #: TreeNode containing the refinable properties
    refinables = None

    #: A dict containing an instance of each refinement method
    refine_methods = None

    _refine_method = 0
    @property
    def refine_method(self):
        """ An integer describing which method to use for the refinement (see 
        refinement.methods.get_all_refine_methods) """
        return self._refine_method
    @refine_method.setter
    def refine_method(self, value): self._refine_method = int(value)

    #: A dict containing the current refinement options
    @property
    def refine_options(self):
        method = self.get_refinement_method()
        return { name: getattr(method, name) for name in method.options }

    #: A dict containing all refinement options
    @property
    def all_refine_options(self):
        return {
            method.index : { name: getattr(method, name) for name in method.options }
            for method in self.refine_methods.values()
        }

    def __init__(self, *args, **kwargs):
        my_kwargs = self.pop_kwargs(kwargs,
            "refine_method", "refine_options"
        )
        super(Refiner, self).__init__(*args, **kwargs)
        kwargs = my_kwargs

        # Setup the refinables treestore
        self.refinables = TreeNode()

        # Setup the refine methods
        try:
            self.refine_method = int(self.get_kwarg(kwargs, None, "refine_method"))
        except ValueError:
            self.refine_method = self.refine_method
            pass # ignore faulty values, these indices change from time to time.

        self.refine_methods = self.create_refine_methods(self.get_kwarg(kwargs, None, "refine_options"))

        self.update_refinement_treestore()

    # ------------------------------------------------------------
    #      Methods & Functions
    # ------------------------------------------------------------
    def setup_context(self, store=False):
        """
            Creates a RefineContext object filled with parameters based on the
            current state of the Mixture object.
        """
        self.context = RefineContext(
            parent=self.parent,
            options=self.parent.refine_options,
            store=store
        )

    def delete_context(self):
        """
            Clears the RefineContext from this model
        """
        self.context = None

    def _inner_refine(self, refine_method, context, stop=None, **kwargs):
        # Suppress updates:
        with self.mixture.needs_update.hold():
            with self.mixture.data_changed.hold():
                # If something has been selected: continue...
                if len(context.ref_props) > 0:
                    # Make sure the stop signal is not set from a previous run:
                    if stop is not None:
                        stop.clear()

                    # Log some information:
                    logger.info("-"*80)
                    logger.info("Starting refinement with this setup:")
                    msg_frm = "%22s: %s"
                    logger.info(msg_frm % ("refinement method", refine_method))
                    logger.info(msg_frm % ("number of parameters", len(context.ref_props)))
                    logger.info(msg_frm % ("GUI mode", settings.GUI_MODE))

                    # Record start time
                    t1 = time.time()

                    try: # Run until it ends or it raises an exception:
                        refine_method(context, stop=stop)
                    except any as error:
                        logger.exception("Unhandled run-time error when refining: %s" % error)
                        context.status = "error"
                        context.status_message = "Error occurred..."
                    else: # No errors occurred:
                        if stop is not None and stop.is_set():
                            context.status = "stopped"
                            context.status_message = "Stopped ..."
                            logger.info("Refinement was stopped prematurely")
                        else:
                            context.status = "finished"
                            context.status_message = "Finished"
                            logger.info("Refinement ended successfully")

                    # Record end time
                    t2 = time.time()

                    # Log some more information:
                    logger.info('%s took %0.3f ms' % ("Total refinement", (t2 - t1) * 1000.0))
                    logger.info('Best solution found was:')
                    for line in context.best_solution_to_string().split('\n'):
                        logger.info(line)
                    logger.info("-"*80)
                else: # nothing selected for refinement
                    context.status = "error"
                    context.status_message = "No parameters selected!"
                # Return the context to whatever called this
                return context

    def refine(self, threaded=False, on_complete=None, **kwargs):
        """
            This refines the selected properties using the selected algorithm.
            This can be run asynchronously when threaded is set to True.
        """

        refine_method = partial(self._inner_refine,
            self.get_refinement_method(), self.context, **kwargs)

        if not threaded:
            context = refine_method()
            if callable(on_complete):
                on_complete(context)
        else:
            def thread_completed(context):
                #Assuming this is GTK-thread safe (i.e. wrapped in @run_when_idle)
                on_complete(context)
                self.thread = None
            self.thread = CancellableThread(refine_method, thread_completed)
            self.thread.start()

    def cancel(self):
        """
            Cancels a threaded refinement, 
            and will call the on_complete callback passed to `refine`
        """
        if self.thread is not None:
            logger.info("Refinement cancelled")
            self.thread.cancel()
        else:
            logger.info("Cannot cancel, no refinement running")
        self.thread = None

    def stop(self):
        """ Stops a threaded refinement, not returning any result """
        if self.thread is not None:
            logger.info("Refinement stopped")
            self.thread.stop()
        else:
            logger.info("Cannot stop, no refinement running")
        self.thread = None

    # ------------------------------------------------------------
    #      Refinement Methods Management
    # ------------------------------------------------------------
    @staticmethod
    def get_all_refine_methods():
        return get_all_refine_methods()

    @staticmethod
    def create_refine_methods(refine_options):
        """
            Returns a dict of refine methods as values and their index as key
            with the passed refine_options dict applied.
        """

        # 1. Create a list of refinement instances:
        refine_methods = {}
        for index, method in get_all_refine_methods().iteritems():
            refine_methods[index] = method()

        # 2. Create dict of default options
        default_options = {}
        for method in refine_methods.values():
            default_options[method.index] = {
                name: getattr(type(method), name).default for name in method.options
            }

        # 3. Apply the refine options to the methods
        if not refine_options == None:
            for index, options in zip(refine_options.keys(), refine_options.values()):
                index = int(index)
                if index in refine_methods:
                    method = refine_methods[index]
                    for arg, value in zip(options.keys(), options.values()):
                        if hasattr(method, arg):
                            setattr(method, arg, value)

        return refine_methods

    def get_refinement_method(self):
        """
            Returns the actual refinement method by translating the 
            `refine_method` attribute
        """
        return self.refine_methods[self.refine_method]

    def get_refinement_option(self, option):
        return getattr(type(self.get_refinement_method()), option)

    def get_refinement_option_value(self, option):
        return getattr(self.get_refinement_method(), option)

    def set_refinement_option_value(self, option, value):
        return setattr(self.get_refinement_method(), option, value)

    # ------------------------------------------------------------
    #      Refinables Management
    # ------------------------------------------------------------


    # TODO set a restrict range attribute on the PropIntels, so we can use custom ranges for each property
    def auto_restrict(self):
        """
            Convenience function that restricts the selected properties 
            automatically by setting their minimum and maximum values.
        """
        with self.mixture.needs_update.hold():
            for node in self.refinables.iter_children():
                ref_prop = node.object
                if ref_prop.refine and ref_prop.refinable:
                    ref_prop.value_min = ref_prop.value * 0.8
                    ref_prop.value_max = ref_prop.value * 1.2

    def randomize(self):
        """
            Convenience function that randomize the selected properties.
            Respects the current minimum and maximum values.
            Executes an optimization after the randomization.
        """
        with self.mixture.data_changed.hold_and_emit():
            with self.mixture.needs_update.hold_and_emit():
                for node in self.refinables.iter_children():
                    ref_prop = node.object
                    if ref_prop.refine and ref_prop.refinable:
                        ref_prop.value = random.uniform(ref_prop.value_min, ref_prop.value_max)

    def update_refinement_treestore(self):
        """
            This creates a tree store with all refinable properties and their
            minimum, maximum and current value.
        """
        if self.parent is not None: # not linked so no valid phases!
            self.refinables.clear()

            def add_property(parent_node, obj, prop, is_grouper):
                rp = RefinableWrapper(obj=obj, prop=prop, parent=self.mixture, is_grouper=is_grouper)
                return parent_node.append(TreeNode(rp))

            def parse_attribute(obj, prop, root_node):
                """
                    obj: the object
                    attr: the attribute of obj or None if obj contains attributes
                    root_node: the root TreeNode new iters should be put under
                """
                if prop is not None:
                    if hasattr(obj, "get_uninherited_property_value"):
                        value = obj.get_uninherited_property_value(prop)
                    else:
                        value = getattr(obj, prop.name)
                else:
                    value = obj

                if isinstance(value, RefinementValue): # AtomRelation and UnitCellProperty
                    new_node = add_property(root_node, value, prop, False)
                elif hasattr(value, "__iter__"): # List or similar
                    for new_obj in value:
                        parse_attribute(new_obj, None, root_node)
                elif isinstance(value, RefinementGroup): # Phase, Component, Probability
                    if len(value.refinables) > 0:
                        new_node = add_property(root_node, value, prop, True)
                        for prop in value.refinables:
                            parse_attribute(value, prop, new_node)
                else: # regular values
                    new_node = add_property(root_node, obj, prop, False)

            for phase in self.mixture.project.phases:
                if phase in self.mixture.phase_matrix:
                    parse_attribute(phase, None, self.refinables)


    pass # end of class
예제 #6
0
class Refinement(ChildModel):
    """
        A simple model that plugs onto the Mixture model. It provides
        the functionality related to refinement of parameters.
    """

    # MODEL INTEL:
    class Meta(ChildModel.Meta):
        store_id = "Refinement"

    mixture = property(ChildModel.parent.fget, ChildModel.parent.fset)

    #: Flag, True if after refinement plots should be generated of the parameter space
    make_psp_plots = BoolProperty(
        default=False, text="Make parameter space plots",
        tabular=False, visible=True, persistent=True
    )

    #: TreeNode containing the refinable properties
    refinables = ListProperty(
        default=None, text="Refinables",
        tabular=True, persistent=False, visible=True, 
        data_type=RefinableWrapper,
        cast_to=None, widget_type="object_tree_view"
    )

    #: A dict containing an instance of each refinement method
    refine_methods = None


    #: An integer describing which method to use for the refinement
    refine_method_index = IntegerChoiceProperty(
        default=0, text="Refinement method index",
        tabular=True, persistent=True, visible=True,
        choices={ key: method.name for key, method in RefineMethodManager.get_all_methods().items() }
    )

    #: A dict containing the current refinement options
    @LabeledProperty(
        default=None, text="Refine options",
        persistent=False, visible=False,
        mix_with=(ReadOnlyMixin,)
    )
    def refine_options(self):
        return self.get_refinement_method().get_options()
    
    #: A dict containing all refinement options
    @property
    def all_refine_options(self):
        return {
            method.index : method.get_options()
            for method in list(self.refine_methods.values())
        }

    def __init__(self, *args, **kwargs):
        my_kwargs = self.pop_kwargs(kwargs,
            "refine_method_index", "refine_method", "refine_options"
        )
        super(Refinement, self).__init__(*args, **kwargs)
        kwargs = my_kwargs

        # Setup the refinables treestore
        self.refinables = TreeNode()
        self.update_refinement_treestore()

        # Setup the refine methods
        try:
            self.refine_method_index = int(self.get_kwarg(kwargs, None, "refine_method_index", "refine_method"))
        except ValueError:
            self.refine_method_index = self.refine_method_index
            pass # ignore faulty values, these indices change from time to time.

        self.refine_methods = RefineMethodManager.initialize_methods(
            self.get_kwarg(kwargs, None, "refine_options")
        )

    # ------------------------------------------------------------
    #      Refiner methods
    # ------------------------------------------------------------
    def get_refiner(self):
        """
            This returns a Refiner object which can be used to refine the
            selected properties using the selected algorithm.
            Just call 'refine(stop)' on the returned object, with stop a
            threading.Event or multiprocessing.Event which you can use to stop
            the refinement before completion.
            The Refiner object also has a RefineHistory and RefineStatus object
            that can be used to track the status and history of the refinement.
        """ 
       
        return Refiner(
            method            = self.get_refinement_method(),
            data_callback     = lambda: self.mixture.data_object,
            refinables        = self.refinables,
            event_cmgr        = EventContextManager(self.mixture.needs_update, self.mixture.data_changed),
            metadata          = dict(
                phases          = self.mixture.phases,
                num_specimens   = len(self.mixture.specimens),
            )
        )

    # ------------------------------------------------------------
    #      Refinement Methods Management
    # ------------------------------------------------------------
    def get_refinement_method(self):
        """
            Returns the actual refinement method by translating the 
            `refine_method` attribute
        """
        return self.refine_methods[self.refine_method_index]

    # ------------------------------------------------------------
    #      Refinables Management
    # ------------------------------------------------------------
    # TODO set a restrict range attribute on the PropIntels, so we can use custom ranges for each property
    def auto_restrict(self):
        """
            Convenience function that restricts the selected properties 
            automatically by setting their minimum and maximum values.
        """
        with self.mixture.needs_update.hold():
            for node in self.refinables.iter_children():
                ref_prop = node.object
                if ref_prop.refine and ref_prop.refinable:
                    ref_prop.value_min = ref_prop.value * 0.8
                    ref_prop.value_max = ref_prop.value * 1.2

    def randomize(self):
        """
            Convenience function that randomize the selected properties.
            Respects the current minimum and maximum values.
            Executes an optimization after the randomization.
        """
        with self.mixture.data_changed.hold_and_emit():
            with self.mixture.needs_update.hold_and_emit():
                for node in self.refinables.iter_children():
                    ref_prop = node.object
                    if ref_prop.refine and ref_prop.refinable:
                        ref_prop.value = random.uniform(ref_prop.value_min, ref_prop.value_max)

    def update_refinement_treestore(self):
        """
            This creates a tree store with all refinable properties and their
            minimum, maximum and current value.
        """
        if self.parent is not None: # not linked so no valid phases!
            self.refinables.clear()

            def add_property(parent_node, obj, prop, is_grouper):
                rp = RefinableWrapper(obj=obj, prop=prop, parent=self.mixture, is_grouper=is_grouper)
                return parent_node.append(TreeNode(rp))

            def parse_attribute(obj, prop, root_node):
                """
                    obj: the object
                    attr: the attribute of obj or None if obj contains attributes
                    root_node: the root TreeNode new iters should be put under
                """
                if prop is not None:
                    if isinstance(prop, InheritableMixin):
                        value = prop.get_uninherited(obj)
                    else:
                        value = getattr(obj, prop.label)
                else:
                    value = obj

                if isinstance(value, RefinementValue): # AtomRelation and UnitCellProperty
                    new_node = add_property(root_node, value, prop, False)
                elif hasattr(value, "__iter__"): # List or similar
                    for new_obj in value:
                        parse_attribute(new_obj, None, root_node)
                elif isinstance(value, RefinementGroup): # Phase, Component, Probability
                    if len(value.refinables) > 0:
                        new_node = add_property(root_node, value, prop, True)
                        for prop in value.refinables:
                            parse_attribute(value, prop, new_node)
                else: # regular values
                    new_node = add_property(root_node, obj, prop, False)

            for phase in self.mixture.project.phases:
                if phase in self.mixture.phase_matrix:
                    parse_attribute(phase, None, self.refinables)


    pass # end of class