{"id":16440238,"url":"https://github.com/soypat/mu8","last_synced_at":"2025-03-16T17:36:26.486Z","repository":{"id":45268434,"uuid":"441579384","full_name":"soypat/mu8","owner":"soypat","description":"Genetic algorithm for unsupervised machine learning in Go.","archived":false,"fork":false,"pushed_at":"2023-05-16T23:52:49.000Z","size":85,"stargazers_count":120,"open_issues_count":0,"forks_count":5,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-06-19T00:23:32.610Z","etag":null,"topics":["algorithm","evolutionary-algorithm","genetic-algorithm","genetic-algorithms","go","go-generics","golang","golang-generics","island-model","machine-learning","optimization","optimization-algorithms","unsupervised-machine-learning"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/soypat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2021-12-25T00:48:10.000Z","updated_at":"2024-06-04T19:20:06.000Z","dependencies_parsed_at":"2022-08-04T19:01:34.249Z","dependency_job_id":null,"html_url":"https://github.com/soypat/mu8","commit_stats":{"total_commits":61,"total_committers":1,"mean_commits":61.0,"dds":0.0,"last_synced_commit":"972c281266dd261348dead99d95aa262166b6d02"},"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fmu8","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fmu8/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fmu8/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soypat%2Fmu8/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soypat","download_url":"https://codeload.github.com/soypat/mu8/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219861933,"owners_count":16555980,"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":["algorithm","evolutionary-algorithm","genetic-algorithm","genetic-algorithms","go","go-generics","golang","golang-generics","island-model","machine-learning","optimization","optimization-algorithms","unsupervised-machine-learning"],"created_at":"2024-10-11T09:11:36.946Z","updated_at":"2024-10-11T09:11:37.046Z","avatar_url":"https://github.com/soypat.png","language":"Go","readme":"[![go.dev reference](https://pkg.go.dev/badge/github.com/soypat/mu8)](https://pkg.go.dev/github.com/soypat/mu8)\n[![Go Report Card](https://goreportcard.com/badge/github.com/soypat/mu8)](https://goreportcard.com/report/github.com/soypat/mu8)\n[![codecov](https://codecov.io/gh/soypat/mu8/branch/main/graph/badge.svg)](https://codecov.io/gh/soypat/mu8/branch/main)\n[![License](https://img.shields.io/badge/License-BSD_2--Clause-orange.svg)](https://opensource.org/licenses/BSD-2-Clause)\n\n# μ8\n\n\u003cimg align=\"right\" width=\"190px\" src=\"https://user-images.githubusercontent.com/26156425/147430929-bd9adebd-9c00-4ee2-a5bd-bc8642ee9a82.png\"\u003e\n\nSimple unsupervised machine learning package using Go 1.18 generics.\n\n## User information\nμ8 (mu8) uses a simple genetic algorithm implementation to optimize a objective function. It allows optimizing floating point numbers, integers and anything else that can implement the 3 method [`Gene`](./mu8.go) interface\n\nThe genetic algorithm implementation is currently ~150 lines long and is contained in [`population.go`](./genetic/population.go). It consists of the following steps:\n\n1. Natural selection. Best individual conserved (population champion)\n2. Mate.\n3. Mutate babies.\n4. Rinse and repeat.\n\nThe file [`mu8.go`](./mu8.go) contains `Genome` and `Gene` interface definitions. Users should implement `Genome` interface and use `Gene` implementations from [`genes`](./genes) package.\n\nThere is an Islands Model Genetic Algorithm (IMGA) implementation in [`islands.go`](./genetic/islands.go) using the `Islands` type that makes use of a parallel optimization algorithm to make use of multi-core machines.\n\n## μ8 examples\n\n### Basic usage example\nEverything starts with the `mu8.Genome` type on the user side. We define a type that implements it\nusing a helper type `genes.ContrainedFloat` from the `genes` package. All this `genes` type does\nis save us the trouble of writing our own `mu8.Gene` implementation.\n\n```go\ntype mygenome struct {\n\tgenoma []genes.ConstrainedFloat\n}\n\nfunc (g *mygenome) GetGene(i int) mu8.Gene { return \u0026g.genoma[i] }\nfunc (g *mygenome) Len() int               { return len(g.genoma) }\n\n// Simulate simply adds the genes. We'd expect the genes to reach the max values of the constraint.\nfunc (g *mygenome) Simulate() (fitness float64) {\n\tfor i := range g.genoma {\n\t\tfitness += g.genoma[i].Value()\n\t}\n    // fitness must ALWAYS be greater than zero for succesful simulation.\n\treturn math.Max(0, fitness/float64(g.Len()))\n}\n```\nWe're almost ready to optimize our implementation to maximize it's fitness, which would simply be the addition of all it's genes.\n\nLet's write the function that initializes a blank-slate `mygenome`\n\n```go\nfunc newGenome(n int) *mygenome {\n\treturn \u0026mygenome{genoma: make([]genes.ConstrainedFloat, n)}\n}\n```\nThe function above may be confusing... what is the constraint on the number? By default\n`genes.ConstrainedFloat` uses the range [0, 1]. \n\n```go\nconst Nindividuals = 100\nindividuals := make([]*mygenome, Nindividuals)\nfor i := 0; i \u003c Nindividuals; i++ {\n\tgenome := newGenome(genomelen)\n\t// This spices up the initial population so fitnesses are not all zero.\n\tmu8.Mutate(genome, src, .1)\n\tindividuals[i] = genome\n}\n\npop := genetic.NewPopulation(individuals, rand.NewSource(1), func() *mygenome {\n\t\treturn newGenome(3)\n})\n\nconst Ngeneration = 100\nctx := context.Background()\nfor i := 0; i \u003c Ngenerations; i++ {\n\t\terr := pop.Advance(ctx)\n\t\tif err != nil {\n\t\t\tpanic(err.Error())\n\t\t}\n\t\terr = pop.Selection(0.5, 1)\n\t\tif err != nil {\n\t\t\tpanic(err.Error())\n\t\t}\n}\nfmt.Printf(\"champ fitness=%.3f\\n\", pop.ChampionFitness())\n```\nThe final fitness should be close to 1.0 if the algorithm did it's job. For the code see \n[`mu8_test.go`](./mu8_test.go)\n\n### Rocket stage optimization example\n\nSee [`rocket`](./examples/rocket/main.go) for a demonstration on rocket stage optimization. \nBelow is the output of said program\n```\nchampHeight:117.967km\nchampHeight:136.748km\nchampHeight:140.633km\nchampHeight:141.873km\nchampHeight:141.873km\nchampHeight:141.873km\nchampHeight:142.883km\nchampHeight:143.292km\nchampHeight:143.292km\nchampHeight:143.292km\nchampHeight:143.292km\nour champion: \nStage 0: coast=281.2s, propMass=0.0kg, Δm=99.35kg/s, totalMass=200.0\nStage 1: coast=0.0s, propMass=1.6kg, Δm=0.01kg/s, totalMass=21.6\n```\n\n### Gradient \"ascent\" example\n```go\nsrc := rand.NewSource(1)\nconst (\n\tgenomelen      = 6\n\tgradMultiplier = 10.0\n\tepochs         = 6\n)\n// Create new individual and mutate it randomly.\nindividual := newGenome(genomelen)\nrng := rand.New(src)\nfor i := 0; i \u003c genomelen; i++ {\n\tindividual.GetGene(i).Mutate(rng)\n}\n// Prepare for gradient descent.\ngrads := make([]float64, genomelen)\nctx := context.Background()\n// Champion will harbor our best individual.\nchampion := newGenome(genomelen)\nfor epoch := 0; epoch \u003c epochs; epoch++ {\n\t// We calculate the gradients of the individual passing a nil\n\t// newIndividual callback since the GenomeGrad type we implemented\n\t// does not require blank-slate initialization.\n\terr := mu8.Gradient(ctx, grads, individual, nil)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\t// Apply gradients.\n\tfor i := 0; i \u003c individual.Len(); i++ {\n\t\tgene := individual.GetGeneGrad(i)\n\t\tgrad := grads[i]\n\t\tgene.SetValue(gene.Value() + grad*gradMultiplier)\n\t}\n\tmu8.CloneGrad(champion, individual)\n\tfmt.Printf(\"fitness=%f with grads=%f\\n\", individual.Simulate(ctx), grads)\n}\n```\n\n## Contributing\nContributions very welcome! I myself have no idea what I'm doing so I welcome\nissues on any matter :)\n\nPull requests also welcome but please submit an issue first on what you'd like to change.\nI promise I'll answer as fast as I can.\n\nPlease take a look at the TODO's in the project: \u003ckbd\u003eCtrl\u003c/kbd\u003e+\u003ckbd\u003eF\u003c/kbd\u003e `TODO`\n\n## References\nInspired by [CodeBullets amazing video](https://www.youtube.com/watch?v=BOZfhUcNiqk) on the subject.\n\n## Logo work\nGopher rendition by [Juliette Whittingslow](https://www.instagram.com/artewitty/).  \nGopher design authored by [Renée French](https://www.instagram.com/reneefrench)\nis licensed by the Creative Commons Attribution 3.0 licensed.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoypat%2Fmu8","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoypat%2Fmu8","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoypat%2Fmu8/lists"}