{"id":19885095,"url":"https://github.com/dadukhankevin/genespace","last_synced_at":"2025-05-02T16:31:31.505Z","repository":{"id":257938030,"uuid":"870898477","full_name":"dadukhankevin/genespace","owner":"dadukhankevin","description":"Building universal digital genetic code for artificial general evolution (AGE).","archived":false,"fork":false,"pushed_at":"2024-10-16T21:57:04.000Z","size":49,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-19T08:43:34.003Z","etag":null,"topics":["encoder-decoder","finch","genespace","genetic-algorithm","neural-network","neuroevolution","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dadukhankevin.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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-10T21:43:44.000Z","updated_at":"2024-10-16T21:57:08.000Z","dependencies_parsed_at":"2024-10-17T03:25:07.280Z","dependency_job_id":null,"html_url":"https://github.com/dadukhankevin/genespace","commit_stats":null,"previous_names":["dadukhankevin/genespace"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dadukhankevin%2Fgenespace","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dadukhankevin%2Fgenespace/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dadukhankevin%2Fgenespace/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dadukhankevin%2Fgenespace/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dadukhankevin","download_url":"https://codeload.github.com/dadukhankevin/genespace/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224319311,"owners_count":17291816,"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":["encoder-decoder","finch","genespace","genetic-algorithm","neural-network","neuroevolution","python"],"created_at":"2024-11-12T17:32:33.591Z","updated_at":"2024-11-12T17:33:17.822Z","avatar_url":"https://github.com/dadukhankevin.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GeneSpace: A New Framework for AGE: Artificial General Evolution\n\nGeneSpace is a genetic algorithm framework that aims unify all genetic algorithms into one algorithm by using a *universal* genetic code, and neural network style *genespace decoders* to map genotypes to phenotypes.\n\n**It is very much a work in progress, and super rough around the edges.**\n\nI hope the potential still shows.\n\n## Installation\n```bash\ngit clone https://github.com/dadukhankevin/genespace\n```\n\n## About\n### Colab Notebook\n**Demo 1:** [Solve two problems with one algorithm](https://colab.research.google.com/drive/1PeIKc-5Iv8z19bOfarrv-mkxe5hQW3kT?usp=sharing)\n\nThat's pretty much it, the *goal* is that every problem can be solved with this algorithm:s\n```python\nge = general_evolution.GeneralEvolution(fitness_function, \n                                        output_shape=ANY_SHAPE, \n                                        device=\"cuda\")\nge.solve(SOME_NUMBER_OF_STEPS)\n```\nIt's far from perfect yet, but it's a start! All you have to do is change the fitness function, and the output shape.\n\nBut behind this simple interface, a lot of interesting stuff is happening to make this work, here's how it works:\n\n## Background: Phenotypes vs Genotypes\nI'm no expert in biology, but as you likely remember from 7th grade science, our existence can be explained in two ways:\n\n- our genotype (our DNA)\n- our phenotype (our observable traits)\n\n\nInterestingly, in biology, multiple genotypes can lead to the same phenotype. This leads to interesting concepts\nlike convergent evolution, where two different species evolve similar traits in response to similar environmental pressures -- even with completely different underlying DNA (genotypes).\n\nOn the other hand, sometimes the same genotype (DNA) can lead to different phenotypes (observable traits). This hints that there are some evolvable traits that are not encoded in our DNA (genotype), but are instead encoded in some other (possibly non-genetic) format. In biology, this is called*epigenetics*, which involves changes in phenotype without altering the DNA sequence itself. \n\n## The Problem with (current) Evolutionary Algorithms\n\nThe difference between genotypes and phenotypes is fundamental to how biology works, and how evolution works in biology. Imagine if DNA and our observable traits had a 1-1 relationship. It would mean every cell in our body would need DNA telling it precisely where to be specifically, what atoms it requires, etc. Instead, we see that this is not the case. DNA is a highly compressed format for information, and our phenotypes are a result of this compressed information being *expressed/interpreted* in a specific way.\n\nIn *genetic algorithms*, we are almost always directly evolving the phenotypes of our solutions. Even with algorithms that claim to seperate G-P mappings, it is usually pretty superficial. This leads to a *lot* of problems; I think these include:\n\n\n- **Highly specific genetic algorithms**: Since every genetic algorithm works by directly evolving observable traits (phenotypes), it means there can be no *universal genetic code* like we have in biology (DNA). Each algorithm must then be specifically designed for each task, and there can be no sharing of genetic material between algorithms where the focus may be on different modalities.\n\n- **Inefficiency**: Since we are only evolving the phenotypes of our solutions, we are greatly limited by the size of our phenotypes, and so we must, to our detriment, keep our populations low.\n\n- **Bad Search Spaces**: In typical GAs, our search space is so wide that we must explore it very slowly. A single mutation usually results in a very small incremental change to our solution. In true language-based evolution, we see that a single mutation can actually have large changes to the phenotype. This also means that crossover, in many ways, is more like combining ideas rather than combining phenotypes. \n\nI have found one older paper that has a similar concept here (paper link)[https://link.springer.com/chapter/10.1007/978-3-319-10762-2_11] But I can't find any code, the focus seems a bit different, and the paper is more theoretical. This project is all code, and focuses on practical applications.\n\n## How GeneSpace Solves These Problems\n\nThis project is brand new, but it is heavily based on Finch, a framework I have built over the past several years. While building it, and rebuilding it many times, I learned a lot about GAs and their limitations. My hope is that GeneSpace will build upon what I learned while building Finch, but also be a much more powerful, general, almost *linguistic* framework for artificial evolution. \n\nSo here's GeneSpace:\n\n### Universal \"genespaces\"\nIn DNA, everything is represented as a sequence of *A* *T* *C* *G*. In GeneSpace, we represent everything as a sequence of either binaries either *0* and *1*.\n\n### GeneSpace Decoders\n\nIf our genotype of binary, how do we decode it into a phenotype? We use a decoder. Here it makes sense to use neural networks. (I almost called these gene regulatory networks).\n\nWe will call these decoders *GeneSpaceDecoders* (*/decoders.py*). They are neural networks that take a genotype and decode it into a phenotype. As the genetic algorithm is evolving these sequences of floats (inputs), we can use these decoders to generate a phenotype (output) of any size or shape. Since MLPs (Multilayer Perceptrons) are known as *universal function approximators*, we can use them to approximate any continuous function, including our decoders.\n\nThis means that over time, our GA will evolve genotypes that best decode into phenotypes, all while our *genespace* is learning to get better at producing the phenotypes from the genotypes. We can do this using backpropagation: mapping the worst genotypes to the best phenotypes.\n\nThis ends up creating a more flexible and powerful (co)evolutionary system. We only need one algorithm, one that works on inputs to the decoders, and we can decode it into any shape or size we want!\n\nI have also been inspired by Stephen Wolfram's posts about \"mining the mathematical universe\" and as such, these algorithms focus on creating *genespace*(s) that just *happen* to work, and genes that just *happen* to exploit these neural networks appropriately. Without rhyme or reason really. I learned this could work when I accidentally discovered that genetic algorithms could *just so happen* to evolve prompts/images that trick the best image generation/recognition models into producing almost any desired output. This whole project is building on that discovery. \n\n## Current State\n\nThis project is still in its infancy. Below are some examples demonstrating the versatility of GeneSpace.\n\n## Contributing\n\nPlease contribute! You can contact me on X/Twitter [@DanielJLosey](https://x.com/DanielJLosey).\n\n## The Future\n\nAGE: Artificial General Evolution. (Like AGI, but for evolution.)\nGenetic algorithms that can evolve any arbitrary solution, with a universal -shareable- genetic code. A universal general genetic algorithm applicable to any problem. I hope this project will be a good step toward that goal. \n\n## License\n\nThis project is licensed under the MIT License with Attribution:\n\nCopyright (c) 2024 [Daniel Losey](https://www.x.com/DanielJLosey)\n\nSee the LICENSE file for more details.\n# Examples\n\n## General Evolution\n\nGeneSpace introduces a `GeneralEvolution` class that can be used to solve various optimization problems using the same underlying algorithm. Here are two examples demonstrating its versatility:\n\n### Image Evolution\n\nIn this example, we use GeneSpace to evolve an image to match a target image.\n\n```python\nfrom genespace import general_evolution\nimport torch\nimport numpy as np\nfrom PIL import Image\nimport requests\nfrom io import BytesIO\nimport matplotlib.pyplot as plt\nimport torch.nn.functional as F\n\ndef download_image(size):\n    response = requests.get('https://static.wikia.nocookie.net/disney/images/6/64/Profile_-_Spider-Man.png/revision/latest?cb=20220320010954')\n    img = Image.open(BytesIO(response.content)).convert('RGB')\n    img = img.resize(size)\n    return np.array(img) / 255.0\n\nX = 50  # Image size\nimage = download_image((X, X))\ntarget_img_tensor = torch.from_numpy(image).float().to('cuda')\n\ndef image_fitness(phenotypes):\n    target = target_img_tensor.unsqueeze(0).repeat(phenotypes.size(0), 1, 1, 1)\n    mse = F.mse_loss(phenotypes, target, reduction='none')\n    mse = mse.view(mse.size(0), -1).mean(dim=1) \n    fitness = 1 / (mse + 1e-8)\n    fitness_percentages = (fitness / (1 / 1e-8)) * 100\n    return fitness_percentages.tolist()\n\nimage_ge = general_evolution.GeneralEvolution(image_fitness, \n                                              output_shape=(X, X, 3), \n                                              device=\"cuda\", scale=2)\n\nimage_ge.solve(32000)\n\n# Render the evolved image\ndef render_evolved_image(environment, index=0):\n    genes = environment.individuals[index].genes\n    genes_tensor = torch.tensor(genes, dtype=torch.float32).to(environment.genepool.gsp.device)\n    genes_tensor = genes_tensor.unsqueeze(0) \n    with torch.no_grad():\n        phenotype = environment.genepool.gsp.forward(genes_tensor)\n    image_array = phenotype.squeeze().cpu().numpy()\n    image_array = np.clip(image_array, 0, 1)\n    plt.figure(figsize=(5, 5))\n    plt.imshow(image_array)\n    plt.axis('off')\n    plt.title(f'Evolved Image (Individual {index})')\n    plt.show()\n\nrender_evolved_image(image_ge.environment)\n```\nNote that the algorithm only takes up 2 lines!\n```python\nimage_ge = general_evolution.GeneralEvolution(image_fitness, \n                                              output_shape=(X, X, 3), \n                                              device=\"cuda\", scale=2)\nimage_ge.solve(32000)\n```\n\n### Traveling Salesman Problem (TSP)\n\nIn this example, we use the same `GeneralEvolution` class to solve the Traveling Salesman Problem.\n\n```python\nimport numpy as np\nimport torch\nfrom genespace import general_evolution\nimport matplotlib.pyplot as plt\n\n# Define the cities\nnum_cities = 12\nnp.random.seed(42)\ncities = np.random.rand(num_cities, 2)\n\n# Compute the distance matrix\ndistance_matrix = np.sqrt(((cities[:, np.newaxis, :] - cities[np.newaxis, :, :]) ** 2).sum(axis=2))\n\ndef city_fitness(phenotypes):\n    batch_size = phenotypes.size(0)\n    total_distances = []\n    for i in range(batch_size):\n        phenotype = phenotypes[i].cpu().numpy()\n        route = np.argsort(phenotype)\n        total_distance = 0.0\n        for j in range(num_cities):\n            from_city = route[j]\n            to_city = route[(j + 1) % num_cities]\n            total_distance += distance_matrix[from_city, to_city]\n        total_distances.append(total_distance)\n    fitness_values = 1 / (np.array(total_distances) + 1e-8)\n    return fitness_values.tolist()\n\ntsp_ge = general_evolution.GeneralEvolution(city_fitness, \n                                            output_shape=(num_cities,), \n                                            device=\"cuda\", scale=2)\n\ntsp_ge.solve(400)\n\n# Plot the best route\ndef plot_route(tsp):\n    best_genes = tsp.environment.individuals[0].genes\n    best_genes_tensor = torch.tensor(best_genes, dtype=torch.float32).unsqueeze(0).to('cuda')\n\n    with torch.no_grad():\n      phenotype = tsp.environment.genepool.gsp.forward(best_genes_tensor)\n    phenotype = phenotype.cpu().numpy().squeeze()\n    route = np.argsort(phenotype)\n\n    plt.figure(figsize=(8, 6))\n    route_cities = cities[route]\n    plt.plot(route_cities[:, 0], route_cities[:, 1], 'o-', label='Route')\n    plt.plot([route_cities[-1, 0], route_cities[0, 0]], [route_cities[-1, 1], route_cities[0, 1]], 'o-')\n    for i, city in enumerate(cities):\n        plt.text(city[0], city[1], f'City {i}')\n    plt.title('Best TSP Route Found')\n    plt.xlabel('X Coordinate')\n    plt.ylabel('Y Coordinate')\n    plt.legend()\n    plt.show()\n\nplot_route(tsp_ge)\n```\nAgain, the algorithm only takes up 2 lines!\n```python\ntsp_ge = general_evolution.GeneralEvolution(city_fitness, \n                                            output_shape=(num_cities,), \n                                            device=\"cuda\", scale=2)\n\ntsp_ge.solve(400) # Less steps\n```\n\nWith almost no modifications to the code, we can solve completely different problems. \n\nGeneSpaceDecoders.\n\nEt. voila.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdadukhankevin%2Fgenespace","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdadukhankevin%2Fgenespace","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdadukhankevin%2Fgenespace/lists"}