Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#221] non-distributed optimization algorithm #296

Open
wants to merge 28 commits into
base: master
Choose a base branch
from

Conversation

marcocapozzoli
Copy link
Collaborator

No description provided.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this binary file really supposed to be committed?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This python module is already being published on PyPI: https://pypi.org/project/hyperon-das-node/.
So, a pip install hyperon-das-node==0.0.1 might work.

MULTIPLY_STRENGTHS = "multiply_strengths"


def handle_fitness_function(tag: str) -> callable:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please rename to compute_fitness_function() if this function is supposed to be called directly or fitness_function_handle() if the idea is to use it to set a variable and call the function indirectly.

This or just rename it to fitness_function().

atom = atom_db.get_atom(handle)
if (custom_attributes := atom.custom_attributes):
if (strength := custom_attributes.get('strength')):
strengths_value.append(strength)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why storing strength values in a list? Why not just compute the product?

MAX_CORRELATIONS_WITHOUT_STIMULATE = 1000


class SharedQueue:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't understand why you are wrapping a Python queue inside this new class. What does this class add?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems you created this wrapper just to have a ShareQueue with the same interface/signatures as the C++ implementation. Any reason for that?

Another question is why there is another quite similar implementation in this PR: https://github.com/singnet/das/blob/masc/221-non-distributed-optimization-algorithm/src/evolution/das_node/shared_queue.py

"""
if method == SelectionMethodType.BEST_FITNESS:
return best_fitness
elif method == SelectionMethodType.FITNESS_PROPORTIONATE:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean "FITNESS_PROPORTIONAL"? Anyway, even "proportional" is an awful name :-P

Please use ROULETTE instead, which is a more usual name for this function. However, I have two comments which don't need to be addressed in this PR but need to be fixed (please create separate cards for them).

Firstly, this roulette selection method allows the same individual to be selected more than once. Although this is OK in some implementations of GA/GP, in the case of our optimization algorithm tit doesn't seem to make sense IMO.

Secondly, we should implement a tournament selection as well. It's supposed to give equivalent results (when compared to roulette selection) but it's a lot cheaper.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think cycle from itertools can be useful here: https://docs.python.org/3/library/itertools.html#itertools.cycle

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this file could follow the same pattern I've suggested for FitnessFunctions.

Comment on lines +25 to +26
COPY asset/hyperon_das_node-0.0.1-cp310-abi3-manylinux_2_28_x86_64.whl /app/
RUN pip3 install /app/hyperon_das_node-0.0.1-cp310-abi3-manylinux_2_28_x86_64.whl
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +3 to +7
pyinstaller --onefile ./evolution/main.py
mkdir -p bin
mv ./dist/main ./dist/evolution
mv ./dist/evolution ./bin/evolution
rm -rf build/ __pycache__
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this creating a wheel file?

Please check how the other modules are created using a bazel target:

py_wheel(
name = "hyperon_das_wheel",
abi = "none",
author = "Andre Senna",
author_email = "[email protected]",
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
],
description_content_type = "text/markdown",
description_file = "README.md",
distribution = "hyperon_das",
platform = "any",
python_requires = ">=3.10",
python_tag = "py3",
requires_file = "//deps:requirements_lock.txt",
stamp = 1,
summary = "Query Engine API for Distributed AtomSpace",
version = "$(DAS_VERSION)", # must be defined when calling `bazel build` with `--define=DAS_VERSION=<version>`
deps = [":hyperon_das"],
)

Comment on lines +5 to +26
class FitnessFunctionsTAG(str, enum.Enum):
MULTIPLY_STRENGTHS = "multiply_strengths"


def handle_fitness_function(tag: str) -> callable:
if tag == FitnessFunctionsTAG.MULTIPLY_STRENGTHS:
return multiply_strengths
else:
raise ValueError("Unknown fitness function TAG")


def multiply_strengths(atom_db, query) -> float:
strengths_value = []
for handle in query.handles:
atom = atom_db.get_atom(handle)
if (custom_attributes := atom.custom_attributes):
if (strength := custom_attributes.get('strength')):
strengths_value.append(strength)
# if (truth_value := custom_attributes.get('truth_value')):
# strength, confidence = truth_value
# strengths_value.append(strength)
return math.prod(strengths_value)
Copy link
Collaborator

@angeloprobst angeloprobst Mar 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is cleaner/simpler and also addresses @andre-senna's comment.
Unless I'm not seeing other problems/issues this change could cause.

import math
from typing import ClassVar

def multiply_strengths(atom_db, query) -> float:
    strengths_value = []
    for handle in query.handles:
        atom = atom_db.get_atom(handle)
        if (custom_attributes := atom.custom_attributes):
            if (strength := custom_attributes.get('strength')):
                strengths_value.append(strength)
            # if (truth_value := custom_attributes.get('truth_value')):
            #     strength, confidence = truth_value
            #     strengths_value.append(strength)
    return math.prod(strengths_value)

class FitnessFunctions:
    _fitness_functions: ClassVar[dict[str, callable]] = {
    	"multiply_strengths": multiply_strengths
    }

	@classmethod
    def get(cls, fitness_function_name: str) -> callable:
        if fitness_function_name not in cls._fitness_functions:
            raise ValueError("Unknown fitness function")
        return cls._fitness_functions[fitness_function_name]

In the places where it is used (just an example):

from fitness_functions import FitnessFunctions
...
class QueryOptimizerIterator:
    ...
    def _evaluate(self, query_answer: QueryAnswer) -> tuple[QueryAnswer, float]:
        """ ... """
        fitness_function = FitnessFunctions.get(self.fitness_function)
        ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants