Compare commits

...

10 Commits

Author SHA1 Message Date
anthonyrawlins
ec48447670 Add MEND module duplication operator
Implement the core MEND mechanism: a module duplication mutation that
copies a hidden node with all its incoming and outgoing connections.
This is the single addition MEND makes to standard NEAT.

- ModuleDuplication: JAX-compatible operator using jax.lax.scan
- CombinedMutation: composes DefaultMutation + ModuleDuplication
- DefaultGenome: accepts duplication_rate parameter
- Tests for standalone duplication, combined mutation, and rate=0 no-op

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 10:40:54 +11:00
wls2002
7e872c7191 update logo 2026-02-09 11:36:44 +08:00
wls2002
cc651bbd75 update evox logo 2026-02-09 11:35:40 +08:00
WLS2002
b9778dcf8b Update README.md 2025-08-20 20:29:25 +08:00
WLS2002
44be8fdc4f Update README.md 2025-08-20 20:29:06 +08:00
WLS2002
505556a214 Update README.md 2025-08-20 20:26:47 +08:00
WLS2002
79b3b4af1c Create README.md in EvoX example 2025-08-20 20:18:28 +08:00
WLS2002
4422cc613e Merge pull request #30 from mg10011/feature/RecurrentMutation
Add RecurrentMutation class for more control over recurrent mutations
2025-04-23 09:40:58 +08:00
massy
7b2ae5c8a2 Add RecurrentMutation class for more control over recurrent mutations
Extends DefaultMutation with a specialized implementation that:
- Adds a bias parameter (p_recur) for controling the likelihood of
  a new connection mutation to be a recurrent connection
- Implements an vectorized algorithm for connection addition with retry
  logic
- Preserves all other mutation operations from the base implementation
- Maintains API compatibility with DefaultMutation
2025-04-22 10:35:47 -04:00
WLS2002
96923be0b5 Update README.md 2025-04-21 12:44:14 +08:00
14 changed files with 834 additions and 26 deletions

View File

@@ -1,9 +1,9 @@
<h1 align="center">
<a href="https://github.com/EMI-Group/evox">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="./imgs/evox_logo_dark.png">
<source media="(prefers-color-scheme: light)" srcset="./imgs/evox_logo_light.png">
<img alt="EvoX Logo" height="50" src="./imgs/evox_logo_light.png">
<source media="(prefers-color-scheme: dark)" srcset="./imgs/evox_brand_light.svg">
<source media="(prefers-color-scheme: light)" srcset="./imgs/evox_brand_dark.svg">
<img alt="EvoX Logo" height="50" src="./imgs/evox_brand_light.svg">
</picture>
</a>
<br>
@@ -296,6 +296,15 @@ pip install git+https://github.com/EMI-Group/tensorneat.git
## Multi-device and Distributed Acceleration
TensorNEAT doesn't natively support multi-device or distributed execution, but these features can be accessed via the EvoX framework. EvoX is a high-performance, distributed, GPU-accelerated framework for Evolutionary Algorithms. For more details, visit: [EvoX GitHub](https://github.com/EMI-Group/evox/).
**Notice**: As the latest EvoX has been migrated to the PyTorch backend, we need to install the JAX-Version EvoX to run multi-device EvoX.
The current JAX-Version Evox branch is [v0.9.1-dev](https://github.com/EMI-Group/evox/tree/v0.9.1-dev).
Use
```bash
pip install "git+https://github.com/EMI-Group/evox@v0.9.1-dev"
```
to install the JAX based EvoX.
TensorNEAT includes an EvoX Adaptor, which allows TensorNEAT algorithms to run within the EvoX framework. Additionally, TensorNEAT provides a monitor for use with EvoX.
Here is an example of creating an EvoX algorithm and monitor:
@@ -410,17 +419,17 @@ We warmly welcome community developers to contribute to TensorNEAT and look forw
If you use TensorNEAT in your research and want to cite it in your work, please use:
```
@inproceedings{10.1145/3638529.3654210,
@article{10.1145/3730406,
author = {Wang, Lishuang and Zhao, Mengfei and Liu, Enyu and Sun, Kebin and Cheng, Ran},
title = {Tensorized NeuroEvolution of Augmenting Topologies for GPU Acceleration},
year = {2024},
isbn = {9798400704949},
doi = {10.1145/3638529.3654210},
booktitle = {Proceedings of the Genetic and Evolutionary Computation Conference},
pages = {11561164},
numpages = {9},
keywords = {neuroevolution, GPU acceleration, algorithm library},
location = {Melbourne, VIC, Australia},
series = {GECCO '24}
title = {TensorNEAT: A GPU-accelerated Library for NeuroEvolution of Augmenting Topologies},
year = {2025},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
url = {https://doi.org/10.1145/3730406},
doi = {10.1145/3730406},
journal = {ACM Trans. Evol. Learn. Optim.},
month = apr,
keywords = {Neuroevolution, GPU Acceleration, Algorithm Library}
}
```

View File

@@ -0,0 +1,9 @@
# Notice
This part is based on the JAX-Version EvoX.
To run this example, please install EvoX with the [jax-version branch: (v0.9.1-dev)](https://github.com/EMI-Group/evox/tree/v0.9.1-dev)
Use this command to install EvoX's JAX Version:
```bash
pip install "git+https://github.com/EMI-Group/evox@v0.9.1-dev"
```

54
imgs/evox_brand_dark.svg Normal file
View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="6062.18mm" height="1250mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 625393.22 128953.89"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<style type="text/css">
<![CDATA[
.fil3 {fill:#373435}
.fil4 {fill:#C8383C}
.fil2 {fill:url(#id0)}
.fil0 {fill:url(#id1)}
.fil1 {fill:url(#id2)}
]]>
</style>
<linearGradient id="id0" gradientUnits="userSpaceOnUse" x1="359566.17" y1="126101.11" x2="359566.17" y2="2853.49">
<stop offset="0" style="stop-opacity:0.917647; stop-color:#373435"/>
<stop offset="0.309804" style="stop-opacity:0.835294; stop-color:#373535"/>
<stop offset="0.560784" style="stop-opacity:0.74902; stop-color:#373636"/>
<stop offset="0.788235" style="stop-opacity:0.87451; stop-color:#302F30"/>
<stop offset="1" style="stop-opacity:1; stop-color:#282829"/>
</linearGradient>
<linearGradient id="id1" gradientUnits="userSpaceOnUse" xlink:href="#id0" x1="56326.49" y1="126101.11" x2="56326.49" y2="2853.49">
</linearGradient>
<linearGradient id="id2" gradientUnits="userSpaceOnUse" xlink:href="#id0" x1="199869.19" y1="126101.11" x2="199869.19" y2="2853.49">
</linearGradient>
</defs>
<g id="圖層_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<path class="fil0" d="M111173.52 4085.97l-110434.04 0c-205.57,0 -390.44,81.83 -521.58,212.97l-4.93 4.93c-131.13,131.13 -212.97,316.01 -212.97,521.58l0 23170.55c0,202.13 83.81,387.49 217.9,521.58 131.14,136.07 316.01,217.91 521.58,217.91l111173.52 0c202.13,0 387.49,-83.81 521.58,-217.91 134.09,-134.09 217.9,-319.45 217.9,-521.58l0 -22431.06c0,-814.42 -664.55,-1478.97 -1478.97,-1478.97zm1478.97 73209.08l0 -24896.02c0,-814.42 -664.55,-1478.97 -1478.97,-1478.97l-110434.04 0c-205.57,0 -390.44,81.84 -521.58,212.97l-4.93 4.93c-131.13,131.14 -212.97,316.01 -212.97,521.58l0 72469.6c0,202.13 83.81,387.49 217.9,521.58 131.14,136.07 316.01,217.9 521.58,217.9l111173.52 0c202.13,0 387.49,-83.8 521.58,-217.9 134.09,-134.09 217.9,-319.45 217.9,-521.58l0 -22431.06c0,-814.42 -664.55,-1478.97 -1478.97,-1478.97l-80854.68 0c-879,0 -1680.1,-360.38 -2262.33,-942.11 -582.22,-577.29 -942.11,-1379.39 -942.11,-2262.34l0 -15775.69c0,-878.02 359.39,-1678.14 939.64,-2259.87l4.93 -4.93c581.73,-580.25 1381.85,-939.64 2259.86,-939.64l81594.16 0c202.13,0 387.49,-83.81 521.58,-217.9 134.09,-134.09 217.9,-319.45 217.9,-521.58z"/>
<path class="fil1" d="M160583.37 4085.97l-25473.74 0c-135.08,0 -254.89,27.61 -352,77.89 -99.09,55.21 -188.81,138.04 -262.26,246.99 -76.42,109.44 -119.8,223.81 -131.14,333.26 -11.83,113.39 7.4,237.13 57.68,362.35l48201.05 119303.69c58.17,144.45 152.33,260.8 269.17,339.18 113.39,76.41 257.83,119.3 418.54,119.3l33473.97 0c161.2,0 305.16,-42.88 418.54,-119.3 116.84,-78.38 211,-194.73 269.17,-339.18l47792.35 -118291.1c189.8,-469.82 138.53,-960.84 -144.94,-1380.86 -283.46,-420.52 -719.26,-652.23 -1226.06,-652.23l-24381.27 0c-160.72,0 -305.17,42.88 -418.55,119.3 -116.83,78.39 -211.49,195.23 -269.17,339.18l-35807.78 88627.85c-247.97,616.24 -658.63,1117.62 -1174.3,1465.17 -517.64,349.03 -1135.36,543.28 -1794.98,543.28 -660.11,0 -1277.33,-194.25 -1794.98,-543.28 -515.66,-347.56 -926.32,-848.93 -1174.3,-1465.17l-35807.3 -88627.85c-57.68,-143.95 -152.33,-260.79 -269.17,-339.18 -113.39,-76.41 -257.34,-119.3 -418.54,-119.3z"/>
<path class="fil2" d="M396465.92 4085.97l-73800 0c-8492.23,0 -16213.92,3475.58 -21812.31,9073.48 -5598.38,5598.9 -9073.46,13320.11 -9073.46,21812.36l0 59010.96c0,8492.25 3475.58,16213.96 9073.46,21812.36 5598.39,5597.91 13320.08,9073.48 21812.31,9073.48l73800 0c8492.23,0 16214.41,-3475.58 21812.31,-9073.98 5598.38,-5597.91 9073.96,-13319.62 9073.96,-21811.87l0 -59010.96c0,-8492.25 -3475.58,-16213.96 -9073.96,-21811.87 -5597.9,-5598.4 -13320.08,-9073.98 -21812.31,-9073.98zm-68834.62 24649.53l63869.72 0c3081.68,0 5883.34,1260.08 7912.49,3289.23 2029.14,2029.15 3289.22,4830.81 3289.22,7912.5l0 49079.66c0,3081.68 -1260.09,5883.35 -3289.22,7912.5 -2029.15,2029.15 -4830.81,3289.23 -7912.49,3289.23l-63869.72 0c-3081.67,0 -5883.33,-1260.09 -7912.48,-3289.23 -2029.15,-2029.15 -3289.22,-4830.82 -3289.22,-7912.5l0 -49079.66c0,-3081.69 1260.08,-5883.35 3289.22,-7912.5 2029.15,-2029.16 4830.81,-3289.23 7912.48,-3289.23z"/>
<path class="fil3" d="M522115.89 82633.04l-14940.89 -15397.6c-51.58,-53.21 -115.75,-79.7 -189.87,-78.29 -74.03,1.42 -137.28,30.26 -186.84,85.3l-2352.65 2612.89c-93.82,104.17 -87.02,264.71 15.13,360.56l15613.97 14650.18c52.5,49.24 115.35,72.6 187.24,69.55 71.88,-3.04 132.61,-31.58 180.83,-84.99l1679.65 -1865.47c90.98,-101.03 88.04,-254.55 -6.59,-352.14z"/>
<path class="fil3" d="M516827.35 88459.62l-16690.8 -13454.25c-106.71,-86 -261.77,-73.62 -353.45,28.23l-2891.52 3211.16c-50.87,56.55 -72.91,124.69 -64.68,200.33 8.13,75.55 44.28,137.49 106.01,181.75l17498.33 12557.38c107.23,76.96 253.65,61.12 341.99,-36.96l2083.87 -2314.37c49.24,-54.52 71.28,-119.71 65.49,-193.02 -5.89,-73.32 -38.08,-134.13 -95.24,-180.24z"/>
<path class="fil3" d="M511534.04 94292.2l-18440.5 -11510.81c-107.02,-66.81 -243.69,-47.52 -328.17,46.2l-3424.7 3803.43c-54.73,60.82 -76.45,135.97 -62.44,216.49 13.91,80.62 59.61,144.18 131.59,182.97l19382.78 10464.45c106.01,57.27 233.44,35.23 314.06,-54.33l2482.52 -2757.17c52.8,-58.6 74.73,-130.27 63.66,-208.46 -10.97,-78.19 -51.78,-140.94 -118.8,-182.77z"/>
<path class="fil3" d="M506237.6 100131.27l-20190.42 -9567.46c-104.59,-49.55 -224.6,-25.58 -302.07,60.41l-3952.69 4390.01c-58.28,64.79 -79.4,146.83 -59.3,231.61 20,84.99 75.34,148.96 156.47,180.84l21267.24 8371.65c102.05,40.11 212.72,14.11 286.13,-67.43l2875.88 -3194.1c56.25,-62.55 77.68,-140.63 61.23,-223.18 -16.44,-82.45 -66.41,-146.42 -142.46,-182.36z"/>
<path class="fil3" d="M500939.21 105976.44l-21940.13 -7623.92c-100.11,-34.83 -205.41,-7.82 -276.28,70.98l-4476.23 4971.22c-61.73,68.53 -81.84,157.08 -55.74,245.41 26.09,88.44 90.88,151.91 179.82,176.07l23151.69 6278.53c96.16,26.1 192.41,-2.23 259.13,-76.35l3264.76 -3625.84c59.61,-66.2 80.32,-150.47 58.18,-236.78 -22.23,-86.21 -81.13,-150.08 -165.21,-179.32z"/>
<path class="fil3" d="M495640.02 111827.39l-23689.94 -5680.77c-94.44,-22.54 -186.93,6.09 -251.81,78.28l-4995.4 5547.97c-64.68,71.88 -83.67,166.42 -51.79,257.6 31.89,91.29 105.61,153.42 200.95,169.37l25036.04 4185.62c89.46,15.03 173.43,-14.32 234.15,-81.74l3649.29 -4053.11c62.65,-69.45 82.45,-159.72 54.73,-248.97 -27.81,-89.35 -95.24,-152.52 -186.22,-174.24z"/>
<path class="fil3" d="M490341.04 117683.01l-25439.87 -3737.32c-88.03,-12.9 -169.66,16.44 -229.17,82.55l-5511 6120.74c-67.31,74.83 -85.19,174.85 -47.83,268.27 37.37,93.52 119.11,153.63 219.53,161.44l26920.5 2092.82c82.55,6.49 156.26,-23.06 211.6,-84.58l4030.47 -4476.23c65.29,-72.49 84.08,-168.25 50.97,-260.04 -33.09,-91.68 -108.64,-153.52 -205.2,-167.64z"/>
<path class="fil3" d="M485042.76 123542.4l-27189.77 -1793.88c-81.54,-5.39 -153.93,24.07 -208.56,84.79l-6023.87 6690.16c-69.66,77.37 -86.31,182.27 -43.97,277.51 42.44,95.04 131.49,152.92 235.57,152.92l28804.95 0c75.74,0 140.93,-29.04 191.71,-85.4l4408.59 -4896.18c67.73,-75.24 85.4,-175.86 47.32,-269.59 -38.08,-93.71 -121.04,-153.63 -221.97,-160.33z"/>
<path class="fil4" d="M455586.23 0l23946.23 0c606.08,0 1127.69,232.22 1533.33,682.65l112395.09 124827.36c557.66,619.39 690.56,1458.5 351.63,2219.83 -338.93,761.34 -1051.53,1224.05 -1884.86,1224.05l-23946.32 0c-606.08,0 -1127.79,-232.32 -1533.33,-682.74l-112394.99 -124827.37c-557.65,-619.28 -690.56,-1458.4 -351.62,-2219.83 338.93,-761.23 1051.53,-1223.95 1884.85,-1223.95z"/>
<path class="fil3" d="M546063.84 55728.5l-17150.76 -12698.51c-107.23,-79.4 -255.89,-64.37 -345.13,34.72l-3542.88 3934.72c-91.29,101.44 -87.94,255.88 7.72,353.25l14841.57 15109.54c51.28,52.29 114.74,78.28 188.06,77.17 73.2,-1.11 135.86,-29.14 185.51,-83.06l5852.07 -6345.75c51.38,-55.75 74.22,-123.57 67.11,-199.12 -7.2,-75.55 -42.34,-137.79 -103.26,-182.97z"/>
<path class="fil3" d="M553805.64 48026.39l-20076.6 -10405.36c-105.7,-54.73 -230.69,-31.98 -310.3,56.46l-3337.36 3706.35c-51.68,57.47 -73.62,127.13 -64.38,203.79 9.35,76.66 47.42,139.01 111.4,182.27l17863.05 12103.08c102.35,69.36 235.87,57.58 324.52,-28.73l5550.91 -5404.09c61.23,-59.6 87.73,-137.89 75.24,-222.47 -12.49,-84.57 -60.52,-151.9 -136.46,-191.3z"/>
<path class="fil3" d="M561723.31 40809.94l-23179.82 -8581.22c-101.13,-37.46 -208.97,-10.76 -281.16,69.36l-3126.37 3472.11c-56.35,62.65 -77.77,140.83 -61.12,223.48 16.55,82.55 66.61,146.42 142.86,182.36l20967.6 9879.29c95.64,45.08 202.87,29.55 281.77,-40.91l5338.48 -4770.28c69.36,-62.04 98.9,-148.96 81.54,-240.34 -17.27,-91.38 -76.57,-161.45 -163.78,-193.84z"/>
<path class="fil3" d="M569965.29 33882.39l-26606.23 -7035.4c-95.86,-25.38 -191.31,3.05 -257.61,76.76l-2913.54 3235.73c-60.01,66.6 -80.62,151.69 -57.78,238.41 22.95,86.71 82.96,150.48 167.95,178.81l24313.19 8089.46c86.71,28.84 174.94,12.39 245.41,-45.69l5206.69 -4289.8c76.56,-63.05 109.05,-157.18 87.93,-254.14 -21.22,-96.87 -90.16,-168.86 -186.01,-194.14z"/>
<path class="fil3" d="M578696.89 27121.04l-30520.74 -5649.4c-90.78,-16.86 -176.78,12.39 -238.52,81.02l-2700.82 2999.45c-62.74,69.66 -82.45,160.23 -54.32,249.79 28.02,89.45 96.06,152.61 187.33,173.83l28072.15 6551.78c77.27,18.07 151.08,2.33 214.34,-45.59l5149.43 -3901.82c83.26,-63.05 119.2,-163.37 95.14,-265.02 -24.06,-101.53 -101.23,-175.05 -203.98,-194.04z"/>
<path class="fil3" d="M588128.6 20435.94l-35133.75 -4335.48c-86.1,-10.56 -165.1,18.88 -223.18,83.36l-2488.81 2764.08c-64.88,72.19 -83.87,167.13 -51.48,258.62 32.39,91.49 106.82,153.42 202.67,168.66l32447.23 5148.1c68.03,10.76 130.38,-3.45 187.03,-42.54l5175.31 -3576.7c89.76,-62.03 129.87,-167.64 103.87,-273.64 -26.1,-106.01 -110.58,-181.14 -218.91,-194.45z"/>
<path class="fil3" d="M598567.77 13749.33l-40752.78 -3017.31c-82.24,-6.1 -155.55,23.35 -210.69,84.68l-2278.02 2529.94c-66.71,74.12 -84.88,172.61 -48.94,265.52 35.84,93.01 115.66,153.72 214.75,163.68l37715.35 3786.47c59,5.89 111.4,-6.29 161.76,-37.56l5315.54 -3299.09c96.46,-59.91 141.54,-170.28 114.43,-280.64 -27.11,-110.27 -118.09,-187.24 -231.41,-195.67z"/>
<path class="fil3" d="M610537.63 6977.53l-47901.39 -1612.13c-78.8,-2.64 -147.64,26.61 -200.34,85.19l-2068.74 2297.51c-68.13,75.54 -85.49,177.08 -46.61,271.1 38.89,93.92 122.87,153.53 224.5,158.91l44316.99 2379.55c49.76,2.74 93.01,-7 136.77,-30.76l5653.05 -3065.04c103.77,-56.25 155.16,-170.99 128.05,-285.83 -27.11,-114.94 -124.29,-194.54 -242.28,-198.51z"/>
<path class="fil3" d="M625135.21 0l-57676.8 0c-75.75,0 -140.94,29.04 -191.6,85.3l-1860.69 2066.41c-69.25,76.97 -86.11,180.84 -44.68,275.68 41.33,94.84 128.95,153.11 232.32,154.74l53096.1 819.1c39.4,0.61 73.41,-6.4 109.35,-22.54l6441.4 -2885.51c112.2,-50.26 172.41,-168.87 146.72,-289.19 -25.69,-120.32 -129.15,-203.99 -252.12,-203.99z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

54
imgs/evox_brand_light.svg Normal file
View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="6062.18mm" height="1250mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 625393.22 128953.89"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<style type="text/css">
<![CDATA[
.fil3 {fill:#FEFEFE}
.fil4 {fill:#C8383C}
.fil2 {fill:url(#id0)}
.fil0 {fill:url(#id1)}
.fil1 {fill:url(#id2)}
]]>
</style>
<linearGradient id="id0" gradientUnits="userSpaceOnUse" x1="359566.17" y1="126101.12" x2="359566.17" y2="2853.5">
<stop offset="0" style="stop-opacity:0.917647; stop-color:#FEFEFE"/>
<stop offset="0.309804" style="stop-opacity:0.835294; stop-color:#FEFEFE"/>
<stop offset="0.560784" style="stop-opacity:0.74902; stop-color:#FEFEFE"/>
<stop offset="0.788235" style="stop-opacity:0.87451; stop-color:#FEFEFE"/>
<stop offset="1" style="stop-opacity:1; stop-color:#FEFEFE"/>
</linearGradient>
<linearGradient id="id1" gradientUnits="userSpaceOnUse" xlink:href="#id0" x1="56326.49" y1="126101.12" x2="56326.49" y2="2853.5">
</linearGradient>
<linearGradient id="id2" gradientUnits="userSpaceOnUse" xlink:href="#id0" x1="199869.19" y1="126101.12" x2="199869.19" y2="2853.5">
</linearGradient>
</defs>
<g id="圖層_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<path class="fil0" d="M111173.52 4085.98l-110434.04 0c-205.57,0 -390.44,81.84 -521.58,212.97l-4.93 4.93c-131.13,131.13 -212.97,316.01 -212.97,521.58l0 23170.56c0,202.12 83.81,387.48 217.9,521.58 131.14,136.06 316.01,217.9 521.58,217.9l111173.52 0c202.13,0 387.49,-83.81 521.58,-217.9 134.09,-134.1 217.9,-319.47 217.9,-521.58l0 -22431.08c0,-814.42 -664.55,-1478.97 -1478.97,-1478.97zm1478.97 73209.08l0 -24896.01c0,-814.42 -664.55,-1478.98 -1478.97,-1478.98l-110434.04 0c-205.57,0 -390.44,81.84 -521.58,212.97l-4.93 4.93c-131.13,131.14 -212.97,316.01 -212.97,521.58l0 72469.6c0,202.13 83.81,387.49 217.9,521.59 131.14,136.06 316.01,217.9 521.58,217.9l111173.52 0c202.13,0 387.49,-83.81 521.58,-217.9 134.09,-134.1 217.9,-319.47 217.9,-521.59l0 -22431.06c0,-814.42 -664.55,-1478.97 -1478.97,-1478.97l-80854.68 0c-879,0 -1680.1,-360.38 -2262.33,-942.11 -582.22,-577.29 -942.11,-1379.38 -942.11,-2262.34l0 -15775.69c0,-878.02 359.39,-1678.14 939.64,-2259.87l4.93 -4.93c581.73,-580.25 1381.85,-939.64 2259.86,-939.64l81594.16 0c202.13,0 387.49,-83.81 521.58,-217.9 134.09,-134.09 217.9,-319.45 217.9,-521.58z"/>
<path class="fil1" d="M160583.37 4085.98l-25473.74 0c-135.08,0 -254.89,27.61 -352,77.89 -99.09,55.22 -188.81,138.04 -262.26,246.99 -76.42,109.45 -119.8,223.81 -131.14,333.26 -11.83,113.39 7.4,237.13 57.68,362.35l48201.05 119303.69c58.17,144.45 152.33,260.8 269.17,339.18 113.39,76.41 257.83,119.31 418.54,119.31l33473.97 0c161.2,0 305.16,-42.9 418.54,-119.31 116.84,-78.38 211,-194.73 269.17,-339.18l47792.35 -118291.1c189.8,-469.82 138.53,-960.83 -144.94,-1380.86 -283.46,-420.52 -719.26,-652.23 -1226.06,-652.23l-24381.27 0c-160.72,0 -305.17,42.88 -418.55,119.3 -116.83,78.39 -211.49,195.23 -269.17,339.18l-35807.78 88627.86c-247.97,616.23 -658.63,1117.61 -1174.3,1465.16 -517.64,349.04 -1135.36,543.28 -1794.98,543.28 -660.11,0 -1277.33,-194.24 -1794.98,-543.28 -515.66,-347.56 -926.32,-848.93 -1174.3,-1465.16l-35807.3 -88627.86c-57.68,-143.95 -152.33,-260.79 -269.17,-339.18 -113.39,-76.41 -257.34,-119.3 -418.54,-119.3z"/>
<path class="fil2" d="M396465.92 4085.98l-73800 0c-8492.23,0 -16213.92,3475.59 -21812.31,9073.48 -5598.38,5598.9 -9073.46,13320.11 -9073.46,21812.36l0 59010.96c0,8492.25 3475.58,16213.97 9073.46,21812.36 5598.39,5597.91 13320.08,9073.49 21812.31,9073.49l73800 0c8492.23,0 16214.41,-3475.59 21812.31,-9073.99 5598.38,-5597.9 9073.96,-13319.62 9073.96,-21811.87l0 -59010.96c0,-8492.25 -3475.58,-16213.96 -9073.96,-21811.87 -5597.9,-5598.39 -13320.08,-9073.98 -21812.31,-9073.98zm-68834.62 24649.53l63869.72 0c3081.68,0 5883.34,1260.09 7912.49,3289.23 2029.14,2029.15 3289.22,4830.81 3289.22,7912.5l0 49079.66c0,3081.68 -1260.09,5883.35 -3289.22,7912.5 -2029.15,2029.15 -4830.81,3289.23 -7912.49,3289.23l-63869.72 0c-3081.67,0 -5883.33,-1260.09 -7912.48,-3289.23 -2029.15,-2029.15 -3289.22,-4830.82 -3289.22,-7912.5l0 -49079.66c0,-3081.69 1260.08,-5883.35 3289.22,-7912.5 2029.15,-2029.15 4830.81,-3289.23 7912.48,-3289.23z"/>
<path class="fil3" d="M522115.89 82633.04l-14940.89 -15397.6c-51.58,-53.21 -115.75,-79.7 -189.87,-78.29 -74.03,1.42 -137.28,30.26 -186.84,85.3l-2352.65 2612.89c-93.82,104.17 -87.02,264.71 15.13,360.56l15613.97 14650.18c52.5,49.24 115.35,72.6 187.24,69.55 71.88,-3.04 132.61,-31.58 180.83,-84.99l1679.65 -1865.47c90.98,-101.03 88.04,-254.55 -6.59,-352.14z"/>
<path class="fil3" d="M516827.35 88459.62l-16690.8 -13454.25c-106.71,-86 -261.77,-73.62 -353.45,28.23l-2891.52 3211.16c-50.87,56.55 -72.91,124.69 -64.68,200.33 8.13,75.55 44.28,137.49 106.01,181.75l17498.33 12557.38c107.23,76.96 253.65,61.12 341.99,-36.96l2083.87 -2314.37c49.24,-54.52 71.28,-119.71 65.49,-193.02 -5.89,-73.32 -38.08,-134.13 -95.24,-180.24z"/>
<path class="fil3" d="M511534.04 94292.2l-18440.5 -11510.81c-107.02,-66.81 -243.69,-47.52 -328.17,46.2l-3424.7 3803.43c-54.73,60.82 -76.45,135.97 -62.44,216.49 13.91,80.62 59.61,144.18 131.59,182.97l19382.78 10464.45c106.01,57.27 233.44,35.23 314.06,-54.33l2482.52 -2757.17c52.8,-58.6 74.73,-130.27 63.66,-208.46 -10.97,-78.19 -51.78,-140.94 -118.8,-182.77z"/>
<path class="fil3" d="M506237.6 100131.27l-20190.42 -9567.46c-104.59,-49.55 -224.6,-25.58 -302.07,60.41l-3952.69 4390.01c-58.28,64.79 -79.4,146.83 -59.3,231.61 20,84.99 75.34,148.96 156.47,180.84l21267.24 8371.65c102.05,40.11 212.72,14.11 286.13,-67.43l2875.88 -3194.1c56.25,-62.55 77.68,-140.63 61.23,-223.18 -16.44,-82.45 -66.41,-146.42 -142.46,-182.36z"/>
<path class="fil3" d="M500939.21 105976.44l-21940.13 -7623.92c-100.11,-34.83 -205.41,-7.82 -276.28,70.98l-4476.23 4971.22c-61.73,68.53 -81.84,157.08 -55.74,245.41 26.09,88.44 90.88,151.91 179.82,176.07l23151.69 6278.53c96.16,26.1 192.41,-2.23 259.13,-76.35l3264.76 -3625.84c59.61,-66.2 80.32,-150.47 58.18,-236.78 -22.23,-86.21 -81.13,-150.08 -165.21,-179.32z"/>
<path class="fil3" d="M495640.02 111827.39l-23689.94 -5680.77c-94.44,-22.54 -186.93,6.09 -251.81,78.28l-4995.4 5547.97c-64.68,71.88 -83.67,166.42 -51.79,257.6 31.89,91.29 105.61,153.42 200.95,169.37l25036.04 4185.62c89.46,15.03 173.43,-14.32 234.15,-81.74l3649.29 -4053.11c62.65,-69.45 82.45,-159.72 54.73,-248.97 -27.81,-89.35 -95.24,-152.52 -186.22,-174.24z"/>
<path class="fil3" d="M490341.04 117683.01l-25439.87 -3737.32c-88.03,-12.9 -169.66,16.44 -229.17,82.55l-5511 6120.74c-67.31,74.83 -85.19,174.85 -47.83,268.27 37.37,93.52 119.11,153.63 219.53,161.44l26920.5 2092.82c82.55,6.49 156.26,-23.06 211.6,-84.58l4030.47 -4476.23c65.29,-72.49 84.08,-168.25 50.97,-260.04 -33.09,-91.68 -108.64,-153.52 -205.2,-167.64z"/>
<path class="fil3" d="M485042.76 123542.4l-27189.77 -1793.88c-81.54,-5.39 -153.93,24.07 -208.56,84.79l-6023.87 6690.16c-69.66,77.37 -86.31,182.27 -43.97,277.51 42.44,95.04 131.49,152.92 235.57,152.92l28804.95 0c75.74,0 140.93,-29.04 191.71,-85.4l4408.59 -4896.18c67.73,-75.24 85.4,-175.86 47.32,-269.59 -38.08,-93.71 -121.04,-153.63 -221.97,-160.33z"/>
<path class="fil4" d="M455586.23 0l23946.23 0c606.08,0 1127.69,232.22 1533.33,682.65l112395.09 124827.36c557.66,619.39 690.56,1458.5 351.63,2219.83 -338.93,761.34 -1051.53,1224.05 -1884.86,1224.05l-23946.32 0c-606.08,0 -1127.79,-232.32 -1533.33,-682.74l-112394.99 -124827.37c-557.65,-619.28 -690.56,-1458.4 -351.62,-2219.83 338.93,-761.23 1051.53,-1223.95 1884.85,-1223.95z"/>
<path class="fil3" d="M546063.84 55728.5l-17150.76 -12698.51c-107.23,-79.4 -255.89,-64.37 -345.13,34.72l-3542.88 3934.72c-91.29,101.44 -87.94,255.88 7.72,353.25l14841.57 15109.54c51.28,52.29 114.74,78.28 188.06,77.17 73.2,-1.11 135.86,-29.14 185.51,-83.06l5852.07 -6345.75c51.38,-55.75 74.22,-123.57 67.11,-199.12 -7.2,-75.55 -42.34,-137.79 -103.26,-182.97z"/>
<path class="fil3" d="M553805.64 48026.39l-20076.6 -10405.36c-105.7,-54.73 -230.69,-31.98 -310.3,56.46l-3337.36 3706.35c-51.68,57.47 -73.62,127.13 -64.38,203.79 9.35,76.66 47.42,139.01 111.4,182.27l17863.05 12103.08c102.35,69.36 235.87,57.58 324.52,-28.73l5550.91 -5404.09c61.23,-59.6 87.73,-137.89 75.24,-222.47 -12.49,-84.57 -60.52,-151.9 -136.46,-191.3z"/>
<path class="fil3" d="M561723.31 40809.94l-23179.82 -8581.22c-101.13,-37.46 -208.97,-10.76 -281.16,69.36l-3126.37 3472.11c-56.35,62.65 -77.77,140.83 -61.12,223.48 16.55,82.55 66.61,146.42 142.86,182.36l20967.6 9879.29c95.64,45.08 202.87,29.55 281.77,-40.91l5338.48 -4770.28c69.36,-62.04 98.9,-148.96 81.54,-240.34 -17.27,-91.38 -76.57,-161.45 -163.78,-193.84z"/>
<path class="fil3" d="M569965.29 33882.39l-26606.23 -7035.4c-95.86,-25.38 -191.31,3.05 -257.61,76.76l-2913.54 3235.73c-60.01,66.6 -80.62,151.69 -57.78,238.41 22.95,86.71 82.96,150.48 167.95,178.81l24313.19 8089.46c86.71,28.84 174.94,12.39 245.41,-45.69l5206.69 -4289.8c76.56,-63.05 109.05,-157.18 87.93,-254.14 -21.22,-96.87 -90.16,-168.86 -186.01,-194.14z"/>
<path class="fil3" d="M578696.89 27121.04l-30520.74 -5649.4c-90.78,-16.86 -176.78,12.39 -238.52,81.02l-2700.82 2999.45c-62.74,69.66 -82.45,160.23 -54.32,249.79 28.02,89.45 96.06,152.61 187.33,173.83l28072.15 6551.78c77.27,18.07 151.08,2.33 214.34,-45.59l5149.43 -3901.82c83.26,-63.05 119.2,-163.37 95.14,-265.02 -24.06,-101.53 -101.23,-175.05 -203.98,-194.04z"/>
<path class="fil3" d="M588128.6 20435.94l-35133.75 -4335.48c-86.1,-10.56 -165.1,18.88 -223.18,83.36l-2488.81 2764.08c-64.88,72.19 -83.87,167.13 -51.48,258.62 32.39,91.49 106.82,153.42 202.67,168.66l32447.23 5148.1c68.03,10.76 130.38,-3.45 187.03,-42.54l5175.31 -3576.7c89.76,-62.03 129.87,-167.64 103.87,-273.64 -26.1,-106.01 -110.58,-181.14 -218.91,-194.45z"/>
<path class="fil3" d="M598567.77 13749.33l-40752.78 -3017.31c-82.24,-6.1 -155.55,23.35 -210.69,84.68l-2278.02 2529.94c-66.71,74.12 -84.88,172.61 -48.94,265.52 35.84,93.01 115.66,153.72 214.75,163.68l37715.35 3786.47c59,5.89 111.4,-6.29 161.76,-37.56l5315.54 -3299.09c96.46,-59.91 141.54,-170.28 114.43,-280.64 -27.11,-110.27 -118.09,-187.24 -231.41,-195.67z"/>
<path class="fil3" d="M610537.63 6977.53l-47901.39 -1612.13c-78.8,-2.64 -147.64,26.61 -200.34,85.19l-2068.74 2297.51c-68.13,75.54 -85.49,177.08 -46.61,271.1 38.89,93.92 122.87,153.53 224.5,158.91l44316.99 2379.55c49.76,2.74 93.01,-7 136.77,-30.76l5653.05 -3065.04c103.77,-56.25 155.16,-170.99 128.05,-285.83 -27.11,-114.94 -124.29,-194.54 -242.28,-198.51z"/>
<path class="fil3" d="M625135.21 0l-57676.8 0c-75.75,0 -140.94,29.04 -191.6,85.3l-1860.69 2066.41c-69.25,76.97 -86.11,180.84 -44.68,275.68 41.33,94.84 128.95,153.11 232.32,154.74l53096.1 819.1c39.4,0.61 73.41,-6.4 109.35,-22.54l6441.4 -2885.51c112.2,-50.26 172.41,-168.87 146.72,-289.19 -25.69,-120.32 -129.15,-203.99 -252.12,-203.99z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

37
imgs/evox_logo_dark.svg Normal file
View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1685.1mm" height="1250mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 98668.67 73192.18"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<style type="text/css">
<![CDATA[
.fil0 {fill:#373435}
.fil1 {fill:#C8383C}
]]>
</style>
</defs>
<g id="圖層_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<path class="fil0" d="M40050.09 46901.21l-8480.21 -8739.43c-29.28,-30.2 -65.7,-45.24 -107.77,-44.44 -42.02,0.81 -77.92,17.17 -106.05,48.41l-1335.32 1483.04c-53.25,59.13 -49.39,150.24 8.59,204.65l8862.24 8315.21c29.8,27.95 65.47,41.2 106.28,39.48 40.8,-1.73 75.26,-17.92 102.64,-48.24l953.34 -1058.81c51.64,-57.34 49.97,-144.48 -3.74,-199.87z"/>
<path class="fil0" d="M37048.4 50208.28l-9473.43 -7636.42c-60.57,-48.81 -148.57,-41.78 -200.61,16.02l-1641.18 1822.6c-28.87,32.1 -41.38,70.77 -36.71,113.71 4.61,42.88 25.13,78.03 60.17,103.16l9931.78 7127.37c60.86,43.68 143.97,34.69 194.11,-20.98l1182.77 -1313.6c27.95,-30.95 40.45,-67.95 37.17,-109.55 -3.34,-41.61 -21.61,-76.13 -54.06,-102.3z"/>
<path class="fil0" d="M34044 53518.76l-10466.54 -6533.35c-60.74,-37.92 -138.32,-26.97 -186.27,26.22l-1943.8 2158.77c-31.06,34.52 -43.39,77.17 -35.44,122.88 7.89,45.76 33.83,81.83 74.69,103.85l11001.36 5939.46c60.17,32.5 132.5,20 178.26,-30.83l1409.04 -1564.93c29.97,-33.26 42.42,-73.94 36.13,-118.32 -6.22,-44.38 -29.39,-80 -67.43,-103.74z"/>
<path class="fil0" d="M31037.83 56832.92l-11459.76 -5430.34c-59.36,-28.12 -127.48,-14.52 -171.45,34.29l-2243.49 2491.7c-33.08,36.77 -45.07,83.34 -33.66,131.46 11.35,48.24 42.76,84.55 88.81,102.64l12070.95 4751.61c57.92,22.77 120.74,8.01 162.4,-38.27l1632.3 -1812.92c31.93,-35.5 44.09,-79.82 34.75,-126.68 -9.33,-46.8 -37.69,-83.11 -80.86,-103.51z"/>
<path class="fil0" d="M28030.55 60150.55l-12452.87 -4327.22c-56.82,-19.77 -116.59,-4.44 -156.81,40.28l-2540.64 2821.59c-35.04,38.9 -46.45,89.15 -31.64,139.29 14.81,50.2 51.58,86.22 102.07,99.93l13140.53 3563.59c54.58,14.81 109.21,-1.26 147.08,-43.34l1853.03 -2057.97c33.83,-37.57 45.59,-85.41 33.02,-134.39 -12.62,-48.93 -46.05,-85.18 -93.77,-101.78z"/>
<path class="fil0" d="M25022.81 63471.45l-13446.04 -3224.31c-53.6,-12.79 -106.1,3.45 -142.92,44.43l-2835.31 3148.94c-36.71,40.8 -47.49,94.46 -29.39,146.21 18.1,51.81 59.94,87.08 114.06,96.13l14210.06 2375.69c50.78,8.53 98.43,-8.13 132.9,-46.39l2071.28 -2300.48c35.56,-39.42 46.8,-90.65 31.06,-141.31 -15.79,-50.71 -54.06,-86.57 -105.7,-98.9z"/>
<path class="fil0" d="M22015.19 66795.01l-14439.27 -2121.24c-49.96,-7.32 -96.3,9.33 -130.07,46.85l-3127.96 3474.03c-38.21,42.47 -48.35,99.24 -27.15,152.26 21.21,53.08 67.61,87.2 124.6,91.63l15279.65 1187.85c46.85,3.68 88.69,-13.09 120.1,-48.01l2287.63 -2540.64c37.06,-41.15 47.72,-95.5 28.93,-147.6 -18.78,-52.04 -61.66,-87.13 -116.47,-95.15z"/>
<path class="fil0" d="M19007.97 70120.71l-15432.48 -1018.18c-46.28,-3.06 -87.37,13.66 -118.38,48.13l-3419.05 3797.23c-39.54,43.92 -48.99,103.45 -24.96,157.51 24.09,53.95 74.63,86.79 133.71,86.79l16349.23 0c42.99,0 79.99,-16.48 108.81,-48.47l2502.25 -2779c38.44,-42.7 48.47,-99.82 26.86,-153.01 -21.61,-53.19 -68.7,-87.2 -125.98,-91z"/>
<path class="fil1" d="M2288.91 0l13591.5 0c344,0 640.06,131.8 870.3,387.46l63793.67 70850.03c316.52,351.56 391.95,827.82 199.58,1259.94 -192.37,432.13 -596.83,694.75 -1069.82,694.75l-13591.55 0c-344,0 -640.12,-131.86 -870.3,-387.51l-63793.62 -70850.03c-316.51,-351.49 -391.95,-827.76 -199.57,-1259.94 192.37,-432.06 596.83,-694.69 1069.81,-694.69z"/>
<path class="fil0" d="M53642.57 31630.61l-9734.5 -7207.47c-60.86,-45.07 -145.24,-36.54 -195.89,19.71l-2010.88 2233.29c-51.81,57.58 -49.91,145.23 4.38,200.5l8423.84 8575.93c29.11,29.68 65.12,44.43 106.74,43.8 41.55,-0.63 77.11,-16.54 105.29,-47.14l3321.54 -3601.75c29.16,-31.64 42.12,-70.14 38.09,-113.01 -4.09,-42.88 -24.03,-78.21 -58.61,-103.85z"/>
<path class="fil0" d="M58036.69 27259.02l-11395.16 -5905.92c-59.99,-31.06 -130.94,-18.15 -176.12,32.05l-1894.23 2103.67c-29.34,32.62 -41.78,72.16 -36.54,115.67 5.3,43.51 26.92,78.9 63.23,103.45l10138.79 6869.51c58.09,39.37 133.88,32.68 184.19,-16.31l3150.61 -3067.27c34.75,-33.83 49.79,-78.26 42.7,-126.27 -7.09,-48 -34.35,-86.21 -77.45,-108.58z"/>
<path class="fil0" d="M62530.64 23163.08l-13156.5 -4870.57c-57.4,-21.26 -118.61,-6.11 -159.58,39.37l-1774.48 1970.71c-31.98,35.56 -44.14,79.93 -34.69,126.84 9.39,46.85 37.81,83.11 81.09,103.51l11900.88 5607.33c54.29,25.59 115.15,16.77 159.93,-23.22l3030.04 -2707.54c39.37,-35.21 56.14,-84.55 46.28,-136.41 -9.8,-51.87 -43.46,-91.64 -92.96,-110.02z"/>
<path class="fil0" d="M67208.65 19231.11l-15101.27 -3993.18c-54.41,-14.4 -108.58,1.73 -146.21,43.57l-1653.68 1836.55c-34.06,37.8 -45.76,86.1 -32.8,135.32 13.03,49.21 47.09,85.41 95.33,101.49l13799.78 4591.45c49.21,16.37 99.3,7.03 139.29,-25.93l2955.24 -2434.82c43.45,-35.79 61.9,-89.21 49.91,-144.25 -12.04,-54.98 -51.18,-95.84 -105.58,-110.19z"/>
<path class="fil0" d="M72164.57 15393.47l-17323.09 -3206.51c-51.53,-9.57 -100.34,7.03 -135.38,45.99l-1532.94 1702.44c-35.61,39.54 -46.8,90.95 -30.83,141.78 15.9,50.77 54.52,86.62 106.33,98.66l15933.31 3718.68c43.86,10.26 85.75,1.32 121.66,-25.87l2922.73 -2214.61c47.26,-35.79 67.66,-92.73 54,-150.42 -13.65,-57.63 -57.46,-99.35 -115.78,-110.13z"/>
<path class="fil0" d="M77517.86 11599.12l-19941.36 -2460.75c-48.87,-6 -93.71,10.72 -126.68,47.31l-1412.61 1568.85c-36.82,40.98 -47.6,94.86 -29.22,146.79 18.39,51.93 60.63,87.08 115.03,95.73l18416.54 2921.98c38.61,6.11 74,-1.96 106.16,-24.15l2937.42 -2030.08c50.95,-35.21 73.71,-95.15 58.96,-155.31 -14.81,-60.17 -62.76,-102.81 -124.25,-110.37z"/>
<path class="fil0" d="M83442.97 7803.9l-23130.63 -1712.58c-46.68,-3.46 -88.29,13.25 -119.58,48.06l-1292.97 1435.95c-37.86,42.07 -48.18,97.97 -27.78,150.71 20.34,52.79 65.64,87.25 121.89,92.9l21406.64 2149.14c33.49,3.34 63.23,-3.57 91.81,-21.32l3017.02 -1872.51c54.75,-34 80.34,-96.65 64.95,-159.29 -15.39,-62.59 -67.03,-106.28 -131.34,-111.06z"/>
<path class="fil0" d="M90236.87 3960.34l-27188.07 -915.02c-44.72,-1.5 -83.8,15.1 -113.71,48.35l-1174.18 1304.03c-38.67,42.87 -48.52,100.51 -26.45,153.87 22.07,53.31 69.74,87.14 127.42,90.2l25153.62 1350.59c28.24,1.56 52.79,-3.98 77.63,-17.46l3208.58 -1739.67c58.9,-31.93 88.06,-97.05 72.68,-162.23 -15.39,-65.24 -70.55,-110.42 -137.51,-112.67z"/>
<path class="fil0" d="M98522.23 0l-32736.44 0c-43,0 -80,16.48 -108.75,48.41l-1056.1 1172.86c-39.31,43.69 -48.87,102.64 -25.36,156.47 23.46,53.83 73.19,86.91 131.86,87.83l30136.51 464.91c22.36,0.35 41.67,-3.63 62.07,-12.79l3656.04 -1637.77c63.68,-28.53 97.86,-95.85 83.28,-164.14 -14.58,-68.29 -73.3,-115.78 -143.1,-115.78z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

37
imgs/evox_logo_light.svg Normal file
View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1685.1mm" height="1250mm" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 98668.67 73192.18"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<style type="text/css">
<![CDATA[
.fil0 {fill:#FEFEFE}
.fil1 {fill:#C8383C}
]]>
</style>
</defs>
<g id="圖層_x0020_1">
<metadata id="CorelCorpID_0Corel-Layer"/>
<path class="fil0" d="M40050.09 46901.21l-8480.21 -8739.43c-29.28,-30.2 -65.7,-45.24 -107.77,-44.44 -42.02,0.81 -77.92,17.17 -106.05,48.41l-1335.32 1483.04c-53.25,59.13 -49.39,150.24 8.59,204.65l8862.24 8315.21c29.8,27.95 65.47,41.2 106.28,39.48 40.8,-1.73 75.26,-17.92 102.64,-48.24l953.34 -1058.81c51.64,-57.34 49.97,-144.48 -3.74,-199.87z"/>
<path class="fil0" d="M37048.4 50208.28l-9473.43 -7636.42c-60.57,-48.81 -148.57,-41.78 -200.61,16.02l-1641.18 1822.6c-28.87,32.1 -41.38,70.77 -36.71,113.71 4.61,42.88 25.13,78.03 60.17,103.16l9931.78 7127.37c60.86,43.68 143.97,34.69 194.11,-20.98l1182.77 -1313.6c27.95,-30.95 40.45,-67.95 37.17,-109.55 -3.34,-41.61 -21.61,-76.13 -54.06,-102.3z"/>
<path class="fil0" d="M34044 53518.76l-10466.54 -6533.35c-60.74,-37.92 -138.32,-26.97 -186.27,26.22l-1943.8 2158.77c-31.06,34.52 -43.39,77.17 -35.44,122.88 7.89,45.76 33.83,81.83 74.69,103.85l11001.36 5939.46c60.17,32.5 132.5,20 178.26,-30.83l1409.04 -1564.93c29.97,-33.26 42.42,-73.94 36.13,-118.32 -6.22,-44.38 -29.39,-80 -67.43,-103.74z"/>
<path class="fil0" d="M31037.83 56832.92l-11459.76 -5430.34c-59.36,-28.12 -127.48,-14.52 -171.45,34.29l-2243.49 2491.7c-33.08,36.77 -45.07,83.34 -33.66,131.46 11.35,48.24 42.76,84.55 88.81,102.64l12070.95 4751.61c57.92,22.77 120.74,8.01 162.4,-38.27l1632.3 -1812.92c31.93,-35.5 44.09,-79.82 34.75,-126.68 -9.33,-46.8 -37.69,-83.11 -80.86,-103.51z"/>
<path class="fil0" d="M28030.55 60150.55l-12452.87 -4327.22c-56.82,-19.77 -116.59,-4.44 -156.81,40.28l-2540.64 2821.59c-35.04,38.9 -46.45,89.15 -31.64,139.29 14.81,50.2 51.58,86.22 102.07,99.93l13140.53 3563.59c54.58,14.81 109.21,-1.26 147.08,-43.34l1853.03 -2057.97c33.83,-37.57 45.59,-85.41 33.02,-134.39 -12.62,-48.93 -46.05,-85.18 -93.77,-101.78z"/>
<path class="fil0" d="M25022.81 63471.45l-13446.04 -3224.31c-53.6,-12.79 -106.1,3.45 -142.92,44.43l-2835.31 3148.94c-36.71,40.8 -47.49,94.46 -29.39,146.21 18.1,51.81 59.94,87.08 114.06,96.13l14210.06 2375.69c50.78,8.53 98.43,-8.13 132.9,-46.39l2071.28 -2300.48c35.56,-39.42 46.8,-90.65 31.06,-141.31 -15.79,-50.71 -54.06,-86.57 -105.7,-98.9z"/>
<path class="fil0" d="M22015.19 66795.01l-14439.27 -2121.24c-49.96,-7.32 -96.3,9.33 -130.07,46.85l-3127.96 3474.03c-38.21,42.47 -48.35,99.24 -27.15,152.26 21.21,53.08 67.61,87.2 124.6,91.63l15279.65 1187.85c46.85,3.68 88.69,-13.09 120.1,-48.01l2287.63 -2540.64c37.06,-41.15 47.72,-95.5 28.93,-147.6 -18.78,-52.04 -61.66,-87.13 -116.47,-95.15z"/>
<path class="fil0" d="M19007.97 70120.71l-15432.48 -1018.18c-46.28,-3.06 -87.37,13.66 -118.38,48.13l-3419.05 3797.23c-39.54,43.92 -48.99,103.45 -24.96,157.51 24.09,53.95 74.63,86.79 133.71,86.79l16349.23 0c42.99,0 79.99,-16.48 108.81,-48.47l2502.25 -2779c38.44,-42.7 48.47,-99.82 26.86,-153.01 -21.61,-53.19 -68.7,-87.2 -125.98,-91z"/>
<path class="fil1" d="M2288.91 0l13591.5 0c344,0 640.06,131.8 870.3,387.46l63793.67 70850.03c316.52,351.56 391.95,827.82 199.58,1259.94 -192.37,432.13 -596.83,694.75 -1069.82,694.75l-13591.55 0c-344,0 -640.12,-131.86 -870.3,-387.51l-63793.62 -70850.03c-316.51,-351.49 -391.95,-827.76 -199.57,-1259.94 192.37,-432.06 596.83,-694.69 1069.81,-694.69z"/>
<path class="fil0" d="M53642.57 31630.61l-9734.5 -7207.47c-60.86,-45.07 -145.24,-36.54 -195.89,19.71l-2010.88 2233.29c-51.81,57.58 -49.91,145.23 4.38,200.5l8423.84 8575.93c29.11,29.68 65.12,44.43 106.74,43.8 41.55,-0.63 77.11,-16.54 105.29,-47.14l3321.54 -3601.75c29.16,-31.64 42.12,-70.14 38.09,-113.01 -4.09,-42.88 -24.03,-78.21 -58.61,-103.85z"/>
<path class="fil0" d="M58036.69 27259.02l-11395.16 -5905.92c-59.99,-31.06 -130.94,-18.15 -176.12,32.05l-1894.23 2103.67c-29.34,32.62 -41.78,72.16 -36.54,115.67 5.3,43.51 26.92,78.9 63.23,103.45l10138.79 6869.51c58.09,39.37 133.88,32.68 184.19,-16.31l3150.61 -3067.27c34.75,-33.83 49.79,-78.26 42.7,-126.27 -7.09,-48 -34.35,-86.21 -77.45,-108.58z"/>
<path class="fil0" d="M62530.64 23163.08l-13156.5 -4870.57c-57.4,-21.26 -118.61,-6.11 -159.58,39.37l-1774.48 1970.71c-31.98,35.56 -44.14,79.93 -34.69,126.84 9.39,46.85 37.81,83.11 81.09,103.51l11900.88 5607.33c54.29,25.59 115.15,16.77 159.93,-23.22l3030.04 -2707.54c39.37,-35.21 56.14,-84.55 46.28,-136.41 -9.8,-51.87 -43.46,-91.64 -92.96,-110.02z"/>
<path class="fil0" d="M67208.65 19231.11l-15101.27 -3993.18c-54.41,-14.4 -108.58,1.73 -146.21,43.57l-1653.68 1836.55c-34.06,37.8 -45.76,86.1 -32.8,135.32 13.03,49.21 47.09,85.41 95.33,101.49l13799.78 4591.45c49.21,16.37 99.3,7.03 139.29,-25.93l2955.24 -2434.82c43.45,-35.79 61.9,-89.21 49.91,-144.25 -12.04,-54.98 -51.18,-95.84 -105.58,-110.19z"/>
<path class="fil0" d="M72164.57 15393.47l-17323.09 -3206.51c-51.53,-9.57 -100.34,7.03 -135.38,45.99l-1532.94 1702.44c-35.61,39.54 -46.8,90.95 -30.83,141.78 15.9,50.77 54.52,86.62 106.33,98.66l15933.31 3718.68c43.86,10.26 85.75,1.32 121.66,-25.87l2922.73 -2214.61c47.26,-35.79 67.66,-92.73 54,-150.42 -13.65,-57.63 -57.46,-99.35 -115.78,-110.13z"/>
<path class="fil0" d="M77517.86 11599.12l-19941.36 -2460.75c-48.87,-6 -93.71,10.72 -126.68,47.31l-1412.61 1568.85c-36.82,40.98 -47.6,94.86 -29.22,146.79 18.39,51.93 60.63,87.08 115.03,95.73l18416.54 2921.98c38.61,6.11 74,-1.96 106.16,-24.15l2937.42 -2030.08c50.95,-35.21 73.71,-95.15 58.96,-155.31 -14.81,-60.17 -62.76,-102.81 -124.25,-110.37z"/>
<path class="fil0" d="M83442.97 7803.9l-23130.63 -1712.58c-46.68,-3.46 -88.29,13.25 -119.58,48.06l-1292.97 1435.95c-37.86,42.07 -48.18,97.97 -27.78,150.71 20.34,52.79 65.64,87.25 121.89,92.9l21406.64 2149.14c33.49,3.34 63.23,-3.57 91.81,-21.32l3017.02 -1872.51c54.75,-34 80.34,-96.65 64.95,-159.29 -15.39,-62.59 -67.03,-106.28 -131.34,-111.06z"/>
<path class="fil0" d="M90236.87 3960.34l-27188.07 -915.02c-44.72,-1.5 -83.8,15.1 -113.71,48.35l-1174.18 1304.03c-38.67,42.87 -48.52,100.51 -26.45,153.87 22.07,53.31 69.74,87.14 127.42,90.2l25153.62 1350.59c28.24,1.56 52.79,-3.98 77.63,-17.46l3208.58 -1739.67c58.9,-31.93 88.06,-97.05 72.68,-162.23 -15.39,-65.24 -70.55,-110.42 -137.51,-112.67z"/>
<path class="fil0" d="M98522.23 0l-32736.44 0c-43,0 -80,16.48 -108.75,48.41l-1056.1 1172.86c-39.31,43.69 -48.87,102.64 -25.36,156.47 23.46,53.83 73.19,86.91 131.86,87.83l30136.51 464.91c22.36,0.35 41.67,-3.63 62.07,-12.79l3656.04 -1637.77c63.68,-28.53 97.86,-95.85 83.28,-164.14 -14.58,-68.29 -73.3,-115.78 -143.1,-115.78z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -7,7 +7,12 @@ import sympy as sp
from .base import BaseGenome
from .gene import DefaultNode, DefaultConn
from .operations import DefaultMutation, DefaultCrossover, DefaultDistance
from .operations import (
DefaultMutation,
DefaultCrossover,
DefaultDistance,
CombinedMutation,
)
from .utils import unflatten_conns, extract_gene_attrs, extract_gene_attrs
from tensorneat.common import (
@@ -40,8 +45,12 @@ class DefaultGenome(BaseGenome):
output_transform=None,
input_transform=None,
init_hidden_layers=(),
duplication_rate: float = 0.0,
):
# If a duplication_rate is specified, wrap the default mutation with the CombinedMutation
if duplication_rate > 0.0:
# Use CombinedMutation which includes standard mutation plus module duplication
mutation = CombinedMutation(duplication_rate=duplication_rate)
super().__init__(
num_inputs,
num_outputs,
@@ -66,7 +75,6 @@ class DefaultGenome(BaseGenome):
return seqs, nodes, conns, u_conns
def forward(self, state, transformed, inputs):
if self.input_transform is not None:
inputs = self.input_transform(inputs)
@@ -133,9 +141,7 @@ class DefaultGenome(BaseGenome):
network["topo_order"] = topo_order
network["topo_layers"] = topo_layers
network["useful_nodes"] = find_useful_nodes(
set(network["nodes"]),
set(network["conns"]),
set(self.output_idx)
set(network["nodes"]), set(network["conns"]), set(self.output_idx)
)
return network
@@ -147,7 +153,6 @@ class DefaultGenome(BaseGenome):
sympy_output_transform=None,
backend="jax",
):
assert backend in ["jax", "numpy"], "backend should be 'jax' or 'numpy'"
if sympy_input_transform is None and self.input_transform is not None:
@@ -187,7 +192,6 @@ class DefaultGenome(BaseGenome):
nodes_exprs = {}
args_symbols = {}
for i in order:
if i in input_idx:
nodes_exprs[symbols[-i - 1]] = symbols[
-i - 1
@@ -258,7 +262,7 @@ class DefaultGenome(BaseGenome):
output_exprs,
forward_func,
network["topo_order"],
network["useful_nodes"]
network["useful_nodes"],
)
def visualize(
@@ -298,7 +302,7 @@ class DefaultGenome(BaseGenome):
nodes_y = []
for node in nodes:
node_y = 0
for y, last_node in enumerate(topo_layers[layer-1]):
for y, last_node in enumerate(topo_layers[layer - 1]):
if (last_node, node) in conns_list:
node_y += y
nodes_y.append(node_y)
@@ -395,7 +399,7 @@ class DefaultGenome(BaseGenome):
edgecolors=edgecolors,
arrowstyle=arrowstyle,
arrowsize=arrowsize,
edge_color=edge_color
edge_color=edge_color,
)
plt.savefig(save_path, dpi=save_dpi)
plt.close()

View File

@@ -1,3 +1,3 @@
from .crossover import BaseCrossover, DefaultCrossover
from .mutation import BaseMutation, DefaultMutation
from .mutation import BaseMutation, DefaultMutation, CombinedMutation, ModuleDuplication
from .distance import BaseDistance, DefaultDistance

View File

@@ -1,2 +1,3 @@
from .base import BaseMutation
from .default import DefaultMutation
from .module_duplication import CombinedMutation, ModuleDuplication

View File

@@ -0,0 +1,141 @@
"""Module Duplication Mutation
Implements the MEND *module duplication* operator. It duplicates a hidden
module (a hidden node together with all its incoming and outgoing connections).
The operator fires with probability ``duplication_rate``.
"""
import jax
import jax.numpy as jnp
from .base import BaseMutation
from .default import DefaultMutation
from ...utils import add_node, add_conn, extract_gene_attrs
from tensorneat.common import I_INF
class ModuleDuplication(BaseMutation):
"""Duplicate a hidden module (node + its connections).
Parameters
----------
duplication_rate: float, optional
Probability that the duplication operator fires during a mutation
step. Must be in ``[0, 1]``. Default ``0.0`` (disabled).
"""
def __init__(self, duplication_rate: float = 0.0):
self.duplication_rate = duplication_rate
self._helper = DefaultMutation()
def _choose_node_key(self, key, nodes, input_idx, output_idx):
return self._helper.choose_node_key(
key,
nodes,
input_idx,
output_idx,
allow_input_keys=False,
allow_output_keys=False,
)
def __call__(
self, state, genome, randkey, nodes, conns, new_node_key, new_conn_key
):
"""Apply module duplication.
If the random draw is lower than ``duplication_rate`` a hidden node is
selected and duplicated (its connections are copied). Otherwise the genome
is returned unchanged.
"""
fire = jax.random.uniform(randkey) < self.duplication_rate
n_fixed = len(genome.conn_gene.fixed_attrs)
def duplicate(_):
node_key, node_idx = self._choose_node_key(
randkey, nodes, genome.input_idx, genome.output_idx
)
def no_node():
return nodes, conns
def do_dup():
# Add a new hidden node with identity attributes.
new_nodes = add_node(
nodes,
jnp.array([new_node_key]),
genome.node_gene.new_identity_attrs(state),
)
# Use jax.lax.scan to iterate over all connections and
# conditionally copy incoming ones (target == node_key).
def copy_incoming(carry, conn):
conns_acc = carry
is_incoming = (conn[1] == node_key) & ~jnp.isnan(conn[0])
attrs = conn[n_fixed:]
src = conn[0]
fix = jnp.array([src, new_node_key])
new_conn = jnp.concatenate((fix, attrs))
pos = jnp.argmax(jnp.isnan(conns_acc[:, 0]))
has_space = jnp.isnan(conns_acc[pos, 0])
should_add = is_incoming & has_space
conns_acc = jnp.where(
should_add,
conns_acc.at[pos].set(new_conn),
conns_acc,
)
return conns_acc, None
new_conns, _ = jax.lax.scan(copy_incoming, conns, conns)
# Copy outgoing connections (source == node_key).
def copy_outgoing(carry, conn):
conns_acc = carry
is_outgoing = (conn[0] == node_key) & ~jnp.isnan(conn[0])
attrs = conn[n_fixed:]
tgt = conn[1]
fix = jnp.array([new_node_key, tgt])
new_conn = jnp.concatenate((fix, attrs))
pos = jnp.argmax(jnp.isnan(conns_acc[:, 0]))
has_space = jnp.isnan(conns_acc[pos, 0])
should_add = is_outgoing & has_space
conns_acc = jnp.where(
should_add,
conns_acc.at[pos].set(new_conn),
conns_acc,
)
return conns_acc, None
# Scan over the ORIGINAL conns for outgoing check, but
# accumulate into the already-updated new_conns.
new_conns, _ = jax.lax.scan(copy_outgoing, new_conns, conns)
return new_nodes, new_conns
return jax.lax.cond(node_idx == I_INF, no_node, do_dup)
return jax.lax.cond(fire, duplicate, lambda _: (nodes, conns), operand=None)
class CombinedMutation(BaseMutation):
"""Combine ``DefaultMutation`` with optional ``ModuleDuplication``.
The combined mutation first runs the standard NEAT structural/value
mutations and then (with the configured ``duplication_rate``) may duplicate a
module.
"""
def __init__(self, duplication_rate: float = 0.0, **default_kwargs):
self.default_mut = DefaultMutation(**default_kwargs)
self.dup_mut = ModuleDuplication(duplication_rate)
def __call__(
self, state, genome, randkey, nodes, conns, new_node_key, new_conn_key
):
k1, k2 = jax.random.split(randkey)
nodes, conns = self.default_mut(
state, genome, k1, nodes, conns, new_node_key, new_conn_key
)
nodes, conns = self.dup_mut(
state, genome, k2, nodes, conns, new_node_key, new_conn_key
)
return nodes, conns

View File

@@ -0,0 +1,298 @@
# recurrent_mutation.py
import jax
import jax.numpy as jnp
# same helpers as DefaultMutation
from tensorneat.common import fetch_first, I_INF, check_cycles, fetch_random
from tensorneat.genome.operations.mutation.default import DefaultMutation
from tensorneat.genome.utils import (
extract_gene_attrs,
add_conn,
add_node,
unflatten_conns,
delete_conn_by_pos,
delete_node_by_pos,
set_gene_attrs,
)
class RecurrentMutation(DefaultMutation):
"""
A DefaultMutation variant that *biases* (not forces) evolution toward
recurrent connections, controlled by the parameter "p_recur".
Applies add/delete node and delete connection mutations identically to
the user's original implementation provided, but uses an optimized
add-connection strategy with cycle bias and retries.
Parameters
----------
p_recur : float, default 0.1
Probability (0 to 1) that any single *successful* add-connection
mutation *must* result in forming a cycle in the graph.
max_conn_tries : int, default 20
Maximum number of distinct (source, target) node pairs to sample
when attempting the add-connection mutation before giving up for
this genome in this generation.
conn_add : float
Probability of attempting connection addition. Inherited from
DefaultMutation.
conn_delete : float
Probability of attempting connection deletion. Inherited from
DefaultMutation.
node_add : float
Probability of attempting node addition. Inherited from DefaultMutation.
node_delete : float
Probability of attempting node deletion. Inherited from DefaultMutation.
kwargs : Other arguments accepted by DefaultMutation.__init__
"""
# constructor
def __init__(
self,
*,
p_recur: float = 0.1,
max_conn_tries: int = 20,
**kwargs # Pass conn_add, conn_delete etc. through to base class
):
# Initialize base class probabilities from DefaultMutation
super().__init__(**kwargs)
if not 0.0 <= p_recur <= 1.0:
raise ValueError("p_recur must be in [0, 1]")
if max_conn_tries < 1:
raise ValueError("max_conn_tries must be >= 1")
self.p_recur = float(p_recur)
self.max_conn_tries = int(max_conn_tries)
# structural mutation (all valuemutation logic is inherited intact)
def mutate_structure(
self, state, genome, randkey, nodes, conns, new_node_key, new_conn_key
):
"""
Apply ONE structural mutation (node-add/del, conn-add/del) chosen
according to the probabilities stored in this object.
Uses the custom `_mutate_add_conn_recurrent_optimized`.
Uses user's original implementations for add_node, delete_node,
delete_conn.
"""
# Helper functions (kept as per user request, structure preserved
def mutate_add_node(key_, nodes_, conns_):
remain_node_space = jnp.isnan(nodes_[:, 0]).sum()
remain_conn_space = jnp.isnan(conns_[:, 0]).sum()
i_key, o_key, idx = self.choose_connection_key(key_, conns_)
def successful_add_node():
# remove the original connection and record its attrs
original_attrs = extract_gene_attrs(
genome.conn_gene, conns_[idx]
)
new_conns = delete_conn_by_pos(conns_, idx)
# add a new node with identity attrs
new_nodes = add_node(
nodes_,
jnp.array([new_node_key]),
genome.node_gene.new_identity_attrs(state)
)
# build two replacement connections
if "historical_marker" in genome.conn_gene.fixed_attrs:
f1 = jnp.array([i_key, new_node_key, new_conn_key[0]])
f2 = jnp.array([new_node_key, o_key, new_conn_key[1]])
else:
f1 = jnp.array([i_key, new_node_key])
f2 = jnp.array([new_node_key, o_key])
new_conns = add_conn(
new_conns,
f1,
genome.conn_gene.new_identity_attrs(state)
)
new_conns = add_conn(new_conns, f2, original_attrs)
return new_nodes, new_conns
cond_do_nothing = (idx == I_INF) | \
(remain_node_space < 1) | \
(remain_conn_space < 2)
return jax.lax.cond(
cond_do_nothing, # condition for doing nothing
lambda: (nodes_, conns_), # do nothing branch (if cond is true)
successful_add_node # do add branch (if cond is false)
)
def mutate_delete_node(key_, nodes_, conns_):
k, idx = self.choose_node_key(
key_, nodes_, genome.input_idx, genome.output_idx,
allow_input_keys=False, allow_output_keys=False,
)
def do():
# delete the node
new_nodes = delete_node_by_pos(nodes_, idx)
# delete all connections
new_conns = jnp.where(
((conns_[:, 0] == k) | (conns_[:, 1] == k))[:, None],
jnp.nan, conns_
)
return new_nodes, new_conns
return jax.lax.cond(
idx == I_INF, # cond to determine "doing nothing"
lambda: (nodes_, conns_), # Do nothing branch
do # do delete branch
)
# NEW biased addconnection with optimizations and p_recur bias
def _mutate_add_conn_recurrent_optimized(key_, nodes_, conns_):
"""
Optimized: Attempts to insert one connection with recurrent bias.
Makes up to "self.max_conn_tries" draws.
Computes graph structure once before vmap.
Checks prerequisites before cycle check.
"""
remain_conn_space = jnp.isnan(conns_[:, 0]).sum()
space_available = remain_conn_space >= 1
# Optimization: Calculate graph structure ONCE before vmap
u_conns = unflatten_conns(nodes_, conns_)
conns_exist_matrix = u_conns != I_INF
# Function for a single attempt
def attempt(k_triplet):
"""One sampling attempt; returns (accept?, i_key, o_key)."""
k1_node, k2_node, k_recur = jax.random.split(k_triplet, 3)
# 1. Sample endpoints
i_key, from_idx = self.choose_node_key(
k1_node, nodes_, genome.input_idx, genome.output_idx,
allow_input_keys=True, allow_output_keys=True,
)
o_key, to_idx = self.choose_node_key(
k2_node, nodes_, genome.input_idx, genome.output_idx,
allow_input_keys=False, allow_output_keys=True,
)
# 2. Basic checks: nodes selected? space available?
nodes_chosen = (from_idx != I_INF) & (to_idx != I_INF)
prereqs_met = nodes_chosen & space_available
# --- Nested conditional logic for efficiency ---
def check_existence_and_cycle():
# 3. Duplicate check (only if prereqs met)
exists = fetch_first(
(conns_[:, 0] == i_key) & (conns_[:, 1] == o_key)
) != I_INF
not_duplicate = ~exists
def check_cycle_logic():
# 4. Cycle check (only if valid, non-duplicate candidate)
forms_cy = check_cycles(
nodes_,
conns_exist_matrix,
from_idx, to_idx
)
# 5. Decide if we *require* a cycle
force_recur = jax.random.uniform(k_recur) < self.p_recur
# 6. Final check: cycle requirement satisfaction
cycle_req_satisfied = (force_recur & forms_cy) | \
(~force_recur)
# Attempt is valid if cycle requirement is met
is_valid = cycle_req_satisfied
return is_valid, i_key, o_key
# If not duplicate, check cycle logic, else invalid
is_valid, final_i_key, final_o_key = jax.lax.cond(
not_duplicate,
check_cycle_logic,
lambda: (jnp.array(False), jnp.nan, jnp.nan) # is_valid=False if duplicate
)
return is_valid, final_i_key, final_o_key
# If basic prereqs met, check existence/cycle, else invalid
is_valid_attempt, i_key_attempt, o_key_attempt = jax.lax.cond(
prereqs_met,
check_existence_and_cycle,
lambda: (jnp.array(False), jnp.nan, jnp.nan) # is_valid=False if prereqs not met
)
return is_valid_attempt, i_key_attempt, o_key_attempt # bool, float, float
# Vectorize attempts and find first success
keys = jax.random.split(key_, self.max_conn_tries)
accept_flags, i_keys_all, o_keys_all = jax.vmap(attempt)(keys)
# find FIRST successful draw (if any)
first_success_idx = fetch_first(accept_flags)
found_valid_candidate = first_success_idx != I_INF
# Conditionally add the connection
def do_accept():
# Get the keys from the first successful attempt
i_key_chosen = i_keys_all[first_success_idx]
o_key_chosen = o_keys_all[first_success_idx]
# Create fixed attributes (using 3rd marker for add_conn)
if "historical_marker" in genome.conn_gene.fixed_attrs:
fix = jnp.array([i_key_chosen, o_key_chosen, new_conn_key[2]])
else:
fix = jnp.array([i_key_chosen, o_key_chosen])
# Add the connection
new_conns = add_conn(
conns_,
fix,
genome.conn_gene.new_zero_attrs(state) # Add with zero/default attrs
)
return nodes_, new_conns
# If a valid candidate was found, add it; otherwise return original
# arrays
return jax.lax.cond(
found_valid_candidate, # Condition is True if we should accept
do_accept, # Do accept branch (if cond is true)
lambda: (nodes_, conns_) # Do nothing branch (if cond is false)
)
def mutate_delete_conn(key_, nodes_, conns_):
i_key, o_key, idx = self.choose_connection_key(key_, conns_)
return jax.lax.cond(
idx == I_INF,
lambda: (nodes_, conns_),
lambda: (nodes_, delete_conn_by_pos(conns_, idx)),
)
# --- Scheduling Logic (unchanged from user's code) ---
k_node_add, k_node_del, k_conn_add, k_conn_del, k_schedule = \
jax.random.split(randkey, 5)
probs = jax.random.uniform(k_schedule, (4,))
def nothing(_, n_, c_): return n_, c_
# Apply mutations conditionally using original helper functions
nodes, conns = jax.lax.cond(
(self.node_add > 0) & (probs[0] < self.node_add),
mutate_add_node, nothing, k_node_add, nodes, conns
)
nodes, conns = jax.lax.cond(
(self.node_delete > 0) & (probs[1] < self.node_delete),
mutate_delete_node, nothing, k_node_del, nodes, conns
)
# Apply connection addition using the OPTIMIZED version
nodes, conns = jax.lax.cond(
(self.conn_add > 0) & (probs[2] < self.conn_add),
_mutate_add_conn_recurrent_optimized, # <<< Use optimized add_conn
nothing, k_conn_add, nodes, conns
)
# Apply connection deletion using original helper function
nodes, conns = jax.lax.cond(
(self.conn_delete > 0) & (probs[3] < self.conn_delete),
mutate_delete_conn, nothing, k_conn_del, nodes, conns
)
return nodes, conns

View File

@@ -0,0 +1,164 @@
"""Tests for the MEND ModuleDuplication mutation.
Tests verify that:
1. The ModuleDuplication operator alone adds exactly one hidden node.
2. The duplicated node has connections copied from the source.
3. CombinedMutation integrates both default and duplication mutations.
"""
import jax
import jax.numpy as jnp
from tensorneat.genome import DefaultGenome
from tensorneat.genome.operations.mutation import ModuleDuplication
def _count_hidden(nodes, genome):
"""Count non-NaN hidden nodes (excluding inputs/outputs)."""
return sum(
1
for n in nodes
if not jnp.isnan(n[0])
and int(n[0]) not in set(genome.input_idx.tolist())
and int(n[0]) not in set(genome.output_idx.tolist())
)
def _count_valid_conns(conns):
"""Count non-NaN connections."""
return sum(1 for c in conns if not jnp.isnan(c[0]))
def test_module_duplication_standalone():
"""Test ModuleDuplication in isolation (not via CombinedMutation).
Start with a genome that has one hidden node (via init_hidden_layers),
then apply only ModuleDuplication with rate=1.0.
"""
genome = DefaultGenome(
num_inputs=2,
num_outputs=1,
max_nodes=10,
max_conns=30,
init_hidden_layers=(1,), # start with 1 hidden node
)
state = genome.setup()
randkey = jax.random.PRNGKey(42)
nodes, conns = genome.initialize(state, randkey)
init_hidden = _count_hidden(nodes, genome)
init_conns = _count_valid_conns(conns)
assert init_hidden == 1, f"Expected 1 initial hidden node, got {init_hidden}"
# Apply only the duplication operator (not CombinedMutation).
dup = ModuleDuplication(duplication_rate=1.0)
k1, _ = jax.random.split(randkey)
new_node_key = jnp.array(genome.max_nodes + 1)
new_conn_key = jnp.array(
[genome.max_conns + 1, genome.max_conns + 2, genome.max_conns + 3]
)
nodes_mut, conns_mut = dup(
state, genome, k1, nodes, conns, new_node_key, new_conn_key
)
post_hidden = _count_hidden(nodes_mut, genome)
post_conns = _count_valid_conns(conns_mut)
assert post_hidden == init_hidden + 1, (
f"Module duplication should add exactly one hidden node, "
f"got {post_hidden} (was {init_hidden})"
)
# The source node had 2 incoming (from inputs) + 1 outgoing (to output) = 3 conns.
# Duplication should copy all of them.
assert post_conns > init_conns, (
f"Duplication should add connections, got {post_conns} (was {init_conns})"
)
def test_duplicated_node_has_connections():
"""The duplicated node must have both incoming and outgoing connections."""
genome = DefaultGenome(
num_inputs=2,
num_outputs=1,
max_nodes=10,
max_conns=30,
init_hidden_layers=(1,),
)
state = genome.setup()
randkey = jax.random.PRNGKey(7)
nodes, conns = genome.initialize(state, randkey)
dup = ModuleDuplication(duplication_rate=1.0)
k1, _ = jax.random.split(randkey)
new_node_key = jnp.array(genome.max_nodes + 1)
new_conn_key = jnp.array(
[genome.max_conns + 1, genome.max_conns + 2, genome.max_conns + 3]
)
nodes_mut, conns_mut = dup(
state, genome, k1, nodes, conns, new_node_key, new_conn_key
)
nk = float(new_node_key)
incoming = sum(
1 for c in conns_mut if not jnp.isnan(c[0]) and float(c[1]) == nk
)
outgoing = sum(
1 for c in conns_mut if not jnp.isnan(c[0]) and float(c[0]) == nk
)
assert incoming > 0, "Duplicated node must have incoming connections"
assert outgoing > 0, "Duplicated node must have outgoing connections"
def test_combined_mutation_with_duplication():
"""CombinedMutation should produce a valid genome (no crashes)."""
genome = DefaultGenome(
num_inputs=2,
num_outputs=1,
max_nodes=15,
max_conns=40,
duplication_rate=1.0,
)
state = genome.setup()
randkey = jax.random.PRNGKey(99)
nodes, conns = genome.initialize(state, randkey)
k1, _ = jax.random.split(randkey)
new_node_key = jnp.array(genome.max_nodes + 1)
new_conn_key = jnp.array(
[genome.max_conns + 1, genome.max_conns + 2, genome.max_conns + 3]
)
nodes_mut, conns_mut = genome.execute_mutation(
state, k1, nodes, conns, new_node_key, new_conn_key
)
# Should not crash and should have at least as many nodes/conns as before.
init_valid = sum(1 for n in nodes if not jnp.isnan(n[0]))
post_valid = sum(1 for n in nodes_mut if not jnp.isnan(n[0]))
assert post_valid >= init_valid, "Mutation should not lose nodes"
def test_duplication_disabled_when_rate_zero():
"""With duplication_rate=0.0, no duplication should occur."""
genome = DefaultGenome(
num_inputs=2,
num_outputs=1,
max_nodes=10,
max_conns=30,
init_hidden_layers=(1,),
)
state = genome.setup()
randkey = jax.random.PRNGKey(0)
nodes, conns = genome.initialize(state, randkey)
dup = ModuleDuplication(duplication_rate=0.0)
k1, _ = jax.random.split(randkey)
new_node_key = jnp.array(genome.max_nodes + 1)
new_conn_key = jnp.array(
[genome.max_conns + 1, genome.max_conns + 2, genome.max_conns + 3]
)
nodes_mut, conns_mut = dup(
state, genome, k1, nodes, conns, new_node_key, new_conn_key
)
assert _count_hidden(nodes_mut, genome) == _count_hidden(nodes, genome), (
"No duplication should occur with rate=0.0"
)