{"id":15489912,"url":"https://github.com/methodswithclass/evolve","last_synced_at":"2025-03-28T16:26:28.829Z","repository":{"id":26100545,"uuid":"107342587","full_name":"methodswithclass/evolve","owner":"methodswithclass","description":"An Evolutionary Algorithm written in JavaScript that can take in any generic module representing a problem to be optimized ","archived":false,"fork":false,"pushed_at":"2023-08-19T05:53:52.000Z","size":171,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-04-25T18:03:31.634Z","etag":null,"topics":["algorithms","evolutionary","evolutionary-algorithm","generation","genetic","genetic-algorithm","individual","maximization","maximize","minimization","minimum","mutate","optimization","organism","population"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/methodswithclass.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2017-10-18T01:18:27.000Z","updated_at":"2023-08-19T03:58:42.000Z","dependencies_parsed_at":"2023-09-29T12:48:13.424Z","dependency_job_id":null,"html_url":"https://github.com/methodswithclass/evolve","commit_stats":{"total_commits":70,"total_committers":2,"mean_commits":35.0,"dds":"0.19999999999999996","last_synced_commit":"e4bb6457ca9c731ae93f8a1864524c7107e22aa2"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/methodswithclass%2Fevolve","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/methodswithclass%2Fevolve/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/methodswithclass%2Fevolve/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/methodswithclass%2Fevolve/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/methodswithclass","download_url":"https://codeload.github.com/methodswithclass/evolve/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246061436,"owners_count":20717432,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["algorithms","evolutionary","evolutionary-algorithm","generation","genetic","genetic-algorithm","individual","maximization","maximize","minimization","minimum","mutate","optimization","organism","population"],"created_at":"2024-10-02T07:08:41.476Z","updated_at":"2025-03-28T16:26:28.797Z","avatar_url":"https://github.com/methodswithclass.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Evolutionary Algorithm\r\n\r\nThis library optimizes a strategy to solve a problem by way of an evolutionary algorithm, a model of biological evolution: rank fitness, crossover of best performing, mutation, run next generation.\r\n\r\nThe initial generation is made up of individuals with random strategies, the output is a strategy that is optimized to solve the given problem\r\n\r\nThe algorithm is agnostic to the actual problem, it is primarily specified through the getFitness function\r\n\r\n# Usage\r\n\r\n`npm install @methodswithclass/evolve`\r\n\r\n`import Evolve from @methodswithclass/evolve`\r\n\r\nThe input consists of options for the evolutionary algorithm and the specification of your problem.\r\n\r\n## Mutate\r\n\r\ncalled when processing each individual\r\n\r\nreturns a brand new strategy or a mutated strategy\r\n\r\nrequired, can be async\r\n\r\nstrategy is the central object that is being optimized (any type)\r\n\r\nif input strategy is undefined then return a brand new strategy with random values, if it exists, return a copy of it, with some values newly randomized by some percentage\r\n\r\nthis function would typically return a randomly valued item (within a relevant range)\r\n\r\n```\r\nconst mutate = async (strategy) =\u003e {\r\n\treturn newStrategy // can be any type, typically the strategy is a consistent type\r\n}\r\n\r\n```\r\n\r\n## BeforeEach\r\n\r\ncalled before processing each new generation\r\n\r\nreturns a boolean on whether to process the next generation, the current generation will finish\r\n\r\noptional, can be async\r\n\r\ngeneration is the number of the current generation\r\n\r\n```\r\nconst beforeEach = async (input) =\u003e {\r\n\tconst { generation, first, last } = input;\r\n\treturn true/false // must explicitly return false if you want the process to stop\r\n}\r\n```\r\n\r\n## AfterEach\r\n\r\ncalled after processing each generation\r\n\r\nreturns nothing\r\n\r\noptional, can be async\r\n\r\ngeneration is the number of the current generation\r\n\r\nbest is the top performing individual of the generation: { strategy, fitness }\r\n\r\n```\r\nconst afterEach = async (input) =\u003e {\r\n\tconst { generation, best } = input;\r\n\tno return\r\n}\r\n```\r\n\r\n## OnEnd\r\n\r\ncalled when all the generations have been processed or evolution told to stop (beforeEach returns false)\r\n\r\noptional, can be async\r\n\r\ngeneration is the number of the current generation\r\n\r\nbest is the top performing individual of the generation: { strategy, fitness }\r\n\r\n```\r\nconst onEnd = async (input) =\u003e {\r\n\tconst { generation, best } = input;\r\n\tno return\r\n}\r\n```\r\n\r\n## GetFitness\r\n\r\nthe primary definition of your problem, it takes a strategy and runs it against the problem to produce a measure of its performance, the fitness\r\n\r\nrequired, can be async\r\n\r\npopulations for every generation run all individuals simultaneously, indexes not guaranteed to be in order\r\n\r\nstrategy is the object (any type) formed by mutate(), fitness is calculated by some process on this object\r\n\r\nindex: {generation number}#{index of individual in population array}\r\n\r\nid: {unique id for generation}#{unique id for individual}\r\n\r\nreturns the fitness of an individual (any type), can be async\r\n\r\nthis fitness is then passed into rank\r\n\r\n```\r\nconst getFitness = async (input) =\u003e {\r\n\tconst { index, id, strategy } = input;\r\n\treturn fitness;\r\n}\r\n\r\n```\r\n\r\n## Combine\r\n\r\ncalled during generation crossover\r\n\r\nrequired, can be async\r\n\r\nstrategies are pulled from the top performers of the generation at random\r\n\r\n```\r\nconst combine = async (strategyA, strategyB) =\u003e {\r\n\treturn newStrategy\r\n}\r\n\r\n```\r\n\r\n## Rank\r\n\r\ncomparator according to Array.sort(),\r\n\r\noptional, must be synchronous\r\n\r\nsome function according to your optimization and fitness scheme/type\r\n\r\nif not included Evolve will treat the fitness as a number, and will maximize it\r\n\r\n```\r\nconst rank = (fitnessA, fitnessB) =\u003e {\r\n\treturn fitnessB - fitnessA;\r\n}\r\n```\r\n\r\n## Initialization\r\n\r\n```\r\nconst input = {\r\n\tfirst, // first generation number, required\r\n\tlast, // last generation number, must be greater than first, required\r\n\tbest, // initial best performer { strategy, fitness }, optional, used when first is not 1, when this is included, all individuals in the first generation are mutated versions of this\r\n\tpopTotal, // number of individuals per generation, required, defaults to 100\r\n\tbeforeEach,\r\n\tafterEach,\r\n\tonEnd,\r\n\tgetFitness,\r\n\tcombine,\r\n\trank,\r\n\tmutate,\r\n}\r\n\r\nconst evolve = Evolve(input);\r\n\r\nawait evolve.start();\r\n\r\nevolve.stop();\r\n```\r\n\r\nprocess auto-stops when the current generation equals the value in `last`\r\n\r\n# Example\r\n\r\n```\r\nconst totalGenes = 100;\r\n\r\nconst getGene = () =\u003e {\r\n\treturn Math.random();\r\n};\r\n\r\nconst mutate = (strategy) =\u003e {\r\n\tconst newStrategy = [];\r\n\r\n\tif (!strategy) {\r\n\t\tfor (let i = 0; i \u003c totalGenes; i++) {\r\n\t\t\tnewStrategy.push(getGene());\r\n\t\t}\r\n\t\treturn newStrategy;\r\n\t}\r\n\r\n\tconst mutatedStrategy = strategy.map((item) =\u003e {\r\n\t\tif (Math.random() \u003c 0.02) {\r\n\t\t\treturn getGene();\r\n\t\t}\r\n\r\n\t\treturn item;\r\n\t});\r\n\r\n\treturn mutatedStrategy;\r\n};\r\n\r\nconst combine = (a, b) =\u003e {\r\n\tconst aSegment = a.slice(0, totalGenes / 2);\r\n\tconst bSegment = b.slice(totalGenes / 2);\r\n\r\n\treturn [...aSegment, ...bSegment];\r\n};\r\n\r\nconst getFitness = ({ strategy }) =\u003e {\r\n\tconst fitness = strategy.reduce((total, item) =\u003e {\r\n\t\tif (1 - item \u003c 0.01) {\r\n\t\t\treturn total + 50;\r\n\t\t}\r\n\t\treturn total - 20;\r\n\t}, 0);\r\n\r\n\treturn fitness;\r\n};\r\n\r\nlet bestPerformer;\r\n\r\nconst evolve = Evolve({\r\n\tfirst: 1,\r\n\tlast: 100,\r\n\tpopTotal: 100,\r\n\tafterEach: ({ generation, best }) =\u003e {\r\n\t\tconsole.log('current generation', generation);\r\n\t\tbestPerformer = best;\r\n\t},\r\n\tgetFitness,\r\n\tmutate,\r\n\tcombine,\r\n});\r\n\r\nawait evolve.start();\r\n\r\nconst { strategy, fitness } = bestPerformer;\r\n\r\nthis resulting strategy will be an array of values all close to 1;\r\n```\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmethodswithclass%2Fevolve","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmethodswithclass%2Fevolve","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmethodswithclass%2Fevolve/lists"}