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

Using struct instead of modules to describe problems? #20

Open
idabmat opened this issue Jul 1, 2022 · 2 comments
Open

Using struct instead of modules to describe problems? #20

idabmat opened this issue Jul 1, 2022 · 2 comments

Comments

@idabmat
Copy link

idabmat commented Jul 1, 2022

To use Genex today, a user builds a module and defines three functions. If the problem has parameters that impact how these functions should work, they can define functions to hold that data. That is the approach taken in the knapsack problem example.

But then what happens if the user wants to solve different versions of the same problem? Like solving the knapsack problem with other weights/profits/weight limits. Currently, they would have to either rewrite the module every time or duplicate it for each version. While working through Genetic Algorithms in Elixir, I opted for protocols when implementing the book's framework. I found the result easier to customise. Again, taking the knapsack problem as an example, the code would look something like this:

defmodule Knapsack do
  @enforce_keys [:profits, :weigths, :weight_limit]
  defstruct [:profits, :weights, :weight_limit, bound_breached: 0]

  defimpl Genex.Solvable do
    def genotype(knapsack), do: Genex.Tools.Genotype.binary(Enum.count(knapsack.weights))

    def fitness_function(knapsack, chromosome) do
      profit =
        chromosome.genes
        |> Enum.zip(knapsack.profits)
        |> Enum.reduce(0, fn {g, p}, acc -> acc + g * p end)

      weight_carried =
        chromosome.genes
        |> Enum.zip(knapsack.weights)
        |> Enum.reduce(0, fn {g, w}, acc -> acc + g * w end)

      weight_allowed? = weight_carried <= knapsack.weight_limit

      if weight_allowed? do
        profit
      else
        knapsack.bound_breached
      end
    end

    def terminate?(_knapsack, population), do: population.generation == 1000
  end
end

solution =
  %Knapsack{profits: [6, 5, 8, 9, 6, 7, 3], weights: [2, 3, 6, 7, 5, 9, 4], weight_limit: 12}
  |> Genex.run(
    title: "Knapsack",
    crossover_type: Genex.Tools.Crossover.two_point(),
    selection_type: Genex.Tools.Selection.roulette(),
    population_size: 50
  )

IO.inspect(solution.strongest.genes)

That would be a breaking change, but since 1.0 was not yet released, I thought this could be an appropriate time to suggest such a change.

Would a PR introducing this be welcome?

@seanmor5
Copy link
Owner

seanmor5 commented Jul 1, 2022

Hi @idabmat ! Thank you for your contributions! I think this is a good idea! Right now I don't have much bandwidth to maintain this library anymore, if you would like I can transfer ownership and offer guidance from time to time!

In my opinion any direction forward for evolutionary algorithms in Elixir should use Nx. For example: https://github.com/jonatanklosko/meow

If you're interested in contributing something in this direction let me know :)

@idabmat
Copy link
Author

idabmat commented Jul 4, 2022

Thanks for the reply @seanmor5. I imagine you have enough on your plate :). I am happy to contribute to this library in any way you see fit. Either sending PRs or maintaining it. Thanks for the link to meow. Will have a look at it.

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

No branches or pull requests

2 participants