diff --git a/.travis.yml b/.travis.yml index d96d298e66..3ef87106a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ branches: - develop - master - MATH_3_X + - feature__MATH-1563__genetic_algorithm script: - mvn diff --git a/commons-math-examples/examples-ga/LICENCE b/commons-math-examples/examples-ga/LICENCE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/commons-math-examples/examples-ga/LICENCE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/commons-math-examples/examples-ga/NOTICE b/commons-math-examples/examples-ga/NOTICE new file mode 100644 index 0000000000..28031e8ef9 --- /dev/null +++ b/commons-math-examples/examples-ga/NOTICE @@ -0,0 +1,5 @@ +Apache Commons Math +Copyright 2001-2022 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/LICENCE b/commons-math-examples/examples-ga/examples-ga-math-functions/LICENCE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/LICENCE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/NOTICE b/commons-math-examples/examples-ga/examples-ga-math-functions/NOTICE new file mode 100644 index 0000000000..28031e8ef9 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/NOTICE @@ -0,0 +1,5 @@ +Apache Commons Math +Copyright 2001-2022 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml b/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml new file mode 100644 index 0000000000..d09ca3ff10 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + org.apache.commons + examples-ga + 4.0-SNAPSHOT + + examples-ga-math-functions + + + 1.8 + 1.8 + + + org.apache.commons.math4.examples.ga.mathfunctions + org.apache.commons.math4.examples.ga.mathfunctions + + org.apache.commons.math4.examples.ga.mathfunctions + + ${basedir}/../../.. + + examples-ga-mathfunctions + org.apache.commons.math4.examples.ga.mathfunctions.Dimension2FunctionOptimizer + 1.7.32 + + + + + org.slf4j + slf4j-simple + ${slf4jVersion} + + + \ No newline at end of file diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java new file mode 100644 index 0000000000..eca37e8798 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dim2GraphPlotter.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import java.awt.BorderLayout; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class Dim2GraphPlotter extends JFrame implements ConvergenceListener { + + /** + * Generated serialversionId. + */ + private static final long serialVersionUID = -5683904006424006584L; + + /** collection of 2-D series. **/ + private final XYSeriesCollection dataset = new XYSeriesCollection(); + + /** + * constructor. + * @param plotSubject subject of plot + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + */ + public Dim2GraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) { + super(plotSubject); + + final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel); + add(chartPanel, BorderLayout.CENTER); + + setSize(640, 480); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + + setVisible(true); + } + + /** + * Adds data point to graph. + * @param graphName name of graph + * @param generation generation, to be plotted along x axis + * @param value value, to be plotted along y axis + */ + private void addDataPoint(String graphName, int generation, double value) { + XYSeries series = null; + + if (!containsGraph(graphName)) { + series = new XYSeries(graphName); + dataset.addSeries(series); + } else { + series = dataset.getSeries(graphName); + } + series.add(generation, value); + + setVisible(true); + } + + /** + * Checks if the graph with the given name already exists. + * @param graphName name of the graph + * @return true/false + */ + @SuppressWarnings("unchecked") + private boolean containsGraph(String graphName) { + final List seriesList = dataset.getSeries(); + if (seriesList == null || seriesList.isEmpty()) { + return false; + } + for (XYSeries series : seriesList) { + if (series.getKey().compareTo(graphName) == 0) { + return true; + } + } + return false; + } + + /** + * Creates chart panel. + * @param chartTitle chart title + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + * @return panel + */ + private JPanel createChartPanel(String chartTitle, String xAxisLabel, String yAxisLabel) { + + final boolean showLegend = true; + final boolean createURL = false; + final boolean createTooltip = false; + + final JFreeChart chart = ChartFactory.createXYLineChart(chartTitle, xAxisLabel, yAxisLabel, dataset, + PlotOrientation.VERTICAL, showLegend, createTooltip, createURL); + final XYPlot plot = chart.getXYPlot(); + final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + + plot.setRenderer(renderer); + + return new ChartPanel(chart); + + } + + /** + * {@inheritDoc} + */ + @Override + public void notify(int generation, Population population) { + PopulationStatisticalSummary populationStatisticalSummary = + new PopulationStatisticalSummaryImpl<>(population); + this.addDataPoint("Average", generation, populationStatisticalSummary.getMeanFitness()); + this.addDataPoint("Best", generation, populationStatisticalSummary.getMaxFitness()); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java new file mode 100644 index 0000000000..307f1efff2 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Coordinate.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +/** + * This class represents the coordinate of the problem domain i.e. the phenotype of chromosome. + */ +public class Dimension2Coordinate { + + /** coordinate of first dimension. **/ + private final double x; + + /** coordinate of second dimension. **/ + private final double y; + + /** + * constructor. + * @param x coordinate of first dimension + * @param y coordinate of second dimension + */ + public Dimension2Coordinate(double x, double y) { + this.x = x; + this.y = y; + } + + /** + * returns the coordinate of first dimension. + * @return coordinate of first dimension + */ + public double getX() { + return x; + } + + /** + * returns the coordinate of second dimension. + * @return coordinate of second dimension + */ + public double getY() { + return y; + } + + /** + * Returns a string representation of coordinate. + */ + @Override + public String toString() { + return "Coordinate [x=" + x + ", y=" + y + "]"; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java new file mode 100644 index 0000000000..e8394d12c6 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2Decoder.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.decoder.Decoder; + +/** + * Decoder to convert chromosome's binary genotype to phenotype + * {@link Dimension2Coordinate}. + */ +public class Dimension2Decoder implements Decoder { + + /** + * Decode the binary representation of chromosome to + * {@link Dimension2Coordinate}. + * @param chromosome The {@link Chromosome} + */ + @Override + public Dimension2Coordinate decode(Chromosome chromosome) { + final BinaryChromosome binaryChromosome = + (BinaryChromosome) chromosome; + final long alleles = binaryChromosome.getRepresentation()[0]; + + long mask1 = ~(Long.MAX_VALUE << 12); + long mask2 = ~(Long.MAX_VALUE << 24) ^ mask1; + + final double x = (alleles & mask1) / 100d; + final double y = ((alleles & mask2) >> 12) / 100d; + + return new Dimension2Coordinate(x, y); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java new file mode 100644 index 0000000000..33e8f73018 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FitnessFunction.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents the mathematical fitness function for optimizing a 2 + * dimension mathematical function. + */ +public class Dimension2FitnessFunction implements FitnessFunction { + + /** + * Computes the fitness value based on the decoded chromosome. + * @param coordinate The {@link Dimension2Coordinate} + * @return the fitness value + */ + @Override + public double compute(Dimension2Coordinate coordinate) { + return -Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .25) * + (Math.pow(Math.sin(50 * Math.pow(Math.pow(coordinate.getX(), 2) + Math.pow(coordinate.getY(), 2), .1)), + 2) + 1); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java new file mode 100644 index 0000000000..6579dda896 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionAdaptiveOptimizer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.AdaptiveGeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.crossover.rategenerator.ConstantCrossoverRateGenerator; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.mutation.rategenerator.AdaptiveLinearMutationRateGenerator; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class Dimension2FunctionAdaptiveOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 2; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 3; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(Dimension2FunctionAdaptiveOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final Dimension2FunctionAdaptiveOptimizer optimizer = new Dimension2FunctionAdaptiveOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new Dim2GraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); + + optimizer.optimize(initPopulation); + } + + private void optimize(Population initial) { + + // initialize a new genetic algorithm + final AdaptiveGeneticAlgorithm ga = new AdaptiveGeneticAlgorithm<>( + new OnePointBinaryCrossover(), new ConstantCrossoverRateGenerator<>(1), + new BinaryMutation(), + new AdaptiveLinearMutationRateGenerator<>(Constants.AVERAGE_MUTATION_RATE / 2, + Constants.AVERAGE_MUTATION_RATE * 2), + new TournamentSelection<>(TOURNAMENT_SIZE)); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness<>( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + final Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final Dimension2FitnessFunction fitnessFunction = new Dimension2FitnessFunction(); + final Dimension2Decoder decoder = new Dimension2Decoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java new file mode 100644 index 0000000000..1d6a3fa6b5 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/Dimension2FunctionOptimizer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class Dimension2FunctionOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 2; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 3; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(Dimension2FunctionAdaptiveOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final Dimension2FunctionOptimizer optimizer = new Dimension2FunctionOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new Dim2GraphPlotter("Convergence Stats", "generation", "fitness")); + + optimizer.optimize(initPopulation); + } + + /** + * Optimizes the population. + * @param initial The {@link Population} + */ + public void optimize(Population initial) { + + // initialize a new genetic algorithm + final GeneticAlgorithm ga = new GeneticAlgorithm<>( + new OnePointBinaryCrossover(), Constants.CROSSOVER_RATE, + new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection(TOURNAMENT_SIZE), Constants.ELITISM_RATE); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness<>( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + final Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final Dimension2FitnessFunction fitnessFunction = new Dimension2FitnessFunction(); + final Dimension2Decoder decoder = new Dimension2Decoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java new file mode 100644 index 0000000000..b523d41f74 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/Dimension2FunctionOptimizerLegacy.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; + +import org.apache.commons.math3.genetics.BinaryChromosome; +import org.apache.commons.math3.genetics.BinaryMutation; +import org.apache.commons.math3.genetics.Chromosome; +import org.apache.commons.math3.genetics.ElitisticListPopulation; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.OnePointCrossover; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; +import org.apache.commons.math3.genetics.TournamentSelection; +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * the legacy genetic algorithm. + */ +public class Dimension2FunctionOptimizerLegacy { + + /** number of dimension. **/ + private static final int DIMENSION = 2; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 3; + + /** + * Optimizes the 2-dimensional fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + final Dimension2FunctionOptimizerLegacy simulation = new Dimension2FunctionOptimizerLegacy(); + + simulation.optimize(initPopulation); + } + + /** + * Optimizes the initial population using legacy genetic algorithm. + * @param initial initial {@link Population} + */ + public void optimize(Population initial) { + + // initialize a new genetic algorithm + final GeneticAlgorithm geneticAlgorithm = new GeneticAlgorithm(new OnePointCrossover<>(), + Constants.CROSSOVER_RATE, new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection(TOURNAMENT_SIZE)); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = geneticAlgorithm.evolve(initial, stopCond); + + // best chromosome from the final population + final Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, Constants.ENCODING))) { + writer.write("*********************************************"); + writer.newLine(); + writer.write("***********Optimization Result***************"); + writer.write(bestFinal.toString()); + } catch (IOException e) { + throw new GeneticException(e); + } + } + + /** + * Generates the initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ElitisticListPopulation(Constants.POPULATION_SIZE_PER_DIMENSION, + Constants.ELITISM_RATE); + for (int i = 0; i < Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(new LegacyBinaryChromosome(BinaryChromosome + .randomBinaryRepresentation(DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION))); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java new file mode 100644 index 0000000000..66c4615abd --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/LegacyBinaryChromosome.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; + +import java.util.List; + +import org.apache.commons.math3.genetics.AbstractListChromosome; +import org.apache.commons.math3.genetics.BinaryChromosome; + +/** + * A representation of concrete binary chromosome. + */ +public class LegacyBinaryChromosome extends BinaryChromosome { + + /** + * constructor. + * @param representation the internal representation + */ + public LegacyBinaryChromosome(List representation) { + super(representation); + } + + /** + * {@inheritDoc} + */ + @Override + public double fitness() { + final List alleles = getRepresentation(); + + final StringBuilder allelesStr = new StringBuilder(); + for (Integer allele : alleles) { + allelesStr.append(Integer.toBinaryString(allele)); + } + + final double x = Integer.parseInt(allelesStr.substring(0, 12), 2) / 100.0; + final double y = Integer.parseInt(allelesStr.substring(12, 24), 2) / 100.0; + + return -Math.pow(Math.pow(x, 2) + Math.pow(y, 2), .25) * + (Math.pow(Math.sin(50 * Math.pow(Math.pow(x, 2) + Math.pow(y, 2), .1)), 2) + 1); + } + + /** + * {@inheritDoc} + */ + @Override + public AbstractListChromosome newFixedLengthChromosome(List chromosomeRepresentation) { + return new LegacyBinaryChromosome(chromosomeRepresentation); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java new file mode 100644 index 0000000000..8800f3978b --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/UnchangedBestFitness.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; + +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; + +/** + * This class represents the stopping condition based on unchanged best fitness. + */ +public class UnchangedBestFitness implements StoppingCondition { + + /** last best fitness. **/ + private double lastBestFitness = Double.MIN_VALUE; + + /** maximum number of generations evolved with unchanged best fitness. **/ + private final int maxGenerationsWithUnchangedBestFitness; + + /** generations having unchanged best fitness. **/ + private int generationsHavingUnchangedBestFitness; + + /** + * constructor. + * @param maxGenerationsWithUnchangedAverageFitness maximum number of + * generations evolved with + * unchanged best fitness + */ + public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) { + this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population population) { + final double currentBestFitness = population.getFittestChromosome().getFitness(); + + if (lastBestFitness == currentBestFitness) { + this.generationsHavingUnchangedBestFitness++; + if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) { + return true; + } + } else { + this.generationsHavingUnchangedBestFitness = 0; + lastBestFitness = currentBestFitness; + } + + return false; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java new file mode 100644 index 0000000000..7965791c5c --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/legacy/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2.legacy; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java new file mode 100644 index 0000000000..432651f230 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimension2/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimension2; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java new file mode 100644 index 0000000000..52571e3825 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimNGraphPlotter.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import java.awt.BorderLayout; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class DimNGraphPlotter extends JFrame implements ConvergenceListener { + + /** + * Generated serialversionId. + */ + private static final long serialVersionUID = -5683904006424006584L; + + /** collection of 2-D series. **/ + private final XYSeriesCollection dataset = new XYSeriesCollection(); + + /** + * constructor. + * @param plotSubject subject of plot + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + */ + public DimNGraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) { + super(plotSubject); + + final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel); + add(chartPanel, BorderLayout.CENTER); + + setSize(640, 480); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + + setVisible(true); + } + + /** + * Adds data point to graph. + * @param graphName name of graph + * @param generation generation, to be plotted along x axis + * @param value value, to be plotted along y axis + */ + private void addDataPoint(String graphName, int generation, double value) { + XYSeries series = null; + + if (!containsGraph(graphName)) { + series = new XYSeries(graphName); + dataset.addSeries(series); + } else { + series = dataset.getSeries(graphName); + } + series.add(generation, value); + + setVisible(true); + } + + /** + * Checks if the graph with the given name already exists. + * @param graphName name of the graph + * @return true/false + */ + @SuppressWarnings("unchecked") + private boolean containsGraph(String graphName) { + final List seriesList = dataset.getSeries(); + if (seriesList == null || seriesList.isEmpty()) { + return false; + } + for (XYSeries series : seriesList) { + if (series.getKey().compareTo(graphName) == 0) { + return true; + } + } + return false; + } + + /** + * Creates chart panel. + * @param chartTitle chart title + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + * @return panel + */ + private JPanel createChartPanel(String chartTitle, String xAxisLabel, String yAxisLabel) { + + final boolean showLegend = true; + final boolean createURL = false; + final boolean createTooltip = false; + + final JFreeChart chart = ChartFactory.createXYLineChart(chartTitle, xAxisLabel, yAxisLabel, dataset, + PlotOrientation.VERTICAL, showLegend, createTooltip, createURL); + final XYPlot plot = chart.getXYPlot(); + final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + + plot.setRenderer(renderer); + + return new ChartPanel(chart); + + } + + /** + * {@inheritDoc} + */ + @Override + public void notify(int generation, Population population) { + PopulationStatisticalSummary populationStatisticalSummary = + new PopulationStatisticalSummaryImpl<>(population); + this.addDataPoint("Average", generation, populationStatisticalSummary.getMeanFitness()); + this.addDataPoint("Best", generation, populationStatisticalSummary.getMaxFitness()); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java new file mode 100644 index 0000000000..98a31bcffc --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNCoordinate.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import java.util.List; + +/** + * This class represents the coordinate of the problem domain i.e. the phenotype + * of chromosome. + */ +public class DimensionNCoordinate { + + /** coordinate of all dimensions. **/ + private final List values; + + /** + * constructor. + * @param values coordinates of all dimensions. + */ + public DimensionNCoordinate(List values) { + this.values = values; + } + + /** + * Returns the values of all coordinates. + * @return values of coordinates + */ + public List getValues() { + return values; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Coordinate [values=" + values + "]"; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java new file mode 100644 index 0000000000..957981500f --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNDecoder.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.decoder.Decoder; + +/** + * Decoder to convert chromosome's binary genotype to phenotype + * {@link DimensionNCoordinate}. + */ +public class DimensionNDecoder implements Decoder { + + /** + * decode the binary representation of chromosome to + * {@link DimensionNCoordinate}. + * @param chromosome The {@link Chromosome} + */ + @Override + public DimensionNCoordinate decode(Chromosome chromosome) { + final BinaryChromosome binaryChromosome = + (BinaryChromosome) chromosome; + final long length = binaryChromosome.getLength(); + final List coordinates = new ArrayList<>(); + + for (int i = 0; i < length; i += 12) { + final String dimensionStrValue = binaryChromosome.getStringRepresentation(i, i + 12); + coordinates.add(Integer.parseUnsignedInt(dimensionStrValue, 2) / 100d); + } + + return new DimensionNCoordinate(coordinates); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java new file mode 100644 index 0000000000..1c0d7db94f --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFitnessFunction.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents the mathematical fitness function for optimizing a 2 + * dimension mathematical function. + */ +public class DimensionNFitnessFunction implements FitnessFunction { + + /** + * Computes the fitness value based on the decoded chromosome. + * @param coordinate The {@link DimensionNCoordinate} + * @return the fitness value + */ + @Override + public double compute(DimensionNCoordinate coordinate) { + double sumOfSquare = 0.0; + for (Double value : coordinate.getValues()) { + sumOfSquare += Math.pow(value, 2); + } + return -Math.pow(sumOfSquare, .25) * (Math.pow(Math.sin(50 * Math.pow(sumOfSquare, .1)), 2) + 1); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java new file mode 100644 index 0000000000..dee317a3dd --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionAdaptiveOptimizer.java @@ -0,0 +1,114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.AbstractGeneticAlgorithm; +import org.apache.commons.math4.ga.AdaptiveGeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.crossover.rategenerator.AdaptiveLinearMaximumRankBasedCrossoverRateGenerator; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.mutation.rategenerator.AdaptiveLinearMutationRateGenerator; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class DimensionNFunctionAdaptiveOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 10; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 4; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(DimensionNFunctionOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final DimensionNFunctionAdaptiveOptimizer optimizer = new DimensionNFunctionAdaptiveOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new DimNGraphPlotter("Adaptive Convergence Stats", "generation", "fitness")); + + optimizer.optimize(initPopulation); + } + + private void optimize(Population initial) { + + // initialize a new genetic algorithm + final AbstractGeneticAlgorithm ga = new AdaptiveGeneticAlgorithm( + new OnePointBinaryCrossover(), + new AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<>(Constants.CROSSOVER_RATE / 2, + Constants.CROSSOVER_RATE), + new BinaryMutation<>(), new AdaptiveLinearMutationRateGenerator<>(0, Constants.AVERAGE_MUTATION_RATE), + new TournamentSelection<>(TOURNAMENT_SIZE), Constants.ELITISM_RATE); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness<>( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + final Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final DimensionNFitnessFunction fitnessFunction = new DimensionNFitnessFunction(); + final DimensionNDecoder decoder = new DimensionNDecoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java new file mode 100644 index 0000000000..5791b05769 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/DimensionNFunctionOptimizer.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; + +import org.apache.commons.math4.examples.ga.mathfunctions.utils.Constants; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an optimizer for a 2-dimensional math function using + * genetic algorithm. + */ +public class DimensionNFunctionOptimizer { + + /** number of dimension. **/ + private static final int DIMENSION = 10; + + /** size of tournament. **/ + private static final int TOURNAMENT_SIZE = 4; + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(DimensionNFunctionOptimizer.class); + + /** + * Optimizes the 2-dimension fitness function. + * @param args arguments + */ + public static void main(String[] args) { + final Population initPopulation = getInitialPopulation(); + + final DimensionNFunctionOptimizer optimizer = new DimensionNFunctionOptimizer(); + + final ConvergenceListenerRegistry convergenceListenerRegistry = + ConvergenceListenerRegistry.getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger()); + convergenceListenerRegistry + .addConvergenceListener(new DimNGraphPlotter("Convergence Stats", "generation", "fitness")); + + optimizer.optimize(initPopulation); + } + + /** + * Optimizes the population. + * @param initial The {@link Population} + */ + public void optimize(Population initial) { + + // initialize a new genetic algorithm + final GeneticAlgorithm ga = new GeneticAlgorithm<>( + new OnePointBinaryCrossover(), Constants.CROSSOVER_RATE, + new BinaryMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection<>(TOURNAMENT_SIZE), Constants.ELITISM_RATE); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness<>( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + final Chromosome bestFinal = finalPopulation.getFittestChromosome(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + logger.info(bestFinal.toString()); + + } + + /** + * Generates an initial population. + * @return initial population + */ + private static Population getInitialPopulation() { + final Population population = new ListPopulation<>( + DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION); + final DimensionNFitnessFunction fitnessFunction = new DimensionNFitnessFunction(); + final DimensionNDecoder decoder = new DimensionNDecoder(); + for (int i = 0; i < DIMENSION * Constants.POPULATION_SIZE_PER_DIMENSION; i++) { + population.addChromosome(BinaryChromosome.randomChromosome( + DIMENSION * Constants.CHROMOSOME_LENGTH_PER_DIMENSION, fitnessFunction, decoder)); + } + return population; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java new file mode 100644 index 0000000000..6a15fed226 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/dimensionN/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.dimensionN; diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java new file mode 100644 index 0000000000..1389c75418 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/Constants.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.utils; + +/** + * This abstraction maintains constants used by this module. + */ +public final class Constants { + + /** size of population. **/ + public static final int POPULATION_SIZE_PER_DIMENSION = 10; + + /** size of tournament. **/ + public static final int TOURNAMENT_SIZE_PER_DIMENSION = 2; + + /** length of chromosome. **/ + public static final int CHROMOSOME_LENGTH_PER_DIMENSION = 12; + + /** rate of crossover. **/ + public static final double CROSSOVER_RATE = 1.0; + + /** rate of elitism. **/ + public static final double ELITISM_RATE = 0.25; + + /** rate of mutation. **/ + public static final double AVERAGE_MUTATION_RATE = 0.05; + + /** number of generations with unchanged best fitness. **/ + public static final int GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS = 50; + + /** encoding for console logger. **/ + public static final String ENCODING = "UTF-8"; + + /** + * constructor. + */ + private Constants() { + + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java new file mode 100644 index 0000000000..360a1c38fb --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-math-functions/src/main/java/org/apache/commons/math4/examples/ga/mathfunctions/utils/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.mathfunctions.utils; diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/LICENCE b/commons-math-examples/examples-ga/examples-ga-tsp/LICENCE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/LICENCE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/NOTICE b/commons-math-examples/examples-ga/examples-ga-tsp/NOTICE new file mode 100644 index 0000000000..28031e8ef9 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/NOTICE @@ -0,0 +1,5 @@ +Apache Commons Math +Copyright 2001-2022 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml new file mode 100644 index 0000000000..b4a7811307 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/pom.xml @@ -0,0 +1,52 @@ + + + + 4.0.0 + + org.apache.commons + examples-ga + 4.0-SNAPSHOT + + examples-ga-tsp + + + 1.8 + 1.8 + + + org.apache.commons.math4.examples.ga.tsp + org.apache.commons.math4.examples.ga.tsp + + org.apache.commons.math4.examples.ga.tsp + + ${basedir}/../../.. + + examples-ga-mathfunctions + org.apache.commons.math4.examples.ga.tsp.TSPOptimizer + 1.7.32 + + + + + org.apache.commons + commons-csv + 1.9.0 + + + org.slf4j + slf4j-simple + ${slf4jVersion} + + + \ No newline at end of file diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java new file mode 100644 index 0000000000..9b06312e0a --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPFitnessFunction.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp; + +import java.util.List; + + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.commons.DistanceMatrix; +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents the fitness function for tsp. + */ +public class TSPFitnessFunction implements FitnessFunction> { + + /** + * {@inheritDoc} + */ + @Override + public double compute(List cities) { + double totalDistance = 0.0; + int index1 = 0; + int index2 = 0; + for (int i = 0; i < cities.size(); i++) { + index1 = i; + index2 = (i == cities.size() - 1) ? 0 : i + 1; + totalDistance += calculateNodeDistance(cities.get(index1), cities.get(index2)); + } + return -totalDistance; + } + + private double calculateNodeDistance(City node1, City node2) { + final DistanceMatrix distanceMatrix = DistanceMatrix.getInstance(); + return distanceMatrix.getDistance(node1, node2); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java new file mode 100644 index 0000000000..21833ef3de --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/TSPOptimizer.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp; + +import java.util.List; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; +import org.apache.commons.math4.examples.ga.tsp.utils.GraphPlotter; +import org.apache.commons.math4.ga.GeneticAlgorithm; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.listener.PopulationStatisticsLogger; +import org.apache.commons.math4.ga.mutation.RealValuedMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents the optimizer for traveling salesman problem. + */ +public class TSPOptimizer { + + /** instance of logger. **/ + private Logger logger = LoggerFactory.getLogger(TSPOptimizer.class); + + /** + * Main method to initiate the optimization process. + * @param args arguments + */ + public static void main(String[] args) { + try { + final Population> initPopulation = getInitialPopulation(Constants.CITIES); + + final TSPOptimizer optimizer = new TSPOptimizer(); + + final ConvergenceListenerRegistry> convergenceListenerRegistry = ConvergenceListenerRegistry + .getInstance(); + convergenceListenerRegistry.addConvergenceListener(new PopulationStatisticsLogger<>()); + convergenceListenerRegistry + .addConvergenceListener(new GraphPlotter("Convergence", "generation", "total-distance")); + + optimizer.optimizeSGA(initPopulation, Constants.CITIES); + + Thread.sleep(5000); + + } catch (InterruptedException e) { + throw new GeneticException(e); + } + } + + /** + * Optimizes the tsp problem. + * @param initial initial population + * @param cities cities + */ + public void optimizeSGA(Population> initial, List cities) { + + // initialize a new genetic algorithm + final GeneticAlgorithm> ga = new GeneticAlgorithm<>(new OnePointCrossover>(), + Constants.CROSSOVER_RATE, new RealValuedMutation>(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection>(Constants.TOURNAMENT_SIZE)); + + // stopping condition + final StoppingCondition> stopCond = new UnchangedBestFitness<>( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population> finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + final RealValuedChromosome> bestFinal = (RealValuedChromosome>) finalPopulation + .getFittestChromosome(); + + logger.info("*********************************************"); + logger.info("***********Optimization Result***************"); + + logger.info(bestFinal.decode().toString()); + + } + + private static Population> getInitialPopulation(List cities) { + final Population> simulationPopulation = new ListPopulation<>(Constants.POPULATION_SIZE); + + for (int i = 0; i < Constants.POPULATION_SIZE; i++) { + simulationPopulation.addChromosome(new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomPermutation(Constants.CHROMOSOME_LENGTH), + new TSPFitnessFunction(), new RandomKeyDecoder(cities))); + } + + return simulationPopulation; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java new file mode 100644 index 0000000000..7a6d8d110b --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/City.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.commons; + +/** + * This class represents a city with location coordinate. + */ +public final class City { + + /** index of city. **/ + private final int index; + + /** x coordinate. **/ + private final double x; + + /** y coordinate. **/ + private final double y; + + /** + * constructor. + * @param index index of city + * @param x x coordinate + * @param y y coordinate + */ + public City(int index, double x, double y) { + this.index = index; + this.x = x; + this.y = y; + } + + /** + * Returns city index. + * @return city index + */ + public int getIndex() { + return index; + } + + /** + * Returns x coordinate. + * @return x coordinate + */ + public double getX() { + return x; + } + + /** + * Returns y coordinate. + * @return y coordinate + */ + public double getY() { + return y; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Node [index=" + index + ", x=" + x + ", y=" + y + "]"; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java new file mode 100644 index 0000000000..175c7c695e --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/DistanceMatrix.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.commons; + +import java.util.List; + +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; + +/** + * This class represents the distance matrix between cities. + */ +public final class DistanceMatrix { + + /** instance of the class. **/ + private static final DistanceMatrix INSTANCE = new DistanceMatrix(); + + /** distances between cities. **/ + private double[][] distances; + + private DistanceMatrix() { + initialize(Constants.CITIES); + } + + /** + * Returns distances between two cities. + * @param city1 first city + * @param city2 second city + * @return distance + */ + public double getDistance(City city1, City city2) { + return distances[city1.getIndex() - 1][city2.getIndex() - 1]; + } + + /** + * Initializes the distance matrix. + * @param cities list of cities + */ + private void initialize(List cities) { + final int len = cities.size(); + this.distances = new double[len][len]; + for (int i = 0; i < len; i++) { + for (int j = 0; j < len; j++) { + distances[i][j] = Math.pow(Math.pow(cities.get(i).getX() - cities.get(j).getX(), 2) + + Math.pow(cities.get(i).getY() - cities.get(j).getY(), 2), .5); + } + } + } + + /** + * Returns the instance of this class. + * @return instance + */ + public static DistanceMatrix getInstance() { + return INSTANCE; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java new file mode 100644 index 0000000000..e0422fd72e --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/commons/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.tsp.commons; diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java new file mode 100644 index 0000000000..9a42d39582 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPChromosome.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.legacy; + +import java.util.List; + +import org.apache.commons.math3.genetics.RandomKey; +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.commons.DistanceMatrix; + +/** + * This class represents chromosome for tsp problem. + */ +public class TSPChromosome extends RandomKey { + + /** list of cities. **/ + private final List cities; + + /** + * constructor. + * @param representation internal representation of chromosome + * @param cities list of cities + */ + public TSPChromosome(List representation, List cities) { + super(representation); + this.cities = cities; + } + + /** + * {@inheritDoc} + */ + @Override + public double fitness() { + final List permutatedNodes = decode(cities); + return -calculateTotalDistance(permutatedNodes); + } + + /** + * {@inheritDoc} + */ + @Override + public TSPChromosome newFixedLengthChromosome(List representation) { + return new TSPChromosome(representation, cities); + } + + private double calculateTotalDistance(List permutedCities) { + double totalDistance = 0.0; + int index1 = 0; + int index2 = 0; + for (int i = 0; i < permutedCities.size(); i++) { + index1 = i; + index2 = (i == permutedCities.size() - 1) ? 0 : i + 1; + totalDistance += calculateNodeDistance(permutedCities.get(index1), permutedCities.get(index2)); + } + return totalDistance; + } + + private double calculateNodeDistance(City node1, City node2) { + return DistanceMatrix.getInstance().getDistance(node1, node2); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java new file mode 100644 index 0000000000..81ae597601 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/TSPOptimizerLegacy.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.legacy; + +import java.io.BufferedWriter; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.List; + +import org.apache.commons.math3.genetics.ElitisticListPopulation; +import org.apache.commons.math3.genetics.GeneticAlgorithm; +import org.apache.commons.math3.genetics.OnePointCrossover; +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.RandomKey; +import org.apache.commons.math3.genetics.RandomKeyMutation; +import org.apache.commons.math3.genetics.StoppingCondition; +import org.apache.commons.math3.genetics.TournamentSelection; +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.examples.ga.tsp.utils.Constants; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * This class represents the tsp optimizer based on legacy implementation of + * Genetic Algorithm. + */ +public class TSPOptimizerLegacy { + + /** + * MainThread.sleep(5000) method to initiate optimization. + * @param args arguments + */ + public static void main(String[] args) { + + final Population initPopulation = getInitialPopulation(Constants.CITIES); + final TSPOptimizerLegacy optimizer = new TSPOptimizerLegacy(); + + optimizer.optimize(initPopulation, Constants.CITIES); + + } + + /** + * Optimizes the tsp problem using legacy GA. + * @param initial initial population + * @param cities cities + */ + public void optimize(Population initial, List cities) { + + // initialize a new genetic algorithm + final GeneticAlgorithm ga = new GeneticAlgorithm(new OnePointCrossover(), Constants.CROSSOVER_RATE, + new RandomKeyMutation(), Constants.AVERAGE_MUTATION_RATE, + new TournamentSelection(Constants.TOURNAMENT_SIZE)); + + // stopping condition + final StoppingCondition stopCond = new UnchangedBestFitness( + Constants.GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS); + + // run the algorithm + final Population finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + @SuppressWarnings("unchecked") + final RandomKey bestFinal = (RandomKey) finalPopulation.getFittestChromosome(); + + try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, Constants.ENCODING))) { + writer.write("*********************************************"); + writer.newLine(); + writer.write("***********Optimization Result***************"); + writer.write(bestFinal.toString()); + } catch (IOException e) { + throw new GeneticException(e); + } + } + + private static Population getInitialPopulation(List cities) { + final Population simulationPopulation = new ElitisticListPopulation(Constants.POPULATION_SIZE, .25); + + for (int i = 0; i < Constants.POPULATION_SIZE; i++) { + simulationPopulation.addChromosome(new TSPChromosome(RandomKey.randomPermutation(cities.size()), cities)); + } + + return simulationPopulation; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java new file mode 100644 index 0000000000..44fe93a325 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/UnchangedBestFitness.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.tsp.legacy; + +import org.apache.commons.math3.genetics.Population; +import org.apache.commons.math3.genetics.StoppingCondition; + +/** + * This class represents the stopping condition based on unchanged best fitness. + */ +public class UnchangedBestFitness implements StoppingCondition { + + /** last best fitness. **/ + private double lastBestFitness = Double.MIN_VALUE; + + /** maximum number of generations evolved with unchanged best fitness. **/ + private final int maxGenerationsWithUnchangedBestFitness; + + /** generations having unchanged best fitness. **/ + private int generationsHavingUnchangedBestFitness; + + /** + * constructor. + * @param maxGenerationsWithUnchangedAverageFitness maximum number of + * generations evolved with + * unchanged best fitness + */ + public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) { + this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population population) { + final double currentBestFitness = population.getFittestChromosome().getFitness(); + + if (lastBestFitness == currentBestFitness) { + this.generationsHavingUnchangedBestFitness++; + if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) { + return true; + } + } else { + this.generationsHavingUnchangedBestFitness = 0; + lastBestFitness = currentBestFitness; + } + + return false; + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java new file mode 100644 index 0000000000..d46c6fd7b5 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/legacy/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.tsp.legacy; diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java new file mode 100644 index 0000000000..938b8f916a --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.tsp; diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java new file mode 100644 index 0000000000..c41cc31203 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/Constants.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.examples.ga.tsp.utils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; + +/** + * This class contains all required constants for this example. + */ +public final class Constants { + + /** size of population. **/ + public static final int POPULATION_SIZE = 100; + + /** size of tournament. **/ + public static final int TOURNAMENT_SIZE = 5; + + /** length of chromosome. **/ + public static final int CHROMOSOME_LENGTH = 14; + + /** rate of crossover. **/ + public static final double CROSSOVER_RATE = 1.0; + + /** rate of elitism. **/ + public static final double ELITISM_RATE = 0.25; + + /** rate of mutation. **/ + public static final double AVERAGE_MUTATION_RATE = 0.05; + + /** maximum number of generations with unchanged best fitness. **/ + public static final int GENERATION_COUNT_WITH_UNCHANGED_BEST_FUTNESS = 50; + + /** list of cities. **/ + public static final List CITIES = Collections.unmodifiableList( + Arrays.asList(new City[] {new City(1, 0, 0), new City(2, 1, 0), new City(3, 2, 0), new City(4, 3, 0), + new City(5, 3, 1), new City(6, 3, 2), new City(7, 3, 3), new City(8, 2, 3), new City(9, 1, 3), + new City(10, 0, 3), new City(11, 1, 2), new City(12, 2, 2), new City(13, 2, 1), new City(14, 1, 1)})); + + /** encoding for console logger. **/ + public static final String ENCODING = "UTF-8"; + + private Constants() { + + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java new file mode 100644 index 0000000000..701380bcea --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/GraphPlotter.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.examples.ga.tsp.utils; + +import java.awt.BorderLayout; + +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.apache.commons.math4.examples.ga.tsp.commons.City; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * This class represents the graph plotter during optimization. + */ +public class GraphPlotter extends JFrame implements ConvergenceListener> { + + /** + * Generated serialversionId. + */ + private static final long serialVersionUID = -5683904006424006584L; + + /** collection of 2-D series. **/ + private final XYSeriesCollection dataset = new XYSeriesCollection(); + + /** + * constructor. + * @param plotSubject subject of plot + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + */ + public GraphPlotter(String plotSubject, String xAxisLabel, String yAxisLabel) { + super(plotSubject); + + final JPanel chartPanel = createChartPanel(plotSubject, xAxisLabel, yAxisLabel); + add(chartPanel, BorderLayout.CENTER); + + setSize(640, 480); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setLocationRelativeTo(null); + + setVisible(true); + } + + /** + * Adds data point to graph. + * @param graphName name of graph + * @param generation generation, to be plotted along x axis + * @param value value, to be plotted along y axis + */ + private void addDataPoint(String graphName, int generation, double value) { + XYSeries series = null; + + if (!containsGraph(graphName)) { + series = new XYSeries(graphName); + dataset.addSeries(series); + } else { + series = dataset.getSeries(graphName); + } + series.add(generation, value); + + setVisible(true); + } + + /** + * Checks if the graph with the given name already exists. + * @param graphName name of the graph + * @return true/false + */ + @SuppressWarnings("unchecked") + private boolean containsGraph(String graphName) { + final List seriesList = dataset.getSeries(); + if (seriesList == null || seriesList.isEmpty()) { + return false; + } + for (XYSeries series : seriesList) { + if (series.getKey().compareTo(graphName) == 0) { + return true; + } + } + return false; + } + + /** + * Creates chart panel. + * @param chartTitle chart title + * @param xAxisLabel x axis label + * @param yAxisLabel y axis label + * @return panel + */ + private JPanel createChartPanel(String chartTitle, String xAxisLabel, String yAxisLabel) { + + final boolean showLegend = true; + final boolean createURL = false; + final boolean createTooltip = false; + + final JFreeChart chart = ChartFactory.createXYLineChart(chartTitle, xAxisLabel, yAxisLabel, dataset, + PlotOrientation.VERTICAL, showLegend, createTooltip, createURL); + final XYPlot plot = chart.getXYPlot(); + final XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(); + + plot.setRenderer(renderer); + + return new ChartPanel(chart); + + } + + /** + * {@inheritDoc} + */ + @Override + public void notify(int generation, Population> population) { + PopulationStatisticalSummary> populationStatisticalSummary = new PopulationStatisticalSummaryImpl<>( + population); + this.addDataPoint("Average", generation, populationStatisticalSummary.getMeanFitness()); + this.addDataPoint("Best", generation, populationStatisticalSummary.getMaxFitness()); + } + +} diff --git a/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java new file mode 100644 index 0000000000..5caadde4b3 --- /dev/null +++ b/commons-math-examples/examples-ga/examples-ga-tsp/src/main/java/org/apache/commons/math4/examples/ga/tsp/utils/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.examples.ga.tsp.utils; diff --git a/commons-math-examples/examples-ga/pom.xml b/commons-math-examples/examples-ga/pom.xml new file mode 100644 index 0000000000..84324837ad --- /dev/null +++ b/commons-math-examples/examples-ga/pom.xml @@ -0,0 +1,56 @@ + + + + 4.0.0 + + org.apache.commons + commons-math-examples + 4.0-SNAPSHOT + + examples-ga + pom + examples-genetic-algorithm + + + + ${basedir}/../.. + + + + + org.apache.commons + commons-math-ga + 4.0-SNAPSHOT + + + org.apache.commons + commons-math3 + + + org.jfree + jfreechart + 1.5.3 + + + + examples-ga-math-functions + examples-ga-tsp + + \ No newline at end of file diff --git a/commons-math-examples/pom.xml b/commons-math-examples/pom.xml index 595491f5c0..7ed9e7e6f2 100644 --- a/commons-math-examples/pom.xml +++ b/commons-math-examples/pom.xml @@ -65,6 +65,12 @@ ${math.version} + + org.apache.commons + commons-math4-ga + ${math.version} + + org.apache.commons commons-math4-legacy @@ -158,6 +164,7 @@ examples-sofm + examples-ga diff --git a/commons-math-ga/LICENCE b/commons-math-ga/LICENCE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/commons-math-ga/LICENCE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/commons-math-ga/NOTICE b/commons-math-ga/NOTICE new file mode 100644 index 0000000000..28031e8ef9 --- /dev/null +++ b/commons-math-ga/NOTICE @@ -0,0 +1,5 @@ +Apache Commons Math +Copyright 2001-2022 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). diff --git a/commons-math-ga/pom.xml b/commons-math-ga/pom.xml new file mode 100644 index 0000000000..a2a3689ea1 --- /dev/null +++ b/commons-math-ga/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + + org.apache.commons + commons-math-parent + 4.0-SNAPSHOT + + commons-math-ga + genetic algorithm + + + + + + org.apache.commons.math4.ga + + org.apache.commons.math4.ga + + org.apache.commons.math4.ga + + ${basedir}/.. + 1.7.32 + + + + + org.apache.commons + commons-numbers-core + + + org.apache.commons + commons-rng-simple + + + org.slf4j + slf4j-api + ${slf4jVersion} + + + + \ No newline at end of file diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java new file mode 100644 index 0000000000..7f9359f8ec --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AbstractGeneticAlgorithm.java @@ -0,0 +1,180 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class represents an abstraction for all Genetic algorithm implementation + * comprising the basic properties and operations. + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractGeneticAlgorithm

{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** the crossover policy used by the algorithm. */ + private final CrossoverPolicy

crossoverPolicy; + + /** the mutation policy used by the algorithm. */ + private final MutationPolicy

mutationPolicy; + + /** the selection policy used by the algorithm. */ + private final SelectionPolicy

selectionPolicy; + + /** + * the number of generations evolved to reach {@link StoppingCondition} in the + * last run. + */ + private int generationsEvolved; + + /** The elitism rate having default value of .25. */ + private double elitismRate = .25; + + /** + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param mutationPolicy The {@link MutationPolicy} + * @param selectionPolicy The {@link SelectionPolicy} + */ + protected AbstractGeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, + final MutationPolicy

mutationPolicy, + final SelectionPolicy

selectionPolicy) { + this.crossoverPolicy = crossoverPolicy; + this.mutationPolicy = mutationPolicy; + this.selectionPolicy = selectionPolicy; + } + + /** + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param mutationPolicy The {@link MutationPolicy} + * @param selectionPolicy The {@link SelectionPolicy} + * @param elitismRate The elitism rate + */ + protected AbstractGeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, + final MutationPolicy

mutationPolicy, + final SelectionPolicy

selectionPolicy, + double elitismRate) { + this.crossoverPolicy = crossoverPolicy; + this.mutationPolicy = mutationPolicy; + this.selectionPolicy = selectionPolicy; + this.elitismRate = elitismRate; + } + + /** + * Returns the crossover policy. + * @return crossover policy + */ + public CrossoverPolicy

getCrossoverPolicy() { + return crossoverPolicy; + } + + /** + * Returns the mutation policy. + * @return mutation policy + */ + public MutationPolicy

getMutationPolicy() { + return mutationPolicy; + } + + /** + * Returns the selection policy. + * @return selection policy + */ + public SelectionPolicy

getSelectionPolicy() { + return selectionPolicy; + } + + /** + * Returns the number of generations evolved to reach {@link StoppingCondition} + * in the last run. + * + * @return number of generations evolved + * @since 2.1 + */ + public int getGenerationsEvolved() { + return generationsEvolved; + } + + /** + * Evolve the given population. Evolution stops when the stopping condition is + * satisfied. Updates the {@link #getGenerationsEvolved() generationsEvolved} + * property with the number of generations evolved before the StoppingCondition + * is satisfied. + * + * @param initial the initial, seed population. + * @param condition the stopping condition used to stop evolution. + * @return the population that satisfies the stopping condition. + */ + public Population

evolve(final Population

initial, final StoppingCondition

condition) { + Population

current = initial; + + LOGGER.info("Starting evolution process."); + // check if stopping condition is satisfied otherwise produce the next + // generation of population. + while (!condition.isSatisfied(current)) { + // notify interested listener + ConvergenceListenerRegistry.

getInstance().notifyAll(generationsEvolved, current); + + current = nextGeneration(current); + this.generationsEvolved++; + } + LOGGER.info("Population convergence achieved after generations: " + generationsEvolved); + + return current; + } + + /** + * Evolve the given population into the next generation. + *

    + *
  1. Get nextGeneration population to fill from current + * generation, using its nextGeneration method
  2. + *
  3. Loop until new generation is filled: + *
      + *
    • Apply configured SelectionPolicy to select a pair of parents from + * current,
    • + *
    • apply configured {@link CrossoverPolicy} to parents,
    • + *
    • apply configured {@link MutationPolicy} to each of the offspring
    • + *
    • Add offspring individually to nextGeneration, space permitting
    • + *
    + *
  4. + *
  5. Return nextGeneration
  6. + *
+ * + * @param current the current population + * @return the population for the next generation. + */ + protected abstract Population

nextGeneration(Population

current); + + /** + * Returns the elitism rate. + * @return elitism rate + */ + public double getElitismRate() { + return elitismRate; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java new file mode 100644 index 0000000000..07e891316f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/AdaptiveGeneticAlgorithm.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.crossover.rategenerator.CrossoverRateGenerator; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.mutation.rategenerator.MutationRateGenerator; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * An implementation of Genetic Algorithm. The probability of crossover and + * mutation is generated in an adaptive way. This implementation allows + * configuration of dynamic crossover and mutation rate generator along with + * crossover policy, mutation policy, selection policy and optionally elitism + * rate. + * @param

phenotype of chromosome + */ +public class AdaptiveGeneticAlgorithm

extends AbstractGeneticAlgorithm

{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** The crossover rate generator. **/ + private final CrossoverRateGenerator

crossoverRateGenerator; + + /** The mutation rate generator. **/ + private final MutationRateGenerator

mutationRateGenerator; + + /** + * @param crossoverPolicy crossover policy + * @param crossoverProbabilityGenerator crossover probability generator + * @param mutationPolicy mutation policy + * @param mutationProbabilityGenerator mutation probability generator + * @param selectionPolicy selection policy + */ + public AdaptiveGeneticAlgorithm(CrossoverPolicy

crossoverPolicy, + CrossoverRateGenerator

crossoverProbabilityGenerator, + MutationPolicy

mutationPolicy, + MutationRateGenerator

mutationProbabilityGenerator, + SelectionPolicy

selectionPolicy) { + super(crossoverPolicy, mutationPolicy, selectionPolicy); + this.crossoverRateGenerator = crossoverProbabilityGenerator; + this.mutationRateGenerator = mutationProbabilityGenerator; + } + + /** + * @param crossoverPolicy crossover policy + * @param crossoverProbabilityGenerator crossover probability generator + * @param mutationPolicy mutation policy + * @param mutationProbabilityGenerator mutation probability generator + * @param selectionPolicy selection policy + * @param elitismRate elitism rate + */ + public AdaptiveGeneticAlgorithm(CrossoverPolicy

crossoverPolicy, + CrossoverRateGenerator

crossoverProbabilityGenerator, + MutationPolicy

mutationPolicy, + MutationRateGenerator

mutationProbabilityGenerator, + SelectionPolicy

selectionPolicy, + double elitismRate) { + super(crossoverPolicy, mutationPolicy, selectionPolicy, elitismRate); + this.crossoverRateGenerator = crossoverProbabilityGenerator; + this.mutationRateGenerator = mutationProbabilityGenerator; + } + + /** + * {@inheritDoc} + */ + @Override + protected Population

nextGeneration(Population

current) { + + LOGGER.debug("Reproducing next generation."); + + // compute statistics of current generation chromosomes. + PopulationStatisticalSummary

populationStats = new PopulationStatisticalSummaryImpl<>(current); + + // Initialize the next generation with elit chromosomes from previous + // generation. + final Population

nextGeneration = current.nextGeneration(getElitismRate()); + + LOGGER.debug( + "No of Elite chromosomes selected from previous generation: " + nextGeneration.getPopulationSize()); + + final int maxOffspringCount = nextGeneration.getPopulationLimit() - nextGeneration.getPopulationSize(); + + // Initialize an empty population for offsprings. + final Population

offspringPopulation = current.nextGeneration(0); + + // perform crossover and generate new offsprings + while (offspringPopulation.getPopulationSize() < maxOffspringCount) { + + // select parent chromosomes + ChromosomePair

pair = getSelectionPolicy().select(current); + LOGGER.debug("Selected Chromosomes: \r\n" + pair.toString()); + + final double crossoverRate = crossoverRateGenerator.generate(pair.getFirst(), pair.getSecond(), + populationStats, getGenerationsEvolved()); + // apply crossover policy to create two offspring + pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond(), crossoverRate); + LOGGER.debug("Offsprings after Crossover: \r\n" + pair.toString()); + + // add the first chromosome to the population + offspringPopulation.addChromosome(pair.getFirst()); + // is there still a place for the second chromosome? + if (offspringPopulation.getPopulationSize() < maxOffspringCount) { + // add the second chromosome to the population + offspringPopulation.addChromosome(pair.getSecond()); + } + } + LOGGER.debug("Performing adaptive mutation of offsprings."); + + // recompute the statistics of the offspring population. + populationStats = new PopulationStatisticalSummaryImpl<>(offspringPopulation); + + // apply mutation policy to the offspring chromosomes and add the mutated + // chromosomes to next generation. + for (Chromosome

chromosome : offspringPopulation) { + nextGeneration.addChromosome(getMutationPolicy().mutate(chromosome, + mutationRateGenerator.generate(chromosome, populationStats, getGenerationsEvolved()))); + } + LOGGER.debug("New Generation: \r\n" + nextGeneration.toString()); + + return nextGeneration; + } + + /** + * Returns crossover probability generator. + * @return crossover probability generator + */ + public CrossoverRateGenerator

getCrossoverProbabilityGenerator() { + return crossoverRateGenerator; + } + + /** + * Returns mutation probability generator. + * @return mutation probability generator + */ + public MutationRateGenerator

getMutationProbabilityGenerator() { + return mutationRateGenerator; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java new file mode 100644 index 0000000000..94e320d42a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/GeneticAlgorithm.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.crossover.CrossoverPolicy; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.mutation.MutationPolicy; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.SelectionPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Implementation of a genetic algorithm. All factors that govern the operation + * of the algorithm can be configured for a specific problem. + * + * @param

phenotype of chromosome + * @since 4.0 + */ +public class GeneticAlgorithm

extends AbstractGeneticAlgorithm

{ + + /** instance of logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractGeneticAlgorithm.class); + + /** crossover rate string. **/ + private static final String CROSSOVER_RATE = "CROSSOVER_RATE"; + + /** mutation rate string. **/ + private static final String MUTATION_RATE = "MUTATION_RATE"; + + /** the rate of crossover for the algorithm. */ + private final double crossoverRate; + + /** the rate of mutation for the algorithm. */ + private final double mutationRate; + + /** + * Create a new genetic algorithm. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) + * @param mutationPolicy The {@link MutationPolicy} + * @param mutationRate The mutation rate as a percentage (0-1 inclusive) + * @param selectionPolicy The {@link SelectionPolicy} + */ + public GeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, + final double crossoverRate, + final MutationPolicy

mutationPolicy, + final double mutationRate, + final SelectionPolicy

selectionPolicy) { + super(crossoverPolicy, mutationPolicy, selectionPolicy); + + checkValidity(crossoverRate, mutationRate); + this.crossoverRate = crossoverRate; + this.mutationRate = mutationRate; + } + + /** + * Create a new genetic algorithm. + * @param crossoverPolicy The {@link CrossoverPolicy} + * @param crossoverRate The crossover rate as a percentage (0-1 inclusive) + * @param mutationPolicy The {@link MutationPolicy} + * @param mutationRate The mutation rate as a percentage (0-1 inclusive) + * @param selectionPolicy The {@link SelectionPolicy} + * @param elitismRate The rate of elitism + */ + public GeneticAlgorithm(final CrossoverPolicy

crossoverPolicy, + final double crossoverRate, + final MutationPolicy

mutationPolicy, + final double mutationRate, + final SelectionPolicy

selectionPolicy, + final double elitismRate) { + super(crossoverPolicy, mutationPolicy, selectionPolicy, elitismRate); + + checkValidity(crossoverRate, mutationRate); + this.crossoverRate = crossoverRate; + this.mutationRate = mutationRate; + } + + private void checkValidity(final double crossoverRateInput, final double inputMutationRate) { + if (crossoverRateInput < 0 || crossoverRateInput > 1) { + throw new GeneticException(GeneticException.OUT_OF_RANGE, crossoverRateInput, CROSSOVER_RATE, 0, 1); + } + if (inputMutationRate < 0 || inputMutationRate > 1) { + throw new GeneticException(GeneticException.OUT_OF_RANGE, inputMutationRate, MUTATION_RATE, 0, 1); + } + } + + /** + * Evolve the given population into the next generation. + *

    + *
  1. Get nextGeneration population to fill from current + * generation, using its nextGeneration method
  2. + *
  3. Loop until new generation is filled: + *
      + *
    • Apply configured SelectionPolicy to select a pair of parents from + * current
    • + *
    • With probability = {@link #getCrossoverRate()}, apply configured + * {@link CrossoverPolicy} to parents
    • + *
    • With probability = {@link #getMutationRate()}, apply configured + * {@link MutationPolicy} to each of the offspring
    • + *
    • Add offspring individually to nextGeneration, space permitting
    • + *
    + *
  4. + *
  5. Return nextGeneration
  6. + *
+ * + * @param current the current population. + * @return the population for the next generation. + */ + @Override + protected Population

nextGeneration(final Population

current) { + + LOGGER.debug("Reproducing next generation."); + final Population

nextGeneration = current.nextGeneration(getElitismRate()); + + while (nextGeneration.getPopulationSize() < nextGeneration.getPopulationLimit() - 1) { + + // select parent chromosomes + ChromosomePair

pair = getSelectionPolicy().select(current); + LOGGER.debug("Selected Chromosomes: \r\n" + pair.toString()); + + // apply crossover policy to create two offspring + pair = getCrossoverPolicy().crossover(pair.getFirst(), pair.getSecond(), crossoverRate); + LOGGER.debug("Offsprings after Crossover: \r\n" + pair.toString()); + + // apply mutation policy to the chromosomes + pair = new ChromosomePair<>(getMutationPolicy().mutate(pair.getFirst(), mutationRate), + getMutationPolicy().mutate(pair.getSecond(), mutationRate)); + LOGGER.debug("Offsprings after Mutation: \r\n" + pair.toString()); + + // add the chromosomes to the population + nextGeneration.addChromosome(pair.getFirst()); + nextGeneration.addChromosome(pair.getSecond()); + } + LOGGER.debug("New Generation : \r\n" + nextGeneration.toString()); + + return nextGeneration; + } + + /** + * Returns the crossover rate. + * @return crossover rate + */ + public double getCrossoverRate() { + return crossoverRate; + } + + /** + * Returns the mutation rate. + * @return mutation rate + */ + public double getMutationRate() { + return mutationRate; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java new file mode 100644 index 0000000000..4f61ace342 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractChromosome.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.Objects; +import java.util.UUID; + +import org.apache.commons.math4.ga.decoder.Decoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * Individual in a population. Chromosomes are compared based on their fitness. + *

+ * The chromosomes are IMMUTABLE, and so their fitness is also immutable and + * therefore it can be cached. + * + * @param

The phenotype of chromosome. The type should override hashCode() + * and equals() methods. + * @since 4.0 + */ +public abstract class AbstractChromosome

implements Chromosome

{ + + /** Value assigned when no fitness has been computed yet. */ + private static final double NO_FITNESS = Double.NEGATIVE_INFINITY; + + /** Cached value of the fitness of this chromosome. */ + private double fitness = NO_FITNESS; + + /** Fitness function to evaluate fitness of chromosome. **/ + private final FitnessFunction

fitnessFunction; + + /** decoder to deode the chromosome's genotype representation. **/ + private final Decoder

decoder; + + /** Id of chromosome. **/ + private final String id; + + /** + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + protected AbstractChromosome(final FitnessFunction

fitnessFunction, final Decoder

decoder) { + this.fitnessFunction = Objects.requireNonNull(fitnessFunction); + this.decoder = Objects.requireNonNull(decoder); + this.id = UUID.randomUUID().toString(); + } + + /** + * returns fitness function. + * @return fitnessFunction + */ + protected FitnessFunction

getFitnessFunction() { + return fitnessFunction; + } + + /** + * Returns the decoder instance. + * @return decoder + */ + public Decoder

getDecoder() { + return decoder; + } + + /** + * Returns id of chromosome. + * @return id + */ + @Override + public String getId() { + return id; + } + + /** + * Access the fitness of this chromosome. The bigger the fitness, the better the + * chromosome. + *

+ * Computation of fitness is usually very time-consuming task, therefore the + * fitness is cached. + * @return the fitness + */ + @Override + public double evaluate() { + if (this.fitness == NO_FITNESS) { + // no cache - compute the fitness + this.fitness = fitnessFunction.compute(decode()); + } + return this.fitness; + } + + /** + * Decodes the chromosome genotype and returns the phenotype. + * @return phenotype + */ + @Override + public P decode() { + return this.decoder.decode(this); + } + + /** + * Compares two chromosomes based on their fitness. The bigger the fitness, the + * better the chromosome. + * @param another another chromosome to compare + * @return + *

    + *
  • -1 if another is better than this
  • + *
  • 1 if another is worse than this
  • + *
  • 0 if the two chromosomes have the same fitness
  • + *
+ */ + @Override + public int compareTo(final Chromosome

another) { + return Double.compare(evaluate(), another.evaluate()); + } + + /** + * Returns true iff another has the same + * representation and therefore the same fitness. By default, it returns false + * -- override it in your implementation if you need it. + * @param another chromosome to compare + * @return true if another is equivalent to this chromosome + */ + public boolean isSame(final AbstractChromosome

another) { + final P decodedChromosome = decode(); + final P otherDecodedChromosome = another.decode(); + return decodedChromosome.equals(otherDecodedChromosome); + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return String.format("(f=%s %s)", evaluate(), decode()); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java new file mode 100644 index 0000000000..9e1676d4f7 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/AbstractListChromosome.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; + +/** + * This class represents an abstract chromosome containing an immutable list of + * allele/genes. + * @param type of the allele/gene in the representation list. T should be + * immutable. + * @param

phenotype of chromosome + * @since 2.0 + */ +public abstract class AbstractListChromosome extends AbstractChromosome

{ + + /** List of allele/genes. */ + private final List representation; + + /** + * @param representation The representation of chromosome genotype as + * {@link List} of generic T + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder An instance of {@link AbstractListChromosomeDecoder}, + * to decode list chromosome. + */ + protected AbstractListChromosome(final List representation, + final FitnessFunction

fitnessFunction, + final AbstractListChromosomeDecoder decoder) { + this(representation, true, fitnessFunction, decoder); + } + + /** + * @param representation The representation of chromosome genotype as an array + * of generic T + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder An instance of {@link AbstractListChromosomeDecoder}, + * to decode list chromosome. + */ + protected AbstractListChromosome(final T[] representation, + FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder) { + this(Arrays.asList(representation), fitnessFunction, decoder); + } + + /** + * @param representation Internal representation of chromosome genotype as an + * array of generic T + * @param copyList if {@code true}, the representation will be copied, + * otherwise it will be referenced. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The instance of {@link AbstractListChromosomeDecoder} + */ + protected AbstractListChromosome(final List representation, + final boolean copyList, + final FitnessFunction

fitnessFunction, + final AbstractListChromosomeDecoder decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + this.representation = Collections.unmodifiableList(copyList ? new ArrayList<>(representation) : representation); + } + + /** + * Returns the (immutable) inner representation of the chromosome. + * @return the representation of the chromosome + */ + public List getRepresentation() { + return representation; + } + + /** + * Returns the length of the chromosome. + * @return the length of the chromosome + */ + public int getLength() { + return getRepresentation().size(); + } + + /** + * returns the decoder. + * @return decoder + */ + @SuppressWarnings("unchecked") + @Override + public AbstractListChromosomeDecoder getDecoder() { + return (AbstractListChromosomeDecoder) super.getDecoder(); + } + + /** + * Creates a new instance of the same class as this is, with a + * given arrayRepresentation. This is needed in crossover and + * mutation operators, where we need a new instance of the same class, but with + * different array representation. + *

+ * Usually, this method just calls a constructor of the class. + * + * @param chromosomeRepresentation the inner array representation of the new + * chromosome. + * @return new instance extended from FixedLengthChromosome with the given + * arrayRepresentation + */ + public abstract AbstractListChromosome newChromosome(List chromosomeRepresentation); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java new file mode 100644 index 0000000000..a9ef839a70 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/BinaryChromosome.java @@ -0,0 +1,303 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.List; + +import java.util.Objects; + +import org.apache.commons.math4.ga.decoder.Decoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; + +/** + * BinaryChromosome represented by a vector of 0s and 1s. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class BinaryChromosome

extends AbstractChromosome

{ + + /** + * maximum allowed length of binary chromosome. + */ + public static final long MAX_LENGTH = Integer.MAX_VALUE; + + /** + * length of binary chromosome. + */ + private final long length; + + /** + * binary representation of chromosome. + */ + private final long[] representation; + + /** + * @param representation Internal representation of chromosome. + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(List representation, + long length, + FitnessFunction

fitnessFunction, + Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + checkMaximumLength(length); + this.length = length; + this.representation = new long[representation.size()]; + for (int i = 0; i < representation.size(); i++) { + this.representation[i] = representation.get(i); + } + } + + /** + * @param representation Internal representation of chromosome. + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(Long[] representation, + long length, + FitnessFunction

fitnessFunction, + Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + checkMaximumLength(length); + this.length = length; + this.representation = new long[representation.length]; + for (int i = 0; i < representation.length; i++) { + this.representation[i] = representation[i]; + } + } + + /** + * @param inputRepresentation Internal representation of chromosome. + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(long[] inputRepresentation, + long length, + FitnessFunction

fitnessFunction, + Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(inputRepresentation); + checkMaximumLength(length); + if (length <= (inputRepresentation.length - 1) * Long.SIZE || length > inputRepresentation.length * Long.SIZE) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "provided length does not match expected representation"); + } + this.length = length; + this.representation = new long[inputRepresentation.length]; + System.arraycopy(inputRepresentation, 0, representation, 0, inputRepresentation.length); + } + + /** + * @param representation Internal representation of chromosome. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + */ + public BinaryChromosome(String representation, FitnessFunction

fitnessFunction, Decoder

decoder) { + super(fitnessFunction, decoder); + Objects.requireNonNull(representation); + this.length = representation.length(); + this.representation = encode(representation); + } + + /** + * Checks the input chromosome length against predefined maximum length. + * @param chromosomeLength input chromsome length + */ + protected void checkMaximumLength(long chromosomeLength) { + if (chromosomeLength > MAX_LENGTH) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "length exceeded the max length " + MAX_LENGTH); + } + } + + /** + * Validates the string representation. + * @param stringRepresentation binary string representation of chromosome + */ + private void validateStringRepresentation(String stringRepresentation) { + char allele = '\0'; + final int chromosomeLength = stringRepresentation.length(); + for (int i = 0; i < chromosomeLength; i++) { + if ((allele = stringRepresentation.charAt(i)) != '0' && allele != '1') { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "Only 0 or 1 are acceptable as characters."); + } + } + } + + /** + * encodes the binary string representation as an array of long. + * @param stringRepresentation binary string + * @return encoded representation + */ + private long[] encode(String stringRepresentation) { + validateStringRepresentation(stringRepresentation); + final int chromosomeLength = stringRepresentation.length(); + final int arraySize = (int) Math.ceil(chromosomeLength / (double) Long.SIZE); + final long[] encodedRepresentation = new long[arraySize]; + final int offset = (int) (chromosomeLength % Long.SIZE == 0 ? 0 : Long.SIZE - chromosomeLength % Long.SIZE); + encodedRepresentation[0] = Long.parseUnsignedLong(stringRepresentation.substring(0, Long.SIZE - offset), 2); + for (int i = Long.SIZE - offset, j = 1; i < chromosomeLength; i += Long.SIZE, j++) { + encodedRepresentation[j] = Long.parseUnsignedLong(stringRepresentation.substring(i, i + Long.SIZE), 2); + } + return encodedRepresentation; + } + + /** + * Returns the chromosome length. + * @return length + */ + public long getLength() { + return length; + } + + /** + * Returns the binary representation. + * @return representation + */ + public long[] getRepresentation() { + final long[] clonedRepresentation = new long[representation.length]; + System.arraycopy(representation, 0, clonedRepresentation, 0, representation.length); + return clonedRepresentation; + } + + /** + * Returns the binary string representation of the chromosome. + * @return the string representation + */ + public String getStringRepresentation() { + if (length > Integer.MAX_VALUE) { + throw new GeneticException(GeneticException.LENGTH_TOO_LARGE, length); + } + return getStringRepresentation(0, length); + } + + /** + * Returns the binary string representation of the chromosome alleles from + * start(inclusive) to end(exclusive) index. + * @param start start allele/gene index(inclusive) + * @param end end allele/gene index(exclusive) + * @return the string representation + */ + public String getStringRepresentation(long start, long end) { + if (start >= end) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "start " + start + " is greater than end " + end); + } + if (end - start > Integer.MAX_VALUE) { + throw new GeneticException(GeneticException.LENGTH_TOO_LARGE, end - start); + } + final int offset = (int) (length % Long.SIZE == 0 ? 0 : Long.SIZE - length % Long.SIZE); + final long offsettedStart = offset + start; + final long offsettedEnd = offset + end; + final int startAlleleBlockIndex = (int) (offsettedStart / Long.SIZE); + final int endAlleleBlockIndex = (int) (offsettedEnd / Long.SIZE); + final int startAlleleElementIndex = (int) (offsettedStart % Long.SIZE); + final int endAlleleElementIndex = (int) (offsettedEnd % Long.SIZE); + + if (startAlleleBlockIndex == endAlleleBlockIndex) { + return getAlleleBlockString(startAlleleBlockIndex).substring(startAlleleElementIndex, + endAlleleElementIndex); + } else { + final StringBuilder allelesStrRepresentation = new StringBuilder(); + + // extract string representation of first allele block. + allelesStrRepresentation + .append(getAlleleBlockString(startAlleleBlockIndex).substring(startAlleleElementIndex)); + + // extract string representation of all allele blocks except first and last. + for (int i = startAlleleBlockIndex + 1; i < endAlleleBlockIndex; i++) { + allelesStrRepresentation.append(getAlleleBlockString(i)); + } + + // extract string representation from last allele block if end allele index != + // 0. + if (endAlleleElementIndex != 0) { + allelesStrRepresentation + .append(getAlleleBlockString(endAlleleBlockIndex).substring(0, endAlleleElementIndex)); + } + + return allelesStrRepresentation.toString(); + } + } + + /** + * Returns the allele block as binary string representation. + * @param alleleBlockIndex index of allele block i.e. array element + * @return the string representation + */ + private String getAlleleBlockString(final int alleleBlockIndex) { + return prepareZeroPrefix(representation[alleleBlockIndex] == 0 ? Long.SIZE - 1 : + Long.numberOfLeadingZeros(representation[alleleBlockIndex])) + + Long.toUnsignedString(representation[alleleBlockIndex], 2); + } + + /** + * Prepares zero prefix for binary chromosome. + * @param count number of zeros + * @return prefix + */ + private String prepareZeroPrefix(int count) { + final StringBuilder zeroPrefix = new StringBuilder(); + for (int i = 0; i < count; i++) { + zeroPrefix.append('0'); + } + return zeroPrefix.toString(); + } + + /** + * {@inheritDoc} + */ + @Override + public FitnessFunction

getFitnessFunction() { + return super.getFitnessFunction(); + } + + /** + * Creates a new chromosome with provided parameters. + * @param chromosomeRepresentation the representation + * @param chromosomeLength length of chromosome + * @return chromosome + */ + public BinaryChromosome

newChromosome(long[] chromosomeRepresentation, long chromosomeLength) { + return new BinaryChromosome

(chromosomeRepresentation, chromosomeLength, getFitnessFunction(), getDecoder()); + } + + /** + * Creates an instance of Binary Chromosome with random binary representation. + * @param

phenotype fo chromosome + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link Decoder} + * @return a binary chromosome + */ + public static

BinaryChromosome

randomChromosome(int length, + FitnessFunction

fitnessFunction, + Decoder

decoder) { + return new BinaryChromosome

(ChromosomeRepresentationUtils.randomBinaryRepresentation(length), length, + fitnessFunction, decoder); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java new file mode 100644 index 0000000000..b29461cba5 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/Chromosome.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.chromosome; + +/** + * This abstraction represents a chromosome. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface Chromosome

extends Comparable> { + + /** + * Access the fitness of this chromosome. The bigger the fitness, the better the + * chromosome. + *

+ * Computation of fitness is usually very time-consuming task, therefore the + * fitness is cached. + * @return the fitness + */ + double evaluate(); + + /** + * Decodes the chromosome genotype and returns the phenotype. + * @return phenotype + */ + P decode(); + + /** + * Returns unique Id of chromosome. + * @return id + */ + String getId(); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java new file mode 100644 index 0000000000..1157a00822 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/ChromosomePair.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +/** + * A pair of {@link Chromosome} objects. + * @param

phenotype of chromosome + * @since 2.0 + */ +public class ChromosomePair

{ + + /** the first chromosome in the pair. */ + private final Chromosome

first; + + /** the second chromosome in the pair. */ + private final Chromosome

second; + + /** + * Create a chromosome pair. + * @param c1 the first chromosome. + * @param c2 the second chromosome. + */ + public ChromosomePair(final Chromosome

c1, final Chromosome

c2) { + super(); + first = c1; + second = c2; + } + + /** + * Access the first chromosome. + * + * @return the first chromosome. + */ + public Chromosome

getFirst() { + return first; + } + + /** + * Access the second chromosome. + * + * @return the second chromosome. + */ + public Chromosome

getSecond() { + return second; + } + + /** {@inheritDoc} */ + @Override + public String toString() { + return String.format("(%s,%s)", getFirst(), getSecond()); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java new file mode 100644 index 0000000000..91fcfd25c3 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosome.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.List; + +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; + +/** + * Chromosome represented by a list of integral values. The acceptable integral + * values should belong to the range min(inclusive) to max(exclusive). + * @param

phenotype of chromosome + * @since 4.0 + */ +public class IntegralValuedChromosome

extends AbstractListChromosome { + + /** minimum acceptable value of allele. **/ + private final int min; + + /** maximum acceptable value of allele. **/ + private final int max; + + /** + * @param representation Internal representation of chromosome. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public IntegralValuedChromosome(List representation, + FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, + int min, + int max) { + super(representation, fitnessFunction, decoder); + this.min = min; + this.max = max; + checkValidity(); + } + + /** + * @param representation Internal representation of chromosome. + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public IntegralValuedChromosome(Integer[] representation, + FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, + int min, + int max) { + super(representation, fitnessFunction, decoder); + this.min = min; + this.max = max; + checkValidity(); + } + + /** + * Returns the minimum acceptable value of allele. + * @return minimum value + */ + public int getMin() { + return min; + } + + /** + * Returns the maximum acceptable value of allele. + * @return maximum value + */ + public int getMax() { + return max; + } + + /** + * Asserts that representation can represent a valid chromosome. + */ + private void checkValidity() { + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + for (int i : getRepresentation()) { + if (i < min || i >= max) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public IntegralValuedChromosome

newChromosome(List chromosomeRepresentation) { + return new IntegralValuedChromosome<>(chromosomeRepresentation, getFitnessFunction(), getDecoder(), this.min, + this.max); + } + + /** + * Creates an instance of Integral valued Chromosome with random binary + * representation. + * @param

phenotype fo chromosome + * @param length length of chromosome + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + * @return an integral-valued chromosome + */ + public static

IntegralValuedChromosome

randomChromosome(int length, + FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, + int min, + int max) { + return new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(length, min, max), fitnessFunction, decoder, + min, max); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java new file mode 100644 index 0000000000..603a1e2130 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosome.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; + +/** + * DoubleEncodedChromosome is used for representing chromosome encoded as + * Double. It is a vector of a fixed length of real numbers.The acceptable real + * values should belong to the range min(inclusive) to max(exclusive). + *

+ * @param

phenotype of chromosome + * @since 4.0 + */ +public class RealValuedChromosome

extends AbstractListChromosome { + + /** minimum acceptable value of allele. **/ + private final double min; + + /** maximum acceptable value of allele. **/ + private final double max; + + /** + * @param representation an array of real values + * @param fitnessFunction the fitness function + * @param decoder the {@link AbstractListChromosomeDecoder} + */ + public RealValuedChromosome(final List representation, + FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder) { + super(representation, fitnessFunction, decoder); + this.min = 0; + this.max = 1d; + checkValidity(); + } + + /** + * @param representation an array of real values + * @param fitnessFunction the fitness function + * @param decoder the {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public RealValuedChromosome(final List representation, + FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, + double min, + double max) { + super(representation, fitnessFunction, decoder); + this.min = min; + this.max = max; + checkValidity(); + } + + /** + * @param representation Internal representation of chromosome as genotype + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + */ + public RealValuedChromosome(final Double[] representation, + FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder) { + this(Arrays.asList(representation), fitnessFunction, decoder); + } + + /** + * @param representation Internal representation of chromosome as genotype + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public RealValuedChromosome(final Double[] representation, + FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, + double min, + double max) { + this(Arrays.asList(representation), fitnessFunction, decoder, min, max); + } + + /** + * Return the minimum allele value. + * @return minimum + */ + public double getMin() { + return min; + } + + /** + * Returns the maximum allele value. + * @return maximum + */ + public double getMax() { + return max; + } + + /** + * Asserts that representation can represent a valid chromosome. + */ + private void checkValidity() { + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + for (double i : getRepresentation()) { + if (i < min || i >= max) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, i); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public RealValuedChromosome

newChromosome(List chromosomeRepresentation) { + return new RealValuedChromosome<>(chromosomeRepresentation, getFitnessFunction(), getDecoder(), this.min, + this.max); + } + + /** + * Creates an instance of RealValued chromosome with randomly generated + * representation. + * @param

phenotype of chromosome + * @param length length of chromosome genotype + * @param fitnessFunction The {@link FitnessFunction} + * @param decoder The {@link AbstractListChromosomeDecoder} + * @param min minimum inclusive value generated as allele + * @param max maximum exclusive value generated as allele + * @return A real-valued chromosome + */ + public static

RealValuedChromosome

randomChromosome(int length, + FitnessFunction

fitnessFunction, + AbstractListChromosomeDecoder decoder, + double min, + double max) { + return new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(length, min, max), + fitnessFunction, decoder, min, max); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java new file mode 100644 index 0000000000..196ddd9e25 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/chromosome/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.chromosome; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java new file mode 100644 index 0000000000..8fda02bd0c --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedElapsedTime.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergence; + +import java.util.concurrent.TimeUnit; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.Population; + +/** + * Stops after a fixed amount of time has elapsed. + *

+ * The first time {@link #isSatisfied(Population)} is invoked, the end time of + * the evolution is determined based on the provided maxTime value. + * Once the elapsed time reaches the configured maxTime value, + * {@link #isSatisfied(Population)} returns true. + * + * @param

phenotype of chromosome + * @since 3.1 + */ +public class FixedElapsedTime

implements StoppingCondition

{ + + /** Maximum allowed time period (in nanoseconds). */ + private final long maxTimePeriod; + + /** The predetermined termination time (stopping condition). */ + private long endTime = -1; + + /** + * Create a new {@link FixedElapsedTime} instance. + * + * @param maxTime maximum number of seconds generations are allowed to evolve + */ + public FixedElapsedTime(final long maxTime) { + this(maxTime, TimeUnit.SECONDS); + } + + /** + * Create a new {@link FixedElapsedTime} instance. + * + * @param maxTime maximum time generations are allowed to evolve + * @param unit {@link TimeUnit} of the maxTime argument + */ + public FixedElapsedTime(final long maxTime, final TimeUnit unit) { + if (maxTime < 0) { + throw new GeneticException(GeneticException.TOO_SMALL, maxTime, 0); + } + maxTimePeriod = unit.toNanos(maxTime); + } + + /** + * Determine whether or not the maximum allowed time has passed. The termination + * time is determined after the first generation. + * + * @return true IFF the maximum allowed time period has elapsed + */ + @Override + public boolean isSatisfied(Population

population) { + if (endTime < 0) { + endTime = System.nanoTime() + maxTimePeriod; + } + + return System.nanoTime() >= endTime; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedGenerationCount.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedGenerationCount.java new file mode 100644 index 0000000000..37cfff9ef1 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/FixedGenerationCount.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergence; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.Population; + +/** + * Stops after a fixed number of generations. + *

+ * Each time {@link #isSatisfied(Population)} is invoked, a generation counter + * is incremented. Once the counter reaches the configured + * {@code maxGenerations} value, {@link #isSatisfied(Population)} returns true. + * + * @param

phenotype of chromosome + * @since 2.0 + */ +public class FixedGenerationCount

implements StoppingCondition

{ + /** Number of generations that have passed. */ + private int numGenerations; + + /** Maximum number of generations (stopping criteria). */ + private final int maxGenerations; + + /** + * Create a new FixedGenerationCount instance. + * + * @param maxGenerations number of generations to evolve + */ + public FixedGenerationCount(final int maxGenerations) { + if (maxGenerations <= 0) { + throw new GeneticException(GeneticException.TOO_SMALL, maxGenerations, 1); + } + this.maxGenerations = maxGenerations; + } + + /** + * Determine whether or not the given number of generations have passed. + * Increments the number of generations counter if the maximum has not been + * reached. + * + * @return true IFF the maximum number of generations has been + * exceeded + */ + @Override + public boolean isSatisfied(Population

population) { + if (this.numGenerations < this.maxGenerations) { + numGenerations++; + return false; + } + return true; + } + + /** + * Returns the number of generations that have already passed. + * @return the number of generations that have passed + */ + public int getNumGenerations() { + return numGenerations; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java new file mode 100644 index 0000000000..601c098711 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/StoppingCondition.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergence; + +import org.apache.commons.math4.ga.population.Population; + +/** + * Algorithm used to determine when to stop evolution. + * + * @param

phenotype of chromosome + * @since 2.0 + */ +public interface StoppingCondition

{ + + /** + * Determine whether or not the given population satisfies the stopping + * condition. + * @param population population of chromosome + * + * @return true if this stopping condition is met by the given + * population, false otherwise. + */ + boolean isSatisfied(Population

population); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedBestFitness.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedBestFitness.java new file mode 100644 index 0000000000..2281b2a70b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedBestFitness.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.convergence; + +import org.apache.commons.math4.ga.population.Population; + +/** + * This class represents a stopping condition based on best fitness value. + * Convergence will be stopped once best fitness remains unchanged for + * predefined number of generations. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class UnchangedBestFitness

implements StoppingCondition

{ + + /** best fitness of previous generation. **/ + private double lastBestFitness = Double.MIN_VALUE; + + /** + * The configured number of generations for which optimization process will + * continue with unchanged best fitness value. + **/ + private final int maxGenerationsWithUnchangedBestFitness; + + /** Number of generations the best fitness value has not been changed. **/ + private int generationsHavingUnchangedBestFitness; + + /** + * @param maxGenerationsWithUnchangedAverageFitness maximum number of + * generations with unchanged + * best fitness + */ + public UnchangedBestFitness(final int maxGenerationsWithUnchangedAverageFitness) { + this.maxGenerationsWithUnchangedBestFitness = maxGenerationsWithUnchangedAverageFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population

population) { + final double currentBestFitness = population.getFittestChromosome().evaluate(); + + if (lastBestFitness == currentBestFitness) { + generationsHavingUnchangedBestFitness++; + if (generationsHavingUnchangedBestFitness == maxGenerationsWithUnchangedBestFitness) { + return true; + } + } else { + this.generationsHavingUnchangedBestFitness = 0; + lastBestFitness = currentBestFitness; + } + + return false; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedMeanFitness.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedMeanFitness.java new file mode 100644 index 0000000000..a729e0afe7 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/UnchangedMeanFitness.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.convergence; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.population.Population; + +/** + * This class represents a stopping condition based on mean fitness value. + * Convergence will be stopped once mean fitness remains unchanged for + * predefined number of generations. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class UnchangedMeanFitness

implements StoppingCondition

{ + + /** Mean fitness of previous generation. **/ + private double lastMeanFitness = Double.MIN_VALUE; + + /** + * The configured number of generations for which optimization process will + * continue with unchanged best fitness value. + **/ + private final int maxGenerationsWithUnchangedMeanFitness; + + /** Number of generations the mean fitness value has not been changed. **/ + private int generationsHavingUnchangedMeanFitness; + + /** + * @param maxGenerationsWithUnchangedMeanFitness maximum number of generations + * with unchanged mean fitness + */ + public UnchangedMeanFitness(final int maxGenerationsWithUnchangedMeanFitness) { + this.maxGenerationsWithUnchangedMeanFitness = maxGenerationsWithUnchangedMeanFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isSatisfied(Population

population) { + + final double currentMeanFitness = calculateMeanFitness(population); + + if (lastMeanFitness == currentMeanFitness) { + generationsHavingUnchangedMeanFitness++; + if (generationsHavingUnchangedMeanFitness == maxGenerationsWithUnchangedMeanFitness) { + return true; + } + } else { + this.generationsHavingUnchangedMeanFitness = 0; + lastMeanFitness = currentMeanFitness; + } + + return false; + } + + /** + * calculates mean fitness of the population. + * @param population + * @return mean fitness + */ + private double calculateMeanFitness(Population

population) { + double totalFitness = 0.0; + for (Chromosome

chromosome : population) { + totalFitness += chromosome.evaluate(); + } + return totalFitness / population.getPopulationSize(); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/package-info.java new file mode 100644 index 0000000000..d386944a88 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/convergence/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.convergence; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java new file mode 100644 index 0000000000..c56434d71b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicy.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * An abstraction to represent the base crossover policy. + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractChromosomeCrossoverPolicy

implements CrossoverPolicy

{ + + /** + * {@inheritDoc} + */ + @Override + public ChromosomePair

crossover(final Chromosome

first, + final Chromosome

second, + final double crossoverRate) { + if (RandomProviderManager.getRandomProvider().nextDouble() < crossoverRate) { + return crossover(first, second); + } else { + return new ChromosomePair<>(first, second); + } + } + + /** + * Performs crossover of two chromosomes. + * @param first The first parent chromosome participating in crossover + * @param second The second parent chromosome participating in crossover + * @return chromosome pair + */ + protected abstract ChromosomePair

crossover(Chromosome

first, Chromosome

second); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java new file mode 100644 index 0000000000..2184310c6d --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicy.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * An abstraction of crossover policy for list chromosomes. + * @param genetype of chromosome + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractListChromosomeCrossoverPolicy extends AbstractChromosomeCrossoverPolicy

{ + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + @Override + public ChromosomePair

crossover(final Chromosome

first, final Chromosome

second) { + // check for validity. + checkValidity(first, second); + + final AbstractListChromosome firstListChromosome = (AbstractListChromosome) first; + final AbstractListChromosome secondListChromosome = (AbstractListChromosome) second; + + return mate(firstListChromosome, secondListChromosome); + } + + /** + * Validates the chromosome pair. + * @param first first chromosome + * @param second second chromosome + */ + @SuppressWarnings("unchecked") + protected void checkValidity(final Chromosome

first, final Chromosome

second) { + if (!(first instanceof AbstractListChromosome && second instanceof AbstractListChromosome)) { + throw new GeneticException(GeneticException.INVALID_FIXED_LENGTH_CHROMOSOME); + } + final AbstractListChromosome firstListChromosome = (AbstractListChromosome) first; + final AbstractListChromosome secondListChromosome = (AbstractListChromosome) second; + + final int length = firstListChromosome.getLength(); + if (length != secondListChromosome.getLength()) { + throw new GeneticException(GeneticException.SIZE_MISMATCH, secondListChromosome.getLength(), length); + } + + } + + /** + * Performs mating between two chromosomes and returns the offspring pair. + * @param first The first parent chromosome participating in crossover + * @param second The second parent chromosome participating in crossover + * @return chromosome pair + */ + protected abstract ChromosomePair

mate(AbstractListChromosome first, AbstractListChromosome second); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java new file mode 100644 index 0000000000..65aa19111a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CrossoverPolicy.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; + +/** + * Policy used to create a pair of new chromosomes by performing a crossover + * operation on a source pair of chromosomes. + * @param

phenotype of chromosome + * @since 2.0 + */ +public interface CrossoverPolicy

{ + + /** + * Perform a crossover operation on the given chromosomes. + * + * @param first the first chromosome. + * @param second the second chromosome. + * @param crossoverRate the probability of crossover + * @return the pair of new chromosomes that resulted from the crossover. + */ + ChromosomePair

crossover(Chromosome

first, Chromosome

second, double crossoverRate); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java new file mode 100644 index 0000000000..1d0da031e8 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/CycleCrossover.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * Cycle Crossover [CX] builds offspring from ordered chromosomes by + * identifying cycles between two parent chromosomes. To form the children, the + * cycles are copied from the respective parents. + *

+ * To form a cycle the following procedure is applied: + *

    + *
  1. start with the first gene of parent 1
  2. + *
  3. look at the gene at the same position of parent 2
  4. + *
  5. go to the position with the same gene in parent 1
  6. + *
  7. add this gene index to the cycle
  8. + *
  9. repeat the steps 2-5 until we arrive at the starting gene of this + * cycle
  10. + *
+ * The indices that form a cycle are then used to form the children in + * alternating order, i.e. in cycle 1, the genes of parent 1 are copied to child + * 1, while in cycle 2 the genes of parent 1 are copied to child 2, and so forth + * ... + * + * Example (zero-start cycle): + *
+ * p1 = (8 4 7 3 6 2 5 1 9 0)    X   c1 = (8 1 2 3 4 5 6 7 9 0)
+ * p2 = (0 1 2 3 4 5 6 7 8 9)    X   c2 = (0 4 7 3 6 2 5 1 8 9)
+ *
+ * cycle 1: 8 0 9
+ * cycle 2: 4 1 7 2 5 6
+ * cycle 3: 3
+ * 
+ * + * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @see + * Cycle Crossover Operator + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 3.1 + */ +public class CycleCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** If the start index shall be chosen randomly. */ + private final boolean randomStart; + + /** + * Creates a new {@link CycleCrossover} policy. + */ + public CycleCrossover() { + this(false); + } + + /** + * Creates a new {@link CycleCrossover} policy using the given + * {@code randomStart} behavior. + * + * @param randomStart whether the start index shall be chosen randomly or be set + * to 0 + */ + public CycleCrossover(final boolean randomStart) { + this.randomStart = randomStart; + } + + /** + * Returns whether the starting index is chosen randomly or set to zero. + * + * @return {@code true} if the starting index is chosen randomly, {@code false} + * otherwise + */ + public boolean isRandomStart() { + return randomStart; + } + + /** + * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the + * actual crossover. + * + * @param first the first chromosome + * @param second the second chromosome + * @return the pair of new chromosomes that resulted from the crossover + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + + final int length = first.getLength(); + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children: do a crossover copy to simplify the later processing + final List child1Rep = new ArrayList<>(second.getRepresentation()); + final List child2Rep = new ArrayList<>(first.getRepresentation()); + + // the set of all visited indices so far + final Set visitedIndices = new HashSet<>(length); + // the indices of the current cycle + final List indices = new ArrayList<>(length); + + // determine the starting index + int idx = randomStart ? RandomProviderManager.getRandomProvider().nextInt(length) : 0; + int cycle = 1; + + while (visitedIndices.size() < length) { + indices.add(idx); + + T item = parent2Rep.get(idx); + idx = parent1Rep.indexOf(item); + + while (idx != indices.get(0)) { + // add that index to the cycle indices + indices.add(idx); + // get the item in the second parent at that index + item = parent2Rep.get(idx); + // get the index of that item in the first parent + idx = parent1Rep.indexOf(item); + } + + // for even cycles: swap the child elements on the indices found in this cycle + if (cycle++ % 2 != 0) { + for (int i : indices) { + final T tmp = child1Rep.get(i); + child1Rep.set(i, child2Rep.get(i)); + child2Rep.set(i, tmp); + } + } + + visitedIndices.addAll(indices); + // find next starting index: last one + 1 until we find an unvisited index + idx = (indices.get(0) + 1) % length; + while (visitedIndices.contains(idx) && visitedIndices.size() < length) { + idx++; + if (idx >= length) { + idx = 0; + } + } + indices.clear(); + } + + return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep)); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java new file mode 100644 index 0000000000..f0313c13b0 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/NPointCrossover.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * N-point crossover policy. For each iteration a random crossover point is + * selected and the first part from each parent is copied to the corresponding + * child, and the second parts are copied crosswise. + * + * Example (2-point crossover): + *

+ * -C- denotes a crossover point
+ *           -C-       -C-                         -C-        -C-
+ * p1 = (1 0  | 1 0 0 1 | 0 1 1)    X    p2 = (0 1  | 1 0 1 0  | 1 1 1)
+ *      \----/ \-------/ \-----/              \----/ \--------/ \-----/
+ *        ||      (*)       ||                  ||      (**)       ||
+ *        VV      (**)      VV                  VV      (*)        VV
+ *      /----\ /--------\ /-----\             /----\ /--------\ /-----\
+ * c1 = (1 0  | 1 0 1 0  | 0 1 1)    X   c2 = (0 1  | 1 0 0 1  | 0 1 1)
+ * 
+ * + * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 3.1 + */ +public class NPointCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** The number of crossover points. */ + private final int crossoverPoints; + + /** + * Creates a new {@link NPointCrossover} policy using the given number of + * points. + *

+ * Note: the number of crossover points must be < + * chromosome length - 1. This condition can only be checked at + * runtime, as the chromosome length is not known in advance. + * + * @param crossoverPoints the number of crossover points + */ + public NPointCrossover(final int crossoverPoints) { + if (crossoverPoints <= 0) { + throw new GeneticException(GeneticException.NOT_STRICTLY_POSITIVE, crossoverPoints); + } + this.crossoverPoints = crossoverPoints; + } + + /** + * Returns the number of crossover points used by this {@link CrossoverPolicy}. + * + * @return the number of crossover points + */ + public int getCrossoverPoints() { + return crossoverPoints; + } + + /** + * Performs a N-point crossover. N random crossover points are selected and are + * used to divide the parent chromosomes into segments. The segments are copied + * in alternate order from the two parents to the corresponding child + * chromosomes. + * + * Example (2-point crossover): + *

+     * -C- denotes a crossover point
+     *           -C-       -C-                         -C-        -C-
+     * p1 = (1 0  | 1 0 0 1 | 0 1 1)    X    p2 = (0 1  | 1 0 1 0  | 1 1 1)
+     *      \----/ \-------/ \-----/              \----/ \--------/ \-----/
+     *        ||      (*)       ||                  ||      (**)       ||
+     *        VV      (**)      VV                  VV      (*)        VV
+     *      /----\ /--------\ /-----\             /----\ /--------\ /-----\
+     * c1 = (1 0  | 1 0 1 0  | 0 1 1)    X   c2 = (0 1  | 1 0 0 1  | 0 1 1)
+     * 
+ * + * @param first first parent (p1) + * @param second second parent (p2) + * @return pair of two children (c1,c2) + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + + final int length = first.getLength(); + if (crossoverPoints >= length) { + throw new GeneticException(GeneticException.TOO_LARGE, crossoverPoints, length); + } + + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children + final List child1Rep = new ArrayList<>(length); + final List child2Rep = new ArrayList<>(length); + + final UniformRandomProvider random = RandomProviderManager.getRandomProvider(); + + List c1 = child1Rep; + List c2 = child2Rep; + + int remainingPoints = crossoverPoints; + int lastIndex = 0; + for (int i = 0; i < crossoverPoints; i++, remainingPoints--) { + // select the next crossover point at random + final int crossoverIndex = 1 + lastIndex + random.nextInt(length - lastIndex - remainingPoints); + + // copy the current segment + for (int j = lastIndex; j < crossoverIndex; j++) { + c1.add(parent1Rep.get(j)); + c2.add(parent2Rep.get(j)); + } + + // swap the children for the next segment + final List tmp = c1; + c1 = c2; + c2 = tmp; + + lastIndex = crossoverIndex; + } + + // copy the last segment + for (int j = lastIndex; j < length; j++) { + c1.add(parent1Rep.get(j)); + c2.add(parent2Rep.get(j)); + } + + return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep)); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java new file mode 100644 index 0000000000..f860798c82 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossover.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * OnePoint Crossover Policy for Binary chromosomes. + * @param

the phenotype + */ +public class OnePointBinaryCrossover

extends AbstractChromosomeCrossoverPolicy

{ + + /** + * {@inheritDoc} + */ + @Override + protected ChromosomePair

crossover(Chromosome

first, Chromosome

second) { + + if (!(first instanceof BinaryChromosome && second instanceof BinaryChromosome)) { + throw new GeneticException(GeneticException.INVALID_FIXED_LENGTH_CHROMOSOME); + } + final BinaryChromosome

firstChromosome = (BinaryChromosome

) first; + final BinaryChromosome

secondChromosome = (BinaryChromosome

) second; + + final long alleleCount = firstChromosome.getLength(); + + // array representations of the parents + final long[] parent1Rep = firstChromosome.getRepresentation(); + final long[] parent2Rep = secondChromosome.getRepresentation(); + + // and of the children + final long[] child1Rep = new long[parent1Rep.length]; + final long[] child2Rep = new long[parent2Rep.length]; + + // select a crossover point at random (0 and length makes no sense) + final long crossoverIndex = 1 + (RandomProviderManager.getRandomProvider().nextLong(alleleCount - 1)); + + final int offset = (int) (alleleCount % Long.SIZE == 0 ? 0 : Long.SIZE - alleleCount % Long.SIZE); + final long offsettedCrossoverIndex = crossoverIndex + offset; + + final int crossoverBlockIndex = (int) (offsettedCrossoverIndex / Long.SIZE); + final int crossoverBlockAlleleIndex = (int) offsettedCrossoverIndex % Long.SIZE; + + if (crossoverBlockAlleleIndex == 0) { + // if the offsetted-crossover index is divisible by + // Long.SIZE then first copy all + // elements of previous array elements. + for (int i = 0; i < crossoverBlockIndex; i++) { + child1Rep[i] = parent1Rep[i]; + child2Rep[i] = parent2Rep[i]; + } + // copy all elements from crossover block index. + for (int i = crossoverBlockIndex; i < parent1Rep.length; i++) { + child1Rep[i] = parent2Rep[i]; + child2Rep[i] = parent1Rep[i]; + } + } else { + // copy all parent array elements to child till crossover block index - 1. + for (int i = 0; i < crossoverBlockIndex; i++) { + child1Rep[i] = parent1Rep[i]; + child2Rep[i] = parent2Rep[i]; + } + // do exchange of alleles of the array element indexed at crossover block index. + final long parent1CrossoverBlockRep = parent1Rep[crossoverBlockIndex]; + final long parent2CrossoverBlockRep = parent2Rep[crossoverBlockIndex]; + final long leftMask = Long.MIN_VALUE >> crossoverBlockAlleleIndex - 1; + final long rightMask = crossoverBlockAlleleIndex != 1 ? + (long) Math.pow(2, Long.SIZE - crossoverBlockAlleleIndex) - 1 : + Long.MAX_VALUE; + + final long child1CrossoverBlockRep = (parent1CrossoverBlockRep & leftMask) | + (parent2CrossoverBlockRep & rightMask); + final long child2CrossoverBlockRep = (parent2CrossoverBlockRep & leftMask) | + (parent1CrossoverBlockRep & rightMask); + + child1Rep[crossoverBlockIndex] = child1CrossoverBlockRep; + child2Rep[crossoverBlockIndex] = child2CrossoverBlockRep; + + // Copy all the alleles which belong to array elements having index > + // crossover block index. + if (crossoverBlockIndex < parent1Rep.length - 1) { + for (int i = crossoverBlockIndex + 1; i < parent1Rep.length; i++) { + child1Rep[i] = parent2Rep[i]; + child2Rep[i] = parent1Rep[i]; + } + } + } + + final BinaryChromosome

childChromosome1 = new BinaryChromosome<>(child1Rep, alleleCount, + firstChromosome.getFitnessFunction(), firstChromosome.getDecoder()); + final BinaryChromosome

childChromosome2 = new BinaryChromosome<>(child2Rep, alleleCount, + secondChromosome.getFitnessFunction(), secondChromosome.getDecoder()); + + return new ChromosomePair<>(childChromosome1, childChromosome2); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java new file mode 100644 index 0000000000..2c6da54ccb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OnePointCrossover.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * One point crossover policy. A random crossover point is selected and the + * first part from each parent is copied to the corresponding child, and the + * second parts are copied crosswise. + * + * Example: + *

+ * -C- denotes a crossover point
+ *                   -C-                                 -C-
+ * p1 = (1 0 1 0 0 1  | 0 1 1)    X    p2 = (0 1 1 0 1 0  | 1 1 1)
+ *      \------------/ \-----/              \------------/ \-----/
+ *            ||         (*)                       ||        (**)
+ *            VV         (**)                      VV        (*)
+ *      /------------\ /-----\              /------------\ /-----\
+ * c1 = (1 0 1 0 0 1  | 1 1 1)    X    c2 = (0 1 1 0 1 0  | 0 1 1)
+ * 
+ * + * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 2.0 + * + */ +public class OnePointCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** + * Performs one point crossover. A random crossover point is selected and the + * first part from each parent is copied to the corresponding child, and the + * second parts are copied crosswise. + * + * Example: + *

+     * -C- denotes a crossover point
+     *                   -C-                                 -C-
+     * p1 = (1 0 1 0 0 1  | 0 1 1)    X    p2 = (0 1 1 0 1 0  | 1 1 1)
+     *      \------------/ \-----/              \------------/ \-----/
+     *            ||         (*)                       ||        (**)
+     *            VV         (**)                      VV        (*)
+     *      /------------\ /-----\              /------------\ /-----\
+     * c1 = (1 0 1 0 0 1  | 1 1 1)    X    c2 = (0 1 1 0 1 0  | 0 1 1)
+     * 
+ * + * @param first first parent (p1) + * @param second second parent (p2) + * @return pair of two children (c1,c2) + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + final int length = first.getLength(); + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children + final List child1Rep = new ArrayList<>(length); + final List child2Rep = new ArrayList<>(length); + + // select a crossover point at random (0 and length makes no sense) + final int crossoverIndex = 1 + (RandomProviderManager.getRandomProvider().nextInt(length - 1)); + + // copy the first part + for (int i = 0; i < crossoverIndex; i++) { + child1Rep.add(parent1Rep.get(i)); + child2Rep.add(parent2Rep.get(i)); + } + // and switch the second part + for (int i = crossoverIndex; i < length; i++) { + child1Rep.add(parent2Rep.get(i)); + child2Rep.add(parent1Rep.get(i)); + } + + return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java new file mode 100644 index 0000000000..24608f9da7 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/OrderedCrossover.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * Order 1 Crossover [OX1] builds offspring from ordered chromosomes by + * copying a consecutive slice from one parent, and filling up the remaining + * genes from the other parent as they appear. + *

+ * This policy works by applying the following rules: + *

    + *
  1. select a random slice of consecutive genes from parent 1
  2. + *
  3. copy the slice to child 1 and mark out the genes in parent 2
  4. + *
  5. starting from the right side of the slice, copy genes from parent 2 as + * they appear to child 1 if they are not yet marked out.
  6. + *
+ *

+ * Example (random sublist from index 3 to 7, underlined): + *

+ * p1 = (8 4 7 3 6 2 5 1 9 0)   X   c1 = (0 4 7 3 6 2 5 1 8 9)
+ *             ---------                        ---------
+ * p2 = (0 1 2 3 4 5 6 7 8 9)   X   c2 = (8 1 2 3 4 5 6 7 9 0)
+ * 
+ *

+ * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @see + * Order 1 Crossover Operator + * + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 3.1 + */ +public class OrderedCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** + * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the + * actual crossover. + * + * @param first the first chromosome + * @param second the second chromosome + * @return the pair of new chromosomes that resulted from the crossover + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + + final int length = first.getLength(); + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children + final List child1 = new ArrayList<>(length); + final List child2 = new ArrayList<>(length); + // sets of already inserted items for quick access + final Set child1Set = new HashSet<>(length); + final Set child2Set = new HashSet<>(length); + + final UniformRandomProvider random = RandomProviderManager.getRandomProvider(); + // choose random points, making sure that lb < ub. + final int a = random.nextInt(length); + int b; + do { + b = random.nextInt(length); + } while (a == b); + // determine the lower and upper bounds + final int lb = Math.min(a, b); + final int ub = Math.max(a, b); + + // add the subLists that are between lb and ub + child1.addAll(parent1Rep.subList(lb, ub + 1)); + child1Set.addAll(child1); + child2.addAll(parent2Rep.subList(lb, ub + 1)); + child2Set.addAll(child2); + + // iterate over every item in the parents + for (int i = 1; i <= length; i++) { + final int idx = (ub + i) % length; + + // retrieve the current item in each parent + final T item1 = parent1Rep.get(idx); + final T item2 = parent2Rep.get(idx); + + // if the first child already contains the item in the second parent add it + if (!child1Set.contains(item2)) { + child1.add(item2); + child1Set.add(item2); + } + + // if the second child already contains the item in the first parent add it + if (!child2Set.contains(item1)) { + child2.add(item1); + child2Set.add(item1); + } + } + + // rotate so that the original slice is in the same place as in the parents. + Collections.rotate(child1, lb); + Collections.rotate(child2, lb); + + return new ChromosomePair<>(first.newChromosome(child1), second.newChromosome(child2)); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java new file mode 100644 index 0000000000..23292f712a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/UniformCrossover.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * Perform Uniform Crossover [UX] on the specified chromosomes. A fixed mixing + * ratio is used to combine genes from the first and second parents, e.g. using + * a ratio of 0.5 would result in approximately 50% of genes coming from each + * parent. This is typically a poor method of crossover, but empirical evidence + * suggests that it is more exploratory and results in a larger part of the + * problem space being searched. + *

+ * This crossover policy evaluates each gene of the parent chromosomes by + * choosing a uniform random number {@code p} in the range [0, 1]. If {@code p} + * < {@code ratio}, the parent genes are swapped. This means with a ratio of + * 0.7, 30% of the genes from the first parent and 70% from the second parent + * will be selected for the first offspring (and vice versa for the second + * offspring). + *

+ * This policy works only on {@link AbstractListChromosome}, and therefore it is + * parameterized by T. Moreover, the chromosomes must have same lengths. + * + * @see Crossover + * techniques (Wikipedia) + * @see Crossover + * (Obitko.com) + * @see Uniform + * crossover + * @param generic type of the {@link AbstractListChromosome}s for crossover + * @param

phenotype of chromosome + * @since 3.1 + */ +public class UniformCrossover extends AbstractListChromosomeCrossoverPolicy { + + /** crossover rate. **/ + public static final String CROSSOVER_RATE = "CROSSOVER_RATE"; + + /** The mixing ratio. */ + private final double ratio; + + /** + * Creates a new {@link UniformCrossover} policy using the given mixing ratio. + * + * @param ratio the mixing ratio + */ + public UniformCrossover(final double ratio) { + if (ratio < 0.0d || ratio > 1.0d) { + throw new GeneticException(GeneticException.OUT_OF_RANGE, ratio, CROSSOVER_RATE, 0.0d, 1.0d); + } + this.ratio = ratio; + } + + /** + * Returns the mixing ratio used by this {@link CrossoverPolicy}. + * + * @return the mixing ratio + */ + public double getRatio() { + return ratio; + } + + /** + * Helper for {@link #crossover(Chromosome, Chromosome, double)}. Performs the + * actual crossover. + * + * @param first the first chromosome + * @param second the second chromosome + * @return the pair of new chromosomes that resulted from the crossover + */ + @Override + protected ChromosomePair

mate(final AbstractListChromosome first, + final AbstractListChromosome second) { + final int length = first.getLength(); + // array representations of the parents + final List parent1Rep = first.getRepresentation(); + final List parent2Rep = second.getRepresentation(); + // and of the children + final List child1Rep = new ArrayList<>(length); + final List child2Rep = new ArrayList<>(length); + + final UniformRandomProvider random = RandomProviderManager.getRandomProvider(); + + for (int index = 0; index < length; index++) { + + if (random.nextDouble() < ratio) { + // swap the bits -> take other parent + child1Rep.add(parent2Rep.get(index)); + child2Rep.add(parent1Rep.get(index)); + } else { + child1Rep.add(parent1Rep.get(index)); + child2Rep.add(parent2Rep.get(index)); + } + } + + return new ChromosomePair<>(first.newChromosome(child1Rep), second.newChromosome(child2Rep)); + } +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java new file mode 100644 index 0000000000..68c92826b1 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.crossover; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java new file mode 100644 index 0000000000..1b1057de0f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearAverageRankBasedCrossoverRateGenerator.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * Generates crossover rate based on linear function of relative average rank of + * input chromosomes in population. + * @param

phenotype of chromosome + */ +public class AdaptiveLinearAverageRankBasedCrossoverRateGenerator

implements CrossoverRateGenerator

{ + + /** minimum crossover rate. **/ + private final double minimumRate; + + /** maximum crossover rate. **/ + private final double maximumRate; + + /** + * @param minimumRate minimum crossover rate + * @param maximumRate maximum crossover rate + */ + public AdaptiveLinearAverageRankBasedCrossoverRateGenerator(double minimumRate, double maximumRate) { + this.maximumRate = maximumRate; + this.minimumRate = minimumRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation) { + final int averageRank = (populationStats.findRank(first) + populationStats.findRank(second)) / 2; + return minimumRate + + (maximumRate - minimumRate) * (1.0 - (double) averageRank / (populationStats.getPopulationSize() - 1)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java new file mode 100644 index 0000000000..c03f488f5f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGenerator.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * Generates crossover rate based on linear function of relative maximum rank of + * input chromosomes in population. + * @param

phenotype of chromosome + */ +public class AdaptiveLinearMaximumRankBasedCrossoverRateGenerator

implements CrossoverRateGenerator

{ + + /** minimum crossover rate. **/ + private final double minimumRate; + + /** maximum crossover rate. **/ + private final double maximumRate; + + /** + * @param minimumRate minimum crossover rate + * @param maximumRate maximum crossover rate + */ + public AdaptiveLinearMaximumRankBasedCrossoverRateGenerator(double minimumRate, double maximumRate) { + this.maximumRate = maximumRate; + this.minimumRate = minimumRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation) { + final int maximumRank = Math.max(populationStats.findRank(first), populationStats.findRank(second)); + return minimumRate + + (maximumRate - minimumRate) * (1.0 - (double) maximumRank / (populationStats.getPopulationSize() - 1)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java new file mode 100644 index 0000000000..2f77ce12f0 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/ConstantCrossoverRateGenerator.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This class represents a constant crossover rate generator. + * @param

phenotype of chromosome + */ +public class ConstantCrossoverRateGenerator

implements CrossoverRateGenerator

{ + + /** the fixed value of crossover rate. **/ + private final double crossoverRate; + + /** + * @param crossoverRate crossover rate + */ + public ConstantCrossoverRateGenerator(double crossoverRate) { + this.crossoverRate = crossoverRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation) { + return crossoverRate; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/CrossoverRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/CrossoverRateGenerator.java new file mode 100644 index 0000000000..438aeeda7c --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/CrossoverRateGenerator.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This abstraction represents crossover rate generator. + * @param

phenotype of chromosome + */ +public interface CrossoverRateGenerator

{ + + /** + * Generates crossover rate. + * @param first first chromosome + * @param second second chromosome + * @param populationStats population statistics summary + * @param generation generation number + * @return crossover rate rate of crossover + */ + double generate(Chromosome

first, + Chromosome

second, + PopulationStatisticalSummary

populationStats, + int generation); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java new file mode 100644 index 0000000000..886eab7704 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/crossover/rategenerator/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.crossover.rategenerator; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java new file mode 100644 index 0000000000..96a1920533 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoder.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * An abstract Decoder of ListChromosome. + * @param genotype fo chromosome + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractListChromosomeDecoder implements Decoder

{ + + /** + * {@inheritDoc} + */ + @SuppressWarnings("unchecked") + @Override + public P decode(Chromosome

chromosome) { + checkValidity(chromosome); + + return decode((AbstractListChromosome) chromosome); + } + + /** + * Checks validity of {@link Chromosome}. + * @param chromosome the {@link Chromosome} + */ + protected void checkValidity(Chromosome

chromosome) { + if (!AbstractListChromosome.class.isAssignableFrom(chromosome.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, chromosome.getClass().getSimpleName()); + } + } + + /** + * Decodes the chromosome genotype and returns the phenotype. + * @param chromosome The list chromosome to decode + * @return decoded phenotype of chromosome + */ + protected abstract P decode(AbstractListChromosome chromosome); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java new file mode 100644 index 0000000000..50f2ba935d --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/Decoder.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import org.apache.commons.math4.ga.chromosome.Chromosome; + +/** + * Decoder is responsible for converting chromosome genotype to phenotype. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface Decoder

{ + + /** + * Converts genotype to phenotype. + * @param chromosome The {@link Chromosome} + * @return phenotype The phenotype of chromosome + */ + P decode(Chromosome

chromosome); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java new file mode 100644 index 0000000000..037ccfde03 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoder.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * A concrete implementation of RandomKey decoder. This class is responsible for + * decoding permutation chromosome encoded with random key. + * @param type of the permutation element + * @since 4.0 + */ +public final class RandomKeyDecoder extends AbstractListChromosomeDecoder> { + + /** base sequence for decoding chromosome. **/ + private final List baseSequence; + + /** + * @param baseSequence the unpermuted sequence + */ + public RandomKeyDecoder(List baseSequence) { + this.baseSequence = Collections.unmodifiableList(Objects.requireNonNull(baseSequence)); + } + + /** + * {@inheritDoc} + */ + @Override + protected List decode(AbstractListChromosome> chromosome) { + final List representation = chromosome.getRepresentation(); + final List sortedRepresentation = new ArrayList<>(representation); + Collections.sort(sortedRepresentation); + + final int sequenceLength = baseSequence.size(); + + // the size of the three lists must be equal + if (representation.size() != sequenceLength) { + throw new GeneticException(GeneticException.SIZE_MISMATCH, representation.size(), sequenceLength); + } + + // do not modify the original representation + final List representationCopy = new ArrayList<>(representation); + + // now find the indices in the original repr and use them for permuting + final List res = new ArrayList<>(sequenceLength); + for (int i = 0; i < sequenceLength; i++) { + final int index = representationCopy.indexOf(sortedRepresentation.get(i)); + res.add(baseSequence.get(index)); + representationCopy.set(index, null); + } + + return res; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java new file mode 100644 index 0000000000..b8d0be72a3 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoder.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; + +/** + * A concrete implementation of transparent decoder for List Chromosome. Treats + * the gentype as phenotype. + * @param the genotype of chromosome + * @since 4.0 + */ +public final class TransparentListChromosomeDecoder extends AbstractListChromosomeDecoder> { + + /** + * {@inheritDoc} + */ + @Override + protected List decode(AbstractListChromosome> chromosome) { + return chromosome.getRepresentation(); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java new file mode 100644 index 0000000000..57e19a8131 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/decoder/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.decoder; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java new file mode 100644 index 0000000000..ade6dea0d2 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/FitnessFunction.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.fitness; + +/** + * This interface represents fitness function. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface FitnessFunction

{ + + /** + * computes the fitness value of the input chromosome's phenotype. + * @param decodedChromosome chromosome decoded as phenotype + * @return fitness value + */ + double compute(P decodedChromosome); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java new file mode 100644 index 0000000000..5b6d4c5203 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/fitness/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.fitness; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java new file mode 100644 index 0000000000..7209472c37 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/GeneticException.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.internal.exception; + +import java.text.MessageFormat; + +/** + * This class represents the Exception encountered during GA optimization. + * @since 4.0 + */ +public class GeneticException extends RuntimeException { + + /** Error message for "out of range" condition. */ + public static final String OUT_OF_RANGE = "Value {0} of {1} is out of range [{2}, {3}]"; + + /** Error message for "not strictly positive" condition. */ + public static final String NOT_STRICTLY_POSITIVE = "Number {0} is not strictly positive"; + + /** Error message for "too large" condition. */ + public static final String TOO_LARGE = "Number {0} is larger than {1}"; + + /** Error message for "too small" condition. */ + public static final String TOO_SMALL = "Number {0} is smaller than {1}"; + + /** Error message for "out of range" condition. */ + public static final String NO_DATA = "No data"; + + /** Error message for "size mismatch" condition. */ + public static final String SIZE_MISMATCH = "Size mismatch: {0} != {1}"; + + /** Error message for "generic illegal argument" condition. */ + public static final String ILLEGAL_ARGUMENT = "Illegal Argument Exception: {0}"; + + /** Error message for "generic illegal argument" condition. */ + public static final String ILLEGAL_RANGE = "Illegal Range of Value Exception: " + + "[Expected min-{0}, max-{1}], [Passed min-{2}, max-{3}]"; + + /** Error message for "generic illegal argument" condition. */ + public static final String INVALID_FIXED_LENGTH_CHROMOSOME = "Invalid Fixed Length Chromosome."; + + /** Error message for "NULL ARGUMENT" condition. */ + public static final String NULL_ARGUMENT = "Null Argument Exception: {0}"; + + /** + * Error message for "List of Chromosome bigger than population size" condition. + */ + public static final String LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE = "List of chromosome bigger than " + + "population size: {0} > {1}"; + + /** + * Error message for "population limit not positive" condition. + */ + public static final String POPULATION_LIMIT_NOT_POSITIVE = "Population limit not positive :{0}"; + + /** + * Error message for " population limit less than list of chromosomes size" + * condition. + */ + public static final String POPULATION_LIMIT_LESS_THAN_LIST_OF_CHROMOSOMES_SIZE = "Population limit is " + + " lesser than list of chromosomes size : {0} < {1}"; + + /** + * Error message for different origin and permuted data. + */ + public static final String DIFFERENT_ORIG_AND_PERMUTED_DATA = "Different original and permuted data"; + + /** + * Error message for chromosome length larger than expected. + */ + public static final String LENGTH_TOO_LARGE = "Chromosome length larger than {0} not supported for" + + " string representation."; + + /** Serializable version identifier. */ + private static final long serialVersionUID = 20210516L; + + /** + * Create an exception where the message is constructed by applying the + * {@code format()} method from {@code java.text.MessageFormat}. + * + * @param message Message format (with replaceable parameters). + * @param formatArguments Actual arguments to be displayed in the message. + */ + public GeneticException(String message, Object... formatArguments) { + super(MessageFormat.format(message, formatArguments)); + } + + /** + * Create an exception. + * @param t instance of {@link Throwable} + */ + public GeneticException(Throwable t) { + super(t); + } + + /** + * Create an exception having both stacktrace and message. + * @param message the exception message + * @param t the instance of {@link Throwable} + * @param formatArguments arguments to format the exception message + */ + public GeneticException(String message, Throwable t, Object... formatArguments) { + super(MessageFormat.format(message, formatArguments), t); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java new file mode 100644 index 0000000000..b55e918a60 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/exception/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.internal.exception; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java new file mode 100644 index 0000000000..b95b60f1a6 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/PopulationStatisticalSummaryImpl.java @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.internal.stats; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This class represents an implementation of population statistical summary. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class PopulationStatisticalSummaryImpl

implements PopulationStatisticalSummary

{ + + /** maximum fitness of the population. **/ + private final double maxFitness; + + /** minimum fitness of the population. **/ + private final double minFitness; + + /** mean fitness of the population. **/ + private double meanFitness; + + /** variance of population fitness. **/ + private final double variance; + + /** population size. **/ + private final int populationSize; + + /** a map of chromosome Id and corresponding rank in population. **/ + private final Map chromosomeIdRankMap = new HashMap<>(); + + /** + * @param population current population {@link Population} of chromosomes + */ + public PopulationStatisticalSummaryImpl(Population

population) { + + // Fetch all chromosomes. + List> chromosomes = getChromosomes(Objects.requireNonNull(population)); + + // Sort all chromosomes. + Collections.sort(chromosomes); + + this.populationSize = chromosomes.size(); + this.maxFitness = chromosomes.get(chromosomes.size() - 1).evaluate(); + this.minFitness = chromosomes.get(0).evaluate(); + this.meanFitness = calculateMeanFitness(chromosomes); + this.variance = calculateVariance(chromosomes); + + updateChromosomeIdRankMap(chromosomes); + + } + + /** + * Updates chromosome Id and rank. + * @param chromosomes list of chromosomes + */ + private void updateChromosomeIdRankMap(List> chromosomes) { + for (int rank = 0; rank < chromosomes.size(); rank++) { + this.chromosomeIdRankMap.put(chromosomes.get(rank).getId(), rank); + } + } + + /** + * Fetches chromosomes. + * @param population + * @return list of chromosomes + */ + private List> getChromosomes(Population

population) { + List> chromosomes = new ArrayList<>(population.getPopulationSize()); + for (Chromosome

chromosome : population) { + chromosomes.add(chromosome); + } + return chromosomes; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMeanFitness() { + return this.meanFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public double getFitnessVariance() { + return this.variance; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMaxFitness() { + return this.maxFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public double getMinFitness() { + return this.minFitness; + } + + /** + * {@inheritDoc} + */ + @Override + public long getPopulationSize() { + return this.populationSize; + } + + /** + * Calculate mean fitness. + * @param chromosomes list of chromosomes + * @return returns mean fitness + */ + private double calculateMeanFitness(List> chromosomes) { + double sum = 0.0; + for (Chromosome

chromosome : chromosomes) { + sum += chromosome.evaluate(); + } + return sum / chromosomes.size(); + } + + /** + * Calculate variance of population fitness. + * @param chromosomes List of chromosomes + * @return fitness variance + */ + private double calculateVariance(List> chromosomes) { + if (this.meanFitness == 0) { + this.meanFitness = calculateMeanFitness(chromosomes); + } + double sumOfSquare = 0.0; + for (Chromosome

chromosome : chromosomes) { + sumOfSquare += Math.pow(chromosome.evaluate(), 2); + } + + return (sumOfSquare / chromosomes.size()) - Math.pow(this.meanFitness, 2); + } + + /** + * {@inheritDoc} + */ + @Override + public int findRank(Chromosome

chromosome) { + return chromosomeIdRankMap.get(chromosome.getId()); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java new file mode 100644 index 0000000000..a0ae55d50e --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/internal/stats/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.internal.stats; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java new file mode 100644 index 0000000000..f99d585687 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListener.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.listener; + +import org.apache.commons.math4.ga.population.Population; + +/** + * This interface represents a convergence listener. Any implementation of the + * same will be notified about the population statics. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface ConvergenceListener

{ + + /** + * Notifies about the population statistics. + * @param generation current generation + * @param population population of chromosome + */ + void notify(int generation, Population

population); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java new file mode 100644 index 0000000000..7fce28e510 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistry.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.listener; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.population.Population; + +/** + * This class is the default implementation of ConvergenceListenerRegistry. It + * will be responsible for registering the interested listeners and notifying + * all when required. + * @param

phenotype of chromosome + * @since 4.0 + */ +public final class ConvergenceListenerRegistry

{ + + /** + * The instance of the singleton class. + */ + @SuppressWarnings("rawtypes") + private static final ConvergenceListenerRegistry INSTANCE = new ConvergenceListenerRegistry<>(); + + /** + * List of registered listeners. + */ + private final List> listeners = new ArrayList<>(); + + /** + * private constructor to construct the singleton instance. + */ + private ConvergenceListenerRegistry() { + } + + /** + * Registers the interested ConvergenceListener passed as an argument. + * @param convergenceListener The {@link ConvergenceListener} + */ + public void addConvergenceListener(ConvergenceListener

convergenceListener) { + this.listeners.add(convergenceListener); + } + + /** + * Notifies all registered ConvergenceListeners about the population statistics. + * @param generation current generation + * @param population population of chromosomes + */ + public synchronized void notifyAll(int generation, Population

population) { + for (ConvergenceListener

convergenceListener : listeners) { + convergenceListener.notify(generation, population); + } + } + + /** + * Add instance of convergence listener. + * @param convergenceListeners list of {@link ConvergenceListener} + */ + public void addConvergenceListeners(List> convergenceListeners) { + for (ConvergenceListener

convergenceListener : Objects.requireNonNull(convergenceListeners)) { + addConvergenceListener(convergenceListener); + } + } + + /** + * Returns instance of this class. + * @param

The phenotype of chromosome + * @return instance + */ + @SuppressWarnings("unchecked") + public static

ConvergenceListenerRegistry

getInstance() { + return INSTANCE; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java new file mode 100644 index 0000000000..b0f7276f8a --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLogger.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.listener; + +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Logs population statistics during the convergence process. + * @param

phenotype of chromosome + * @since 4.0 + */ +public final class PopulationStatisticsLogger

implements ConvergenceListener

{ + + /** instance of log4j logger. **/ + private static final Logger LOGGER = LoggerFactory.getLogger(PopulationStatisticsLogger.class); + + /** + * Logs the population statistics during the process of convergence. + */ + @Override + public void notify(int generation, Population

population) { + final PopulationStatisticalSummary

populationStatisticalSummary = new PopulationStatisticalSummaryImpl<>( + population); + LOGGER.info( + "Population statistics for generation %d ::: Mean Fitness: %f, Max Fitness: %f, Fitness Variance: %f", + generation, populationStatisticalSummary.getMeanFitness(), populationStatisticalSummary.getMaxFitness(), + populationStatisticalSummary.getFitnessVariance()); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java new file mode 100644 index 0000000000..f2e0d4a83b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/listener/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.listener; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java new file mode 100644 index 0000000000..3e41fc20c9 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicy.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * This abstraction represents an abstract mutation policy for ListChromosomes. + * @param genotype of chromosome + * @param

phenotype of chromosome + * @since 4.0 + */ +public abstract class AbstractListChromosomeMutationPolicy implements MutationPolicy

{ + + /** + * Mutate the given chromosome. Randomly changes few genes depending on mutation + * rate. + * @param original the original chromosome. + * @param mutationRate the rate of mutation per gene + * @return the mutated chromosome. + */ + @Override + public Chromosome

mutate(Chromosome

original, double mutationRate) { + // check for validity. + checkValidity(original); + + @SuppressWarnings("unchecked") + final AbstractListChromosome chromosome = (AbstractListChromosome) original; + final List newRep = new ArrayList<>(chromosome.getRepresentation()); + + final Set mutableGeneIndexes = getMutableGeneIndexes(chromosome.getLength(), mutationRate); + for (int mutableGeneIndex : mutableGeneIndexes) { + newRep.set(mutableGeneIndex, mutateGene(newRep.get(mutableGeneIndex))); + } + + return chromosome.newChromosome(newRep); + } + + /** + * Checks input chromosome validity. + * @param original chromosome to be mutated + */ + protected abstract void checkValidity(Chromosome

original); + + /** + * Selects and returns mutable gene indexes based on mutation rate. + * @param length no of alleles/genes in chromosome + * @param mutationRate mutation rate of the allele/gene + * @return mutable gene indexes + */ + protected Set getMutableGeneIndexes(int length, double mutationRate) { + + // calculate the total mutation rate of all the alleles i.e. chromosome. + final double chromosomeMutationRate = mutationRate * length; + final Set indexSet = new HashSet<>(); + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + + // if chromosomeMutationRate >= 1 then more than one allele will be mutated. + if (chromosomeMutationRate >= 1) { + final int noOfMutation = (int) Math.round(chromosomeMutationRate); + while (indexSet.size() < noOfMutation) { + indexSet.add(randomProvider.nextInt(length)); + } + } else if (randomProvider.nextDouble() < chromosomeMutationRate) { + indexSet.add(randomProvider.nextInt(length)); + } + + return indexSet; + } + + /** + * Mutates an individual gene/allele. + * @param originalValue the original value of gene + * @return mutated value of gene + */ + protected abstract T mutateGene(T originalValue); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java new file mode 100644 index 0000000000..2470f38022 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/BinaryMutation.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * Mutation for {@link BinaryChromosome}s. Randomly changes few genes. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class BinaryMutation

implements MutationPolicy

{ + + /** + * {@inheritDoc} + */ + @Override + public Chromosome

mutate(Chromosome

original, double mutationRate) { + // check for validity. + checkValidity(original); + final BinaryChromosome

chromosome = (BinaryChromosome

) original; + final long[] representation = chromosome.getRepresentation(); + final long[] newRep = new long[representation.length]; + System.arraycopy(representation, 0, newRep, 0, representation.length); + + final Map> mutableGeneIndexMap = getMutableGeneIndexes(chromosome.getLength(), + mutationRate); + for (Entry> entry : mutableGeneIndexMap.entrySet()) { + final int alleleBlockIndex = entry.getKey(); + long mask = 0; + final Set alleleElementIndexes = mutableGeneIndexMap.get(alleleBlockIndex); + for (int index : alleleElementIndexes) { + mask += index == 0 ? Long.MIN_VALUE : Math.pow(2, Long.SIZE - 1 - index); + } + newRep[alleleBlockIndex] = newRep[alleleBlockIndex] ^ mask; + } +// for (int alleleBlockIndex : mutableGeneIndexMap.keySet()) { +// long mask = 0; +// final Set alleleElementIndexes = mutableGeneIndexMap.get(alleleBlockIndex); +// for (int index : alleleElementIndexes) { +// mask += index == 0 ? Long.MIN_VALUE : Math.pow(2, Long.SIZE - 1 - index); +// } +// newRep[alleleBlockIndex] = newRep[alleleBlockIndex] ^ mask; +// } + + return chromosome.newChromosome(newRep, chromosome.getLength()); + } + + /** + * Checks input chromosome validity. + * @param original chromosome to be mutated + */ + protected void checkValidity(Chromosome

original) { + if (!BinaryChromosome.class.isAssignableFrom(original.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); + } + } + + /** + * Selects and returns mutable gene indexes based on mutation rate. + * @param length no of alleles/genes in chromosome + * @param mutationRate mutation rate of the allele/gene + * @return mutable gene indexes + */ + protected Map> getMutableGeneIndexes(long length, double mutationRate) { + + // calculate the total mutation rate of all the alleles i.e. chromosome. + final double chromosomeMutationRate = mutationRate * length; + final Map> indexMap = new HashMap<>(); + + // if chromosomeMutationRate >= 1 then more than one allele will be mutated. + if (chromosomeMutationRate >= 1) { + final int noOfMutation = (int) Math.round(chromosomeMutationRate); + final Set mutationIndexes = new HashSet<>(); + for (int i = 0; i < noOfMutation; i++) { + final long mutationIndex = generateMutationIndex(length, mutationIndexes); + mutationIndexes.add(mutationIndex); + updateIndexMap(indexMap, length, mutationIndex); + } + } else if (RandomProviderManager.getRandomProvider().nextDouble() < chromosomeMutationRate) { + updateIndexMap(indexMap, length); + } + return indexMap; + } + + private long generateMutationIndex(long length, Set mutationIndexes) { + long mutationIndex = 0; + do { + mutationIndex = RandomProviderManager.getRandomProvider().nextLong(length); + } while (mutationIndexes.contains(mutationIndex)); + return mutationIndex; + } + + private void updateIndexMap(Map> indexMap, long length, long mutationIndex) { + final int offset = (int) (length % Long.SIZE == 0 ? 0 : Long.SIZE - length % Long.SIZE); + final long offsettedMutableAlleleIndex = offset + mutationIndex; + + final int alleleBlockIndex = (int) (offsettedMutableAlleleIndex / Long.SIZE); + + if (!indexMap.containsKey(alleleBlockIndex)) { + indexMap.put(alleleBlockIndex, new HashSet<>()); + } + final int alleleElementIndex = (int) (offsettedMutableAlleleIndex % Long.SIZE); + + indexMap.get(alleleBlockIndex).add(alleleElementIndex); + } + + private void updateIndexMap(Map> indexMap, long length) { + updateIndexMap(indexMap, length, RandomProviderManager.getRandomProvider().nextLong(length)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java new file mode 100644 index 0000000000..2ff7f0f893 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutation.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * Mutation for {@link IntegralValuedChromosome}. Randomly changes few genes. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class IntegralValuedMutation

extends AbstractListChromosomeMutationPolicy { + + /** minimum acceptable value of allele. **/ + private final int min; + + /** maximum acceptable value of allele. **/ + private final int max; + + /** + * @param min minimum value of allele + * @param max maximum value of allele + */ + public IntegralValuedMutation(final int min, final int max) { + this.min = min; + this.max = max; + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + } + + /** + * Returns the minimum acceptable value. + * @return minimum + */ + public int getMin() { + return min; + } + + /** + * Returns the maximum acceptable value. + * @return maximum + */ + public int getMax() { + return max; + } + + /** + * {@inheritDoc} + */ + @Override + protected void checkValidity(Chromosome

original) { + if (!IntegralValuedChromosome.class.isAssignableFrom(original.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); + } + final IntegralValuedChromosome

chromosome = (IntegralValuedChromosome

) original; + if (chromosome.getMin() != this.min || chromosome.getMax() != this.max) { + throw new GeneticException(GeneticException.ILLEGAL_RANGE, this.min, this.max, chromosome.getMin(), + chromosome.getMax()); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Integer mutateGene(Integer originalValue) { + return min + RandomProviderManager.getRandomProvider().nextInt(max - min); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java new file mode 100644 index 0000000000..096e419a58 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/MutationPolicy.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; + +/** + * Algorithm used to mutate a chromosome. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface MutationPolicy

{ + + /** + * Mutate the given chromosome. + * @param original the original chromosome. + * @param mutationRate The probability of mutation + * @return the mutated chromosome. + */ + Chromosome

mutate(Chromosome

original, double mutationRate); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java new file mode 100644 index 0000000000..3dcbdcf69e --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/RealValuedMutation.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * This class mutates real-valued chromosome. + * @param

phenotype of chromosome + * @since 4.0 + */ +public class RealValuedMutation

extends AbstractListChromosomeMutationPolicy { + + /** minimum value of chromosome gene/allele. **/ + private final double min; + + /** maximum value of chromosome gene/allele. **/ + private final double max; + + /** + * Constructs the mutation operator with normalized range of double values. + */ + public RealValuedMutation() { + this.min = 0d; + this.max = 1d; + } + + /** + * Constructs the mutation operator with provided range of double values. + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + */ + public RealValuedMutation(double min, double max) { + this.min = min; + this.max = max; + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + } + + /** + * Returns the minimum acceptable value. + * @return minimum + */ + public double getMin() { + return min; + } + + /** + * Returns the maximum acceptable value. + * @return maximum + */ + public double getMax() { + return max; + } + + /** + * {@inheritDoc} + */ + @Override + protected void checkValidity(Chromosome

original) { + if (!RealValuedChromosome.class.isAssignableFrom(original.getClass())) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, original.getClass().getSimpleName()); + } + final RealValuedChromosome

chromosome = (RealValuedChromosome

) original; + if (chromosome.getMin() != this.min || chromosome.getMax() != this.max) { + throw new GeneticException(GeneticException.ILLEGAL_RANGE, this.min, this.max, chromosome.getMin(), + chromosome.getMax()); + } + } + + /** + * {@inheritDoc} + */ + @Override + protected Double mutateGene(Double originalValue) { + return min + RandomProviderManager.getRandomProvider().nextDouble() * (max - min); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java new file mode 100644 index 0000000000..30c90e46cb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.mutation; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java new file mode 100644 index 0000000000..555465fff2 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGenerator.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * Generates mutation rate using linear function of relative rank of input + * chromosome in population. + * @param

phenotype of chromosome + */ +public class AdaptiveLinearMutationRateGenerator

implements MutationRateGenerator

{ + + /** minimum crossover rate. **/ + private final double minimumRate; + + /** maximum crossover rate. **/ + private final double maximumRate; + + /** + * @param minimumRate minimum mutation rate + * @param maximumRate maximum mutation rate + */ + public AdaptiveLinearMutationRateGenerator(double minimumRate, double maximumRate) { + this.minimumRate = minimumRate; + this.maximumRate = maximumRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

chromosome, PopulationStatisticalSummary

populationStats, int generation) { + return minimumRate + (maximumRate - minimumRate) * + (1.0 - (double) populationStats.findRank(chromosome) / (populationStats.getPopulationSize() - 1)); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java new file mode 100644 index 0000000000..097e98df8c --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGenerator.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This class represents a constant mutation rate generator. + * @param

phenotype of chromosome + */ +public class ConstantMutationRateGenerator

implements MutationRateGenerator

{ + + /** the constant mutationRate. **/ + private final double mutationRate; + + /** + * @param mutationRate mutation rate + */ + public ConstantMutationRateGenerator(double mutationRate) { + this.mutationRate = mutationRate; + } + + /** + * {@inheritDoc} + */ + @Override + public double generate(Chromosome

chromosome, PopulationStatisticalSummary

populationStats, int generation) { + return mutationRate; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/MutationRateGenerator.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/MutationRateGenerator.java new file mode 100644 index 0000000000..a206e0234f --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/MutationRateGenerator.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; + +/** + * This abstraction represents mutation rate generator. + * @param

phenotype of chromosome + */ +public interface MutationRateGenerator

{ + + /** + * Generates mutation rate based on input params. + * @param chromosome chromosome + * @param populationStats population statictics summary + * @param generation generation count + * @return mutation rate + */ + double generate(Chromosome

chromosome, PopulationStatisticalSummary

populationStats, int generation); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java new file mode 100644 index 0000000000..c7e56cefeb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/mutation/rategenerator/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.mutation.rategenerator; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java new file mode 100644 index 0000000000..40fa263981 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java new file mode 100644 index 0000000000..1622d9c09b --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/ListPopulation.java @@ -0,0 +1,220 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.population; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +/** + * Population of chromosomes represented by a {@link List}. + * + * @param

phenotype of chromosome + * @since 2.0 + */ +public class ListPopulation

implements Population

{ + + /** new line constant. **/ + public static final String NEW_LINE = System.getProperty("line.separator"); + + /** List of chromosomes. */ + private final List> chromosomes; + + /** maximal size of the population. */ + private int populationLimit; + + /** + * Creates a new ListPopulation instance and initializes its inner chromosome + * list. + * + * @param populationLimit maximal size of the population + */ + public ListPopulation(final int populationLimit) { + this(Collections.>emptyList(), populationLimit); + } + + /** + * Creates a new ListPopulation instance. + *

+ * Note: the chromosomes of the specified list are added to the population. + * + * @param chromosomes list of chromosomes to be added to the population + * @param populationLimit maximal size of the population + */ + public ListPopulation(final List> chromosomes, final int populationLimit) { + + Objects.requireNonNull(chromosomes); + + if (populationLimit <= 0) { + throw new GeneticException(GeneticException.NOT_STRICTLY_POSITIVE, populationLimit); + } + if (chromosomes.size() > populationLimit) { + throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, + chromosomes.size(), populationLimit); + } + this.populationLimit = populationLimit; + this.chromosomes = new ArrayList<>(populationLimit); + this.chromosomes.addAll(chromosomes); + } + + /** + * Add a {@link Collection} of chromosomes to this {@link Population}. + * @param chromosomeColl a {@link Collection} of chromosomes + * @since 3.1 + */ + public void addChromosomes(final Collection> chromosomeColl) { + if (chromosomes.size() + chromosomeColl.size() > populationLimit) { + throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, + chromosomes.size(), populationLimit); + } + this.chromosomes.addAll(chromosomeColl); + } + + /** + * Returns an unmodifiable list of the chromosomes in this population. + * @return the unmodifiable list of chromosomes + */ + public List> getChromosomes() { + return Collections.unmodifiableList(chromosomes); + } + + /** + * Access the list of chromosomes. + * @return the list of chromosomes + * @since 3.1 + */ + protected List> getChromosomeList() { + return chromosomes; + } + + /** + * Add the given chromosome to the population. + * @param chromosome the chromosome to add. + */ + @Override + public void addChromosome(final Chromosome

chromosome) { + if (chromosomes.size() >= populationLimit) { + throw new GeneticException(GeneticException.LIST_OF_CHROMOSOMES_BIGGER_THAN_POPULATION_SIZE, + chromosomes.size(), populationLimit); + } + this.chromosomes.add(chromosome); + } + + /** + * Access the fittest chromosome in this population. + * @return the fittest chromosome. + */ + @Override + public Chromosome

getFittestChromosome() { + // best so far + return Collections.max(this.chromosomes); + } + + /** + * Access the maximum population size. + * @return the maximum population size. + */ + @Override + public int getPopulationLimit() { + return this.populationLimit; + } + + /** + * Sets the maximal population size. + * @param populationLimit maximal population size. + */ + public void setPopulationLimit(final int populationLimit) { + if (populationLimit <= 0) { + throw new GeneticException(GeneticException.POPULATION_LIMIT_NOT_POSITIVE, populationLimit); + } + if (populationLimit < chromosomes.size()) { + throw new GeneticException(GeneticException.POPULATION_LIMIT_LESS_THAN_LIST_OF_CHROMOSOMES_SIZE, + populationLimit, chromosomes.size()); + } + this.populationLimit = populationLimit; + } + + /** + * Access the current population size. + * @return the current population size. + */ + @Override + public int getPopulationSize() { + return this.chromosomes.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + final StringBuilder populationStrRepr = new StringBuilder(); + for (Chromosome

chromosome : chromosomes) { + populationStrRepr.append(chromosome.toString()); + populationStrRepr.append(NEW_LINE); + } + return populationStrRepr.toString(); + } + + /** + * Returns an iterator over the unmodifiable list of chromosomes. + *

+ * Any call to {@link Iterator#remove()} will result in a + * {@link UnsupportedOperationException}. + *

+ * + * @return chromosome iterator + */ + @Override + public Iterator> iterator() { + return getChromosomes().iterator(); + } + + /** + * {@inheritDoc} + */ + @Override + public Population

nextGeneration(final double elitismRate) { + final List> oldChromosomes = getChromosomeList(); + + if ((int) (oldChromosomes.size() * elitismRate) == 0) { + // if no of elite chromosome is 0 crete and return an empty population instance. + return new ListPopulation<>(getPopulationLimit()); + } else { + // create a new generation of chromosomes with same parameters and add the elit + // individuals. + final ListPopulation

nextGeneration = new ListPopulation<>(getPopulationLimit()); + + // Sort the chromosome according to ascending order of fitness. + Collections.sort(oldChromosomes); + + // index of the last "not good enough" chromosome + final int boundIndex = (int) Math.ceil((1.0 - elitismRate) * oldChromosomes.size()); + for (int i = boundIndex; i < oldChromosomes.size(); i++) { + nextGeneration.addChromosome(oldChromosomes.get(i)); + } + return nextGeneration; + } + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java new file mode 100644 index 0000000000..65cfe5fbcc --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/Population.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.population; + +import org.apache.commons.math4.ga.chromosome.Chromosome; + +/** + * A collection of chromosomes that facilitates generational evolution. + * + * @param

phenotype of chromosome + * @since 2.0 + */ +public interface Population

extends Iterable> { + + /** + * Access the current population size. + * @return the current population size. + */ + int getPopulationSize(); + + /** + * Access the maximum population size. + * @return the maximum population size. + */ + int getPopulationLimit(); + + /** + * Start the population for the next generation. + * @param elitismRate the Elitism Rate + * @return the beginnings of the next generation. + */ + Population

nextGeneration(double elitismRate); + + /** + * Add the given chromosome to the population. + * @param chromosome the chromosome to add. + */ + void addChromosome(Chromosome

chromosome); + + /** + * Access the fittest chromosome in this population. + * @return the fittest chromosome. + */ + Chromosome

getFittestChromosome(); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java new file mode 100644 index 0000000000..4d77dc9d30 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/population/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.population; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java new file mode 100644 index 0000000000..7c8682ac78 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/SelectionPolicy.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.selection; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.population.Population; + +/** + * Algorithm used to select a chromosome pair from a population. + * @param

phenotype of chromosome + * @since 2.0 + */ +public interface SelectionPolicy

{ + + /** + * Select two chromosomes from the population. + * @param population the population from which the chromosomes are chosen. + * @return the selected chromosomes. + */ + ChromosomePair

select(Population

population); +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java new file mode 100644 index 0000000000..8240f5da26 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/TournamentSelection.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.selection; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.utils.RandomProviderManager; + +/** + * Tournament selection scheme. Each of the two selected chromosomes is selected + * based on n-ary tournament -- this is done by drawing {@link #arity} random + * chromosomes without replacement from the population, and then selecting the + * fittest chromosome among them. + * @param

phenotype of chromosome + * @since 2.0 + */ +public class TournamentSelection

implements SelectionPolicy

{ + + /** number of chromosomes included in the tournament selections. */ + private final int arity; + + /** + * Creates a new TournamentSelection instance. + * + * @param arity how many chromosomes will be drawn to the tournament + */ + public TournamentSelection(final int arity) { + this.arity = arity; + } + + /** + * Select two chromosomes from the population. Each of the two selected + * chromosomes is selected based on n-ary tournament -- this is done by drawing + * {@link #arity} random chromosomes without replacement from the population, + * and then selecting the fittest chromosome among them. + * + * @param population the population from which the chromosomes are chosen. + * @return the selected chromosomes. + */ + @Override + public ChromosomePair

select(final Population

population) { + if (!(population instanceof ListPopulation)) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, population); + } + return new ChromosomePair<>(tournament((ListPopulation

) population), + tournament((ListPopulation

) population)); + } + + /** + * Helper for {@link #select(Population)}. Draw {@link #arity} random + * chromosomes without replacement from the population, and then select the + * fittest chromosome among them. + * + * @param population the population from which the chromosomes are chosen. + * @return the selected chromosome. + */ + private Chromosome

tournament(final ListPopulation

population) { + if (population.getPopulationSize() < this.arity) { + throw new GeneticException(GeneticException.TOO_LARGE, arity, population.getPopulationSize()); + } + + // create a copy of the chromosome list + final List> chromosomes = new ArrayList<>(population.getChromosomes()); + final List> selectedChromosomes = new ArrayList<>(); + + for (int i = 0; i < this.arity; i++) { + // select a random individual and add it to the tournament + final int rind = RandomProviderManager.getRandomProvider().nextInt(chromosomes.size()); + selectedChromosomes.add(chromosomes.get(rind)); + // do not select it again + chromosomes.remove(rind); + } + + // the winner takes it all + return Collections.max(selectedChromosomes); + } + + /** + * Gets the arity (number of chromosomes drawn to the tournament). + * + * @return arity of the tournament + */ + public int getArity() { + return arity; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java new file mode 100644 index 0000000000..b8ba3ae248 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/selection/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.selection; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java new file mode 100644 index 0000000000..e24f3a0d07 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/PopulationStatisticalSummary.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.stats; + +import org.apache.commons.math4.ga.chromosome.Chromosome; + +/** + * This interface represents the statistical summary for population fitness. + * @param

phenotype of chromosome + * @since 4.0 + */ +public interface PopulationStatisticalSummary

{ + + /** + * Returns the arithmetic mean of population fitness. + * @return The mean or Double.NaN if no values have been added. + */ + double getMeanFitness(); + + /** + * Returns the variance of the population fitness. + * @return The variance, Double.NaN if no values have been added or 0.0 for a + * single value set. + */ + double getFitnessVariance(); + + /** + * Returns the minimum fitness of the population. + * @return The max or Double.NaN if no values have been added. + */ + double getMinFitness(); + + /** + * Returns the maximum fitness of the population. + * @return The max or Double.NaN if no values have been added. + */ + double getMaxFitness(); + + /** + * Returns the population size. + * @return The number of available values + */ + long getPopulationSize(); + + /** + * Calculates the rank of chromosome in population based on its fitness. + * @param chromosome chromosome, for which rank would be found + * @return the rank of chromosome + */ + int findRank(Chromosome

chromosome); + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java new file mode 100644 index 0000000000..f40707d99c --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/stats/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.stats; diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java new file mode 100644 index 0000000000..47503354fb --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtils.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.rng.UniformRandomProvider; + +/** + * This interface generates all random representations for chromosomes. + * @since 4.0 + */ +public interface ChromosomeRepresentationUtils { + + /** + * Generates a representation corresponding to a random permutation of length l + * which can be passed to the RandomKey constructor. + * + * @param l length of the permutation + * @return representation of a random permutation + */ + static List randomPermutation(final int l) { + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + final List repr = new ArrayList<>(l); + for (int i = 0; i < l; i++) { + repr.add(randomProvider.nextDouble()); + } + return repr; + } + + /** + * Generates a representation corresponding to an identity permutation of length + * l which can be passed to the RandomKey constructor. + * + * @param l length of the permutation + * @return representation of an identity permutation + */ + static List identityPermutation(final int l) { + final List repr = new ArrayList<>(l); + for (int i = 0; i < l; i++) { + repr.add((double) i / l); + } + return repr; + } + + /** + * Generates a representation of a permutation corresponding to the + * data sorted by comparator. The data is + * not modified during the process. + * + * This is useful if you want to inject some permutations to the initial + * population. + * + * @param type of the data + * @param data list of data determining the order + * @param comparator how the data will be compared + * @return list representation of the permutation corresponding to the + * parameters + */ + static List comparatorPermutation(final List data, final Comparator comparator) { + final List sortedData = new ArrayList<>(data); + Collections.sort(sortedData, comparator); + + return inducedPermutation(data, sortedData); + } + + /** + * Generates a representation of a permutation corresponding to a permutation + * which yields permutedData when applied to + * originalData. + * + * This method can be viewed as an inverse to decode(). + * + * @param type of the data + * @param originalData the original, unpermuted data + * @param permutedData the data, somehow permuted + * @return representation of a permutation corresponding to the permutation + * {@code originalData -> permutedData} + */ + static List inducedPermutation(final List originalData, final List permutedData) { + + if (originalData.size() != permutedData.size()) { + throw new GeneticException(GeneticException.SIZE_MISMATCH, permutedData.size(), originalData.size()); + } + final int l = originalData.size(); + + final List origDataCopy = new ArrayList<>(originalData); + + final Double[] res = new Double[l]; + for (int i = 0; i < l; i++) { + final int index = origDataCopy.indexOf(permutedData.get(i)); + if (index == -1) { + throw new GeneticException(GeneticException.DIFFERENT_ORIG_AND_PERMUTED_DATA); + } + res[index] = (double) i / l; + origDataCopy.set(index, null); + } + return Arrays.asList(res); + } + + /** + * Returns a representation of a random binary array of length + * length. + * @param length length of the array + * @param min minimum inclusive value of allele + * @param max maximum exclusive value of allele + * @return a random binary array of length length + */ + static List randomIntegralRepresentation(final int length, final int min, final int max) { + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + final List rList = new ArrayList<>(length); + for (int j = 0; j < length; j++) { + rList.add(min + randomProvider.nextInt(max - min)); + } + return rList; + } + + /** + * Returns a representation of a random binary array of length + * length. + * @param length length of the array + * @return a random binary array of length length + */ + static long[] randomBinaryRepresentation(final long length) { + if (length > BinaryChromosome.MAX_LENGTH) { + throw new GeneticException(GeneticException.ILLEGAL_ARGUMENT, + "length exceeded the max length " + BinaryChromosome.MAX_LENGTH); + } + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + int elementCount = (int) Math.ceil(length / (double)Long.SIZE); + // random binary list + final long[] representation = new long[elementCount]; + int remainder = (int) (length % Long.SIZE); + representation[0] = remainder == 0 ? randomProvider.nextLong() : + randomProvider.nextLong((long) Math.pow(2, remainder)); + for (int i = 1; i < elementCount; i++) { + representation[i] = randomProvider.nextLong(); + } + return representation; + } + + /** + * Generates a random string representation of chromosome with specified + * characters. + * @param alleles characters representing alleles + * @param length length of chromosome + * @return returns chromosome representation as string + */ + static String randomStringRepresentation(char[] alleles, final long length) { + Objects.requireNonNull(alleles); + final StringBuilder representationStr = new StringBuilder(); + for (int i = 0; i < length; i++) { + representationStr + .append(alleles[(int) (RandomProviderManager.getRandomProvider().nextInt(alleles.length))]); + } + return representationStr.toString(); + } + + /** + * Generates a representation corresponding to a random double values[0..1] of + * length l. + * @param l length of the permutation + * @return representation of a random permutation + */ + static List randomNormalizedDoubleRepresentation(final int l) { + return randomDoubleRepresentation(l, 0, 1); + } + + /** + * Generates a representation corresponding to a random double values of length + * l. + * @param l length of representation + * @param min minimum inclusive value of chromosome gene + * @param max maximum exclusive value of chromosome gene + * @return representation as List of Double + */ + static List randomDoubleRepresentation(final int l, double min, double max) { + if (min >= max) { + throw new GeneticException(GeneticException.TOO_LARGE, min, max); + } + final double range = max - min; + final UniformRandomProvider randomProvider = RandomProviderManager.getRandomProvider(); + final List repr = new ArrayList<>(l); + for (int i = 0; i < l; i++) { + repr.add(min + randomProvider.nextDouble() * range); + } + return repr; + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java new file mode 100644 index 0000000000..0c01896524 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/RandomProviderManager.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.math4.ga.utils; + +import org.apache.commons.rng.UniformRandomProvider; +import org.apache.commons.rng.simple.RandomSource; +import org.apache.commons.rng.simple.ThreadLocalRandomSource; + +/** + * An utility to generate per thread {@link UniformRandomProvider} instance. + * @since 4.0 + */ +public final class RandomProviderManager { + + /** The default RandomSource for random number generation. **/ + private static RandomSource randomSource = RandomSource.XO_RO_SHI_RO_128_PP; + + /** + * constructs the singleton instance. + */ + private RandomProviderManager() { + } + + /** + * Returns the (static) random generator. + * @return the static random generator shared by GA implementation classes + */ + public static UniformRandomProvider getRandomProvider() { + return ThreadLocalRandomSource.current(RandomProviderManager.randomSource); + } + +} diff --git a/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java new file mode 100644 index 0000000000..0e8c79adc3 --- /dev/null +++ b/commons-math-ga/src/main/java/org/apache/commons/math4/ga/utils/package-info.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This package provides Genetic Algorithms components and implementations. + */ +package org.apache.commons.math4.ga.utils; diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java new file mode 100644 index 0000000000..99a378542a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestBinaryOneMax.java @@ -0,0 +1,187 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.FixedGenerationCount; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.OnePointBinaryCrossover; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.Decoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.listener.ConvergenceListener; +import org.apache.commons.math4.ga.listener.ConvergenceListenerRegistry; +import org.apache.commons.math4.ga.mutation.BinaryMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * This is also an example of usage. + */ +public class GeneticAlgorithmTestBinaryOneMax { + + // parameters for the GA + private static final int DIMENSION = 50; + private static final int POPULATION_SIZE = 50; + private static final int NUM_GENERATIONS = 50; + private static final double CROSSOVER_RATE = 1; + private static final double MUTATION_RATE = 0.1; + private static final int TOURNAMENT_ARITY = 2; + + @Test + public void test() { + removeListeners(); + + // initialize a new genetic algorithm + GeneticAlgorithm> ga = new GeneticAlgorithm<>(new OnePointBinaryCrossover>(), + CROSSOVER_RATE, new BinaryMutation>(), MUTATION_RATE, + new TournamentSelection>(TOURNAMENT_ARITY)); + + Assertions.assertEquals(0, ga.getGenerationsEvolved()); + + // initial population + Population> initial = randomPopulation(); + // stopping conditions + StoppingCondition> stopCond = new FixedGenerationCount<>(NUM_GENERATIONS); + + // best initial chromosome + Chromosome> bestInitial = initial.getFittestChromosome(); + + // run the algorithm + Population> finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + Chromosome> bestFinal = finalPopulation.getFittestChromosome(); + + // the only thing we can test is whether the final solution is not worse than + // the initial one + // however, for some implementations of GA, this need not be true :) + + Assertions.assertTrue(bestFinal.compareTo(bestInitial) > 0); + Assertions.assertEquals(NUM_GENERATIONS, ga.getGenerationsEvolved()); + + } + + private void removeListeners() { + try { + ConvergenceListenerRegistry registry = ConvergenceListenerRegistry.getInstance(); + Field listenersField = registry.getClass().getDeclaredField("listeners"); + boolean accessible = listenersField.isAccessible(); + if (!accessible) { + listenersField.setAccessible(true); + } + @SuppressWarnings("unchecked") + List> listeners = (List>) listenersField + .get(ConvergenceListenerRegistry.getInstance()); + listeners.clear(); + listenersField.setAccessible(accessible); + } catch (NoSuchFieldException | SecurityException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + /** + * Initializes a random population. + */ + private ListPopulation> randomPopulation() { + List>> popList = new LinkedList<>(); + + for (int i = 0; i < POPULATION_SIZE; i++) { + BinaryChromosome> randChrom = new FindOnes( + ChromosomeRepresentationUtils.randomBinaryRepresentation(DIMENSION), DIMENSION); + popList.add(randChrom); + } + return new ListPopulation<>(popList, popList.size()); + } + + /** + * Chromosomes represented by a binary chromosome. + * + * The goal is to set all bits (genes) to 1. + */ + private class FindOnes extends BinaryChromosome> { + + FindOnes(long[] representation, long length) { + super(representation, length, new OneMaxFitnessFunction(), new OneMaxDecoder()); + } + } + + private class OneMaxFitnessFunction implements FitnessFunction> { + + @Override + public double compute(List decodedChromosome) { + double value = 0; + for (Integer allele : decodedChromosome) { + value += allele; + } + return value; + } + + } + + private class OneMaxDecoder implements Decoder> { + + @Override + public List decode(Chromosome> chromosome) { + BinaryChromosome> binaryChromosome = (BinaryChromosome>) chromosome; + List phenotype = new ArrayList<>(); + long[] representation = binaryChromosome.getRepresentation(); + for (int i = 0; i < representation.length; i++) { + String value = Long.toUnsignedString(representation[i], 2); + for (int j = 64 - value.length(); j > 0; j--) { + phenotype.add(Integer.valueOf(0)); + } + for (int j = 0; j < value.length(); j++) { + phenotype.add(Integer.parseInt("" + value.charAt(j))); + } + } + return phenotype; + } + } + + @Test + public void testCrossoverRate() { + Assertions.assertThrows(GeneticException.class, () -> { + new GeneticAlgorithm<>(new OnePointCrossover<>(), 1.5, new BinaryMutation<>(), .01, + new TournamentSelection<>(10)); + }); + } + + @Test + public void testMutationRate() { + Assertions.assertThrows(GeneticException.class, () -> { + new GeneticAlgorithm<>(new OnePointCrossover<>(), .5, new BinaryMutation<>(), 1.5, + new TournamentSelection<>(10)); + }); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java new file mode 100644 index 0000000000..4bfb2ec047 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/GeneticAlgorithmTestPermutations.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.convergence.FixedGenerationCount; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.crossover.OnePointCrossover; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.fitness.FitnessFunction; +import org.apache.commons.math4.ga.mutation.RealValuedMutation; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.selection.TournamentSelection; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * This is also an example of usage. + * + * This algorithm does "stochastic sorting" of a sequence 0,...,N. + * + */ +public class GeneticAlgorithmTestPermutations { + + // parameters for the GA + private static final int DIMENSION = 20; + private static final int POPULATION_SIZE = 80; + private static final int NUM_GENERATIONS = 200; + private static final double ELITISM_RATE = 0.2; + private static final double CROSSOVER_RATE = 1; + private static final double MUTATION_RATE = 0.08; + private static final int TOURNAMENT_ARITY = 2; + + // numbers from 0 to N-1 + private static final List sequence = new ArrayList<>(); + static { + for (int i = 0; i < DIMENSION; i++) { + sequence.add(i); + } + } + + @Test + public void test() { + // to test a stochastic algorithm is hard, so this will rather be an usage + // example + + // initialize a new genetic algorithm + GeneticAlgorithm> ga = new GeneticAlgorithm<>(new OnePointCrossover>(), + CROSSOVER_RATE, new RealValuedMutation>(), MUTATION_RATE, + new TournamentSelection>(TOURNAMENT_ARITY), ELITISM_RATE); + + // initial population + Population> initial = randomPopulation(); + // stopping conditions + StoppingCondition> stopCond = new FixedGenerationCount<>(NUM_GENERATIONS); + + // best initial chromosome + Chromosome> bestInitial = initial.getFittestChromosome(); + + // run the algorithm + Population> finalPopulation = ga.evolve(initial, stopCond); + + // best chromosome from the final population + Chromosome> bestFinal = finalPopulation.getFittestChromosome(); + + // the only thing we can test is whether the final solution is not worse than + // the initial one + // however, for some implementations of GA, this need not be true :) + + Assertions.assertTrue(bestFinal.compareTo(bestInitial) > 0); + + } + + /** + * Initializes a random population + */ + private static Population> randomPopulation() { + List>> popList = new ArrayList<>(); + for (int i = 0; i < POPULATION_SIZE; i++) { + Chromosome> randChrom = new MinPermutations( + ChromosomeRepresentationUtils.randomPermutation(DIMENSION)); + popList.add(randChrom); + } + return new ListPopulation>(popList, popList.size()); + } + + /** + * Chromosomes representing a permutation of (0,1,2,...,DIMENSION-1). + * + * The goal is to sort the sequence. + */ + private static class MinPermutations extends RealValuedChromosome> { + + MinPermutations(List representation) { + super(representation, new MinPermutationsFitnessFunction(), new RandomKeyDecoder<>(sequence)); + } + + @Override + public RealValuedChromosome> newChromosome(List chromosomeRepresentation) { + return new MinPermutations(chromosomeRepresentation); + } + + } + + private static class MinPermutationsFitnessFunction implements FitnessFunction> { + + @Override + public double compute(List decodedChromosome) { + double res = 0.0; + for (int i = 0; i < decodedChromosome.size(); i++) { + int value = decodedChromosome.get(i); + if (value != i) { + // bad position found + res += Math.abs(value - i); + } + } + // the most fitted chromosome is the one with minimal error + // therefore we must return negative value + return -res; + } + + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java new file mode 100644 index 0000000000..a682024bab --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/AbstractChromosomeTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractChromosomeTest { + + @Test + public void testGetFitness() { + Chromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { + }; + Assertions.assertEquals(1, c1.evaluate(), .001); + } + + @Test + public void testDecode() { + Chromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { + }; + Assertions.assertEquals("1", c1.decode()); + } + + @Test + public void testCompareTo() { + Chromosome c1 = new AbstractChromosome(chromosome -> 0, chromosome -> "0") { + }; + Chromosome c2 = new AbstractChromosome(chromosome -> 10, chromosome -> "10") { + }; + Chromosome c3 = new AbstractChromosome(chromosome -> 10, chromosome -> "10") { + }; + + Assertions.assertTrue(c1.compareTo(c2) < 0); + Assertions.assertTrue(c2.compareTo(c1) > 0); + Assertions.assertEquals(0, c3.compareTo(c2)); + Assertions.assertEquals(0, c2.compareTo(c3)); + } + + @Test + public void testIsSame() { + AbstractChromosome c1 = new AbstractChromosome(chromosome -> 1, chromosome -> "1") { + }; + AbstractChromosome c2 = new AbstractChromosome(chromosome -> 2, chromosome -> "2") { + }; + AbstractChromosome c3 = new AbstractChromosome(chromosome -> 3, chromosome -> "1") { + }; + Assertions.assertTrue(c1.isSame(c3)); + Assertions.assertFalse(c1.isSame(c2)); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java new file mode 100644 index 0000000000..ef6669ae25 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/BinaryChromosomeTest.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class BinaryChromosomeTest { + + @Test + public void testInvalidConstructor() { + Assertions.assertThrows(GeneticException.class, () -> { + new BinaryChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), Long.MAX_VALUE, + c -> 0, c -> "0"); + }); + Assertions.assertThrows(GeneticException.class, () -> { + new BinaryChromosome(ChromosomeRepresentationUtils.randomBinaryRepresentation(10), 100, c -> 0, + c -> "0"); + }); + } + + @Test + public void testRandomConstructor() { + for (int i = 0; i < 20; i++) { + BinaryChromosome.randomChromosome(10, c -> 1, new DummyListChromosomeDecoder<>("1")); + } + } + + @Test + public void testGetStringRepresentation() { + int length = 10; + int startToEndGap = 1; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 64; + startToEndGap = 10; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 100; + startToEndGap = 50; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 128; + startToEndGap = 70; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 250; + startToEndGap = 128; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 350; + startToEndGap = 228; + testStringRepresentationWithRanges(length, startToEndGap); + + length = 450; + startToEndGap = 108; + testStringRepresentationWithRanges(length, startToEndGap); + + } + + private void testStringRepresentationWithRanges(int length, int startToEndGap) { + for (int i = 0; i < 50; i++) { + String representationStr = ChromosomeRepresentationUtils.randomStringRepresentation(new char[] {'0', '1'}, + length); + BinaryChromosome chromosome = new BinaryChromosome<>(representationStr, c -> 0, c -> "0"); + Assertions.assertEquals(representationStr, chromosome.getStringRepresentation()); + for (int j = 0; j < length - startToEndGap; j++) { + int index = (int) ((length + 1 - startToEndGap) * Math.random()); + Assertions.assertEquals(representationStr.substring(index, index + startToEndGap), + chromosome.getStringRepresentation(index, index + startToEndGap)); + } + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java new file mode 100644 index 0000000000..f43a27ccb8 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/ChromosomePairTest.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class ChromosomePairTest { + + @Test + public void testChromosomePair() { + Chromosome chromosome1 = new AbstractChromosome(c -> 0, c -> "0") { + }; + Chromosome chromosome2 = new AbstractChromosome(c -> 1, c -> "1") { + }; + ChromosomePair chromosomePair = new ChromosomePair<>(chromosome1, chromosome2); + + Assertions.assertEquals(chromosomePair.getFirst(), chromosome1); + Assertions.assertEquals(chromosomePair.getSecond(), chromosome2); + + Assertions.assertNotNull(chromosomePair.toString()); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java new file mode 100644 index 0000000000..e2a89fe4db --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/IntegralValuedChromosomeTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; + +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IntegralValuedChromosomeTest { + + @Test + public void testIntegralValuedChromosome() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + Assertions.assertEquals(min, chromosome.getMin()); + Assertions.assertEquals(max, chromosome.getMax()); + + IntegralValuedChromosome chromosome1 = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max).toArray(new Integer[10]), + c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); + Assertions.assertEquals(min, chromosome1.getMin()); + Assertions.assertEquals(max, chromosome1.getMax()); + } + + @Test + public void testCheckValidity() { + int min = 0; + int max = 10; + Assertions.assertThrows(GeneticException.class, () -> { + new IntegralValuedChromosome<>(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), + c -> 0, new DummyListChromosomeDecoder<>("0"), max, min); + }); + } + + @Test + public void testCheckValidity1() { + int min = 0; + int max = 10; + Assertions.assertThrows(GeneticException.class, () -> { + new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min - 10, max + 10), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + }); + + } + + @Test + public void testNewChromosome() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + IntegralValuedChromosome newChromosome = chromosome + .newChromosome(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max)); + Assertions.assertEquals(chromosome.getMin(), newChromosome.getMin()); + Assertions.assertEquals(chromosome.getMax(), newChromosome.getMax()); + Assertions.assertEquals(chromosome.getDecoder(), newChromosome.getDecoder()); + Assertions.assertEquals(chromosome.getFitnessFunction(), newChromosome.getFitnessFunction()); + + } + + @Test + public void testRandomChromosome() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = IntegralValuedChromosome.randomChromosome(10, c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + Assertions.assertEquals(min, chromosome.getMin()); + Assertions.assertEquals(max, chromosome.getMax()); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java new file mode 100644 index 0000000000..333e2caa32 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/chromosome/RealValuedChromosomeTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.chromosome; + +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RealValuedChromosomeTest { + + @Test + public void test() { + for (int i = 0; i < 10; i++) { + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1), c1 -> 1, + new DummyListChromosomeDecoder<>("1")); + new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1).toArray(new Double[10]), c -> 0, + new DummyListChromosomeDecoder<>("0")); + } + } + + @Test + public void testNewChromosome() { + for (int i = 0; i < 10; i++) { + RealValuedChromosome chromosome = new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1), c1 -> 1, + new DummyListChromosomeDecoder<>("1")); + chromosome.newChromosome(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, 0, 1)); + } + } + + @Test + public void testRandomChromosome() { + for (int i = 0; i < 10; i++) { + RealValuedChromosome.randomChromosome(5, c -> 0, new DummyListChromosomeDecoder<>("0"), 0, 2); + } + } + + @Test + public void testCheckValidity() { + int min = 0; + int max = 10; + Assertions.assertThrows(GeneticException.class, () -> { + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), max, min); + }); + } + + @Test + public void testCheckValidity1() { + int min = 0; + int max = 10; + Assertions.assertThrows(GeneticException.class, () -> { + new RealValuedChromosome<>(ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min - 10, max + 10), + c -> 0, new DummyListChromosomeDecoder<>("0"), min, max); + }); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java new file mode 100644 index 0000000000..e2681ab8a9 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedElapsedTimeTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.concurrent.TimeUnit; + +import org.apache.commons.math4.ga.convergence.FixedElapsedTime; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class FixedElapsedTimeTest { + + @Test + public void testIsSatisfied() { + final Population pop = new ListPopulation<>(10); + + final long start = System.nanoTime(); + final long duration = 3; + final FixedElapsedTime tec = new FixedElapsedTime(duration); + + while (!tec.isSatisfied(pop)) { + try { + Thread.sleep(50); + } catch (InterruptedException e) { + // ignore + } + } + + final long end = System.nanoTime(); + final long elapsedTime = end - start; + final long diff = Math.abs(elapsedTime - TimeUnit.SECONDS.toNanos(duration)); + + Assertions.assertTrue(diff < TimeUnit.MILLISECONDS.toNanos(100)); + } + + @Test + public void testNegativeTime() { + Assertions.assertThrows(GeneticException.class, () -> { + new FixedElapsedTime<>(-10); + }); + } +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java new file mode 100644 index 0000000000..5bf1567545 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/FixedGenerationCountTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import org.apache.commons.math4.ga.convergence.FixedGenerationCount; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class FixedGenerationCountTest { + + @Test + public void testIsSatisfied() { + FixedGenerationCount fgc = new FixedGenerationCount(20); + + int cnt = 0; + Population pop = new ListPopulation<>(10); + + while (!fgc.isSatisfied(pop)) { + cnt++; + } + Assertions.assertEquals(cnt, fgc.getNumGenerations()); + } + + @Test + public void testNegativeGenerationCount() { + Assertions.assertThrows(GeneticException.class, () -> { + new FixedGenerationCount(-1); + }); + } +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java new file mode 100644 index 0000000000..5fffd0650a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedBestFitnessTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.ArrayList; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedBestFitness; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class UnchangedBestFitnessTest { + + @Test + public void testIsSatisfied() { + + final int noOfGenerationsWithUnchangedBestFitness = 5; + StoppingCondition stoppingCondition = new UnchangedBestFitness<>( + noOfGenerationsWithUnchangedBestFitness); + + double[] fitnesses = new double[10]; + for (int i = 0; i < 10; i++) { + fitnesses[i] = i; + } + List> chromosomes = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + final double fitness = fitnesses[i]; + Chromosome ch = new AbstractChromosome(c -> fitness, c -> "Fixed") { + }; + chromosomes.add(ch); + } + Population pop = new ListPopulation<>(chromosomes, 10); + + double initialMaxFitness = new PopulationStatisticalSummaryImpl<>(pop).getMaxFitness(); + + int counter = 0; + while (!stoppingCondition.isSatisfied(pop)) { + counter++; + } + + double maxFitnessAfterConvergence = new PopulationStatisticalSummaryImpl<>(pop).getMaxFitness(); + + Assertions.assertEquals(initialMaxFitness, maxFitnessAfterConvergence, .001); + Assertions.assertEquals(noOfGenerationsWithUnchangedBestFitness, counter); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java new file mode 100644 index 0000000000..6e4042cbfb --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/convergencecond/UnchangedMeanFitnessTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.convergencecond; + +import java.util.ArrayList; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.convergence.StoppingCondition; +import org.apache.commons.math4.ga.convergence.UnchangedMeanFitness; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class UnchangedMeanFitnessTest { + + @Test + public void testIsSatisfied() { + + final int noOfGenerationsWithUnchangedMeanFitness = 5; + StoppingCondition stoppingCondition = new UnchangedMeanFitness<>( + noOfGenerationsWithUnchangedMeanFitness); + + double[] fitnesses = new double[10]; + for (int i = 0; i < 10; i++) { + fitnesses[i] = i; + } + List> chromosomes = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + final double fitness = fitnesses[i]; + Chromosome ch = new AbstractChromosome(c -> fitness, c -> "Fixed") { + }; + chromosomes.add(ch); + } + Population pop = new ListPopulation<>(chromosomes, 10); + + double initialAverageFitness = new PopulationStatisticalSummaryImpl<>(pop).getMeanFitness(); + + int counter = 0; + while (!stoppingCondition.isSatisfied(pop)) { + counter++; + } + + double averageFitnessAfterConvergence = new PopulationStatisticalSummaryImpl<>(pop).getMeanFitness(); + + Assertions.assertEquals(initialAverageFitness, averageFitnessAfterConvergence, .001); + Assertions.assertEquals(noOfGenerationsWithUnchangedMeanFitness, counter); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java new file mode 100644 index 0000000000..c1249b52a5 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractChromosomeCrossoverPolicyTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyChromosome; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractChromosomeCrossoverPolicyTest { + + @Test + public void testCrossoverProbability() { + + CrossoverPolicy crossoverPolicy = new AbstractChromosomeCrossoverPolicy() { + @Override + protected ChromosomePair crossover(Chromosome first, Chromosome second) { + return null; + } + }; + + Chromosome ch1 = new DummyChromosome(); + + Chromosome ch2 = new DummyChromosome(); + + Assertions.assertNull(crossoverPolicy.crossover(ch1, ch2, 1.0)); + Assertions.assertNotNull(crossoverPolicy.crossover(ch1, ch2, 0.0)); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java new file mode 100644 index 0000000000..0739f0d8db --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/AbstractListChromosomeCrossoverPolicyTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyListChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractListChromosomeCrossoverPolicyTest { + + @Test + public void testCrossoverWithNonListChromosome() { + + CrossoverPolicy crossoverPolicy = new AbstractListChromosomeCrossoverPolicy() { + + @Override + protected ChromosomePair mate(AbstractListChromosome first, + AbstractListChromosome second) { + return new ChromosomePair<>(first, second); + } + }; + Chromosome ch1 = new AbstractChromosome(c -> 0, c -> "0") { + }; + + Chromosome ch2 = new AbstractChromosome(c -> 1, c -> "1") { + }; + + Assertions.assertThrows(GeneticException.class, () -> { + crossoverPolicy.crossover(ch1, ch2, 1.0); + }); + + } + + @Test + public void testCrossoverWithUnEqualLengthChromosome() { + + CrossoverPolicy crossoverPolicy = new AbstractListChromosomeCrossoverPolicy() { + + @Override + protected ChromosomePair mate(AbstractListChromosome first, + AbstractListChromosome second) { + return new ChromosomePair<>(first, second); + } + }; + Chromosome ch1 = new DummyListChromosome( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2)); + + Chromosome ch2 = new DummyListChromosome( + ChromosomeRepresentationUtils.randomIntegralRepresentation(12, 0, 2)); + + Assertions.assertThrows(GeneticException.class, () -> { + crossoverPolicy.crossover(ch1, ch2, 1.0); + }); + + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java new file mode 100644 index 0000000000..e8ac82019a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/CycleCrossoverTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyListChromosome; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CycleCrossoverTest { + + @Test + public void testCrossoverExample() { + // taken from + // http://www.rubicite.com/Tutorials/GeneticAlgorithms/CrossoverOperators/CycleCrossoverOperator.aspx + final Integer[] p1 = new Integer[] {8, 4, 7, 3, 6, 2, 5, 1, 9, 0}; + final Integer[] p2 = new Integer[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy cp = new CycleCrossover(); + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation() + .toArray(new Integer[p1.length]); + final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation() + .toArray(new Integer[p2.length]); + + final Integer[] c1e = new Integer[] {8, 1, 2, 3, 4, 5, 6, 7, 9, 0}; + final Integer[] c2e = new Integer[] {0, 4, 7, 3, 6, 2, 5, 1, 8, 9}; + + Assertions.assertArrayEquals(c1e, c1); + Assertions.assertArrayEquals(c2e, c2); + } + + @Test + public void testCrossoverExample2() { + // taken from http://www.scribd.com/doc/54206412/32/Cycle-crossover + final Integer[] p1 = new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9}; + final Integer[] p2 = new Integer[] {9, 3, 7, 8, 2, 6, 5, 1, 4}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy cp = new CycleCrossover(); + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation() + .toArray(new Integer[p1.length]); + final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation() + .toArray(new Integer[p2.length]); + + final Integer[] c1e = new Integer[] {1, 3, 7, 4, 2, 6, 5, 8, 9}; + final Integer[] c2e = new Integer[] {9, 2, 3, 8, 5, 6, 7, 1, 4}; + + Assertions.assertArrayEquals(c1e, c1); + Assertions.assertArrayEquals(c2e, c2); + } + + @Test + public void testCrossover() { + final Integer[] p1 = new Integer[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + final Integer[] p2 = new Integer[] {10, 9, 8, 7, 6, 5, 4, 3, 2, 1}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy cp = new CycleCrossover(true); + + for (int i = 0; i < 20; i++) { + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation() + .toArray(new Integer[p1.length]); + final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation() + .toArray(new Integer[p2.length]); + + int index = 0; + // Determine if it is in the same spot as in the first parent, if + // not it comes from the second parent. + for (final Integer j : c1) { + if (!p1[index].equals(j)) { + Assertions.assertEquals(j, p2[index]); + } else { + Assertions.assertEquals(j, p1[index]); + } + index++; + } + + // Same as above only for the second parent. + index = 0; + for (final Integer k : c2) { + if (p2[index] != k) { + Assertions.assertEquals(k, p1[index]); + } else { + Assertions.assertEquals(k, p2[index]); + } + index++; + } + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java new file mode 100644 index 0000000000..35a55b025b --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/NPointCrossoverTest.java @@ -0,0 +1,127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NPointCrossoverTest { + + @Test + public void testNumberIsTooLargeException() { + final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + final Integer[] p2 = new Integer[] {0, 1, 1, 0, 1, 0, 1, 1, 1}; + + final IntegralValuedChromosome p1c = new IntegralValuedChromosome(p1, c -> 0, + new DummyListChromosomeDecoder("0"), 0, 2); + final IntegralValuedChromosome p2c = new IntegralValuedChromosome(p2, c -> 0, + new DummyListChromosomeDecoder("0"), 0, 2); + + final CrossoverPolicy cp = new NPointCrossover(15); + Assertions.assertThrows(GeneticException.class, () -> { + cp.crossover(p1c, p2c, 1.0); + }); + } + + @Test + public void testCrossoverInvalidFixedLengthChromosomeFirst() { + final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + final IntegralValuedChromosome p1c = new IntegralValuedChromosome(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + final AbstractChromosome p2c = new AbstractChromosome(chromosome -> 0, + new DummyListChromosomeDecoder<>("0")) { + }; + + final CrossoverPolicy cp = new NPointCrossover(1); + + Assertions.assertThrows(GeneticException.class, () -> { + cp.crossover(p1c, p2c, 1.0); + }); + + } + + @Test + public void testCrossoverInvalidFixedLengthChromosomeSecond() { + final Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + final IntegralValuedChromosome p2c = new IntegralValuedChromosome(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + final AbstractChromosome p1c = new AbstractChromosome(chromosome -> 0, + new DummyListChromosomeDecoder<>("0")) { + }; + + final CrossoverPolicy cp = new NPointCrossover(1); + + Assertions.assertThrows(GeneticException.class, () -> { + cp.crossover(p1c, p2c, 1.0); + }); + + } + + @Test + public void testCrossover() { + Integer[] p1 = new Integer[] {1, 0, 1, 0, 1, 0, 1, 0, 1}; + Integer[] p2 = new Integer[] {0, 1, 0, 1, 0, 1, 0, 1, 0}; + + IntegralValuedChromosome p1c = new IntegralValuedChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + IntegralValuedChromosome p2c = new IntegralValuedChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + + final int order = 3; + NPointCrossover npc = new NPointCrossover<>(order); + + // the two parent chromosomes are different at each position, so it is easy to + // detect + // the number of crossovers that happened for each child + for (int i = 0; i < 20; i++) { + ChromosomePair pair = npc.crossover(p1c, p2c, 1.0); + Assertions.assertEquals(order, + detectCrossoverPoints(p1c, p2c, (IntegralValuedChromosome) pair.getFirst())); + Assertions.assertEquals(order, + detectCrossoverPoints(p2c, p1c, (IntegralValuedChromosome) pair.getSecond())); + } + } + + private int detectCrossoverPoints(IntegralValuedChromosome p1, + IntegralValuedChromosome p2, + IntegralValuedChromosome c) { + int crossovers = 0; + final int length = p1.getLength(); + + final List p1Rep = p1.getRepresentation(); + final List p2Rep = p2.getRepresentation(); + final List cRep = c.getRepresentation(); + + List rep = p1Rep; + for (int i = 0; i < length; i++) { + if (rep.get(i) != cRep.get(i)) { + crossovers++; + rep = rep == p1Rep ? p2Rep : p1Rep; + } + } + + return crossovers; + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.java new file mode 100644 index 0000000000..21f26a6cba --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointBinaryCrossoverTest.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +public class OnePointBinaryCrossoverTest { + + @Test + public void testCrossover() { + + OnePointBinaryCrossover opc = new OnePointBinaryCrossover<>(); + + // test a stochastic method. + for (int i = 0; i < 1000; i++) { + long length = 32 + (long) (200 * Math.random()); + BinaryChromosome p1c = new BinaryChromosome<>( + ChromosomeRepresentationUtils.randomBinaryRepresentation(length), length, c -> 0, c -> "0"); + BinaryChromosome p2c = new BinaryChromosome<>( + ChromosomeRepresentationUtils.randomBinaryRepresentation(length), length, c -> 0, c -> "0"); + + ChromosomePair pair = opc.crossover(p1c, p2c, 1.0); + + long[] p1 = p1c.getRepresentation(); + long[] p2 = p2c.getRepresentation(); + + long[] c1 = ((BinaryChromosome) pair.getFirst()).getRepresentation(); + long[] c2 = ((BinaryChromosome) pair.getSecond()).getRepresentation(); + + final int offset = (int) (length % 64 == 0 ? 0 : 64 - (length % 64)); + + // first and last values will be the same + Assertions.assertEquals(prependZero(Long.toBinaryString(p1[0])).charAt(offset), + prependZero(Long.toBinaryString(c1[0])).charAt(offset)); + Assertions.assertEquals(prependZero(Long.toBinaryString(p2[0])).charAt(offset), + prependZero(Long.toBinaryString(c2[0])).charAt(offset)); + Assertions.assertEquals(prependZero(Long.toBinaryString(p1[p1.length - 1])).charAt(63), + prependZero(Long.toBinaryString(c2[c2.length - 1])).charAt(63)); + Assertions.assertEquals(prependZero(Long.toBinaryString(p2[p2.length - 1])).charAt(63), + prependZero(Long.toBinaryString(c1[c1.length - 1])).charAt(63)); + + } + } + + private String prependZero(String value) { + StringBuilder modValue = new StringBuilder(); + for (int i = 64 - value.length(); i > 0; i--) { + modValue.append('0'); + } + modValue.append(value); + + return modValue.toString(); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointCrossoverTest.java new file mode 100644 index 0000000000..a97c74286a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OnePointCrossoverTest.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class OnePointCrossoverTest { + + @Test + public void testCrossover() { + @SuppressWarnings("boxing") + Integer[] p1 = new Integer[] {1, 0, 1, 0, 0, 1, 0, 1, 1}; + @SuppressWarnings("boxing") + Integer[] p2 = new Integer[] {0, 1, 1, 0, 1, 0, 1, 1, 1}; + + IntegralValuedChromosome p1c = new IntegralValuedChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + IntegralValuedChromosome p2c = new IntegralValuedChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + + OnePointCrossover opc = new OnePointCrossover<>(); + + // how to test a stochastic method? + for (int i = 0; i < 20; i++) { + ChromosomePair pair = opc.crossover(p1c, p2c, 1.0); + + Integer[] c1 = new Integer[p1.length]; + Integer[] c2 = new Integer[p2.length]; + + c1 = ((IntegralValuedChromosome) pair.getFirst()).getRepresentation().toArray(c1); + c2 = ((IntegralValuedChromosome) pair.getSecond()).getRepresentation().toArray(c2); + + // first and last values will be the same + Assertions.assertEquals(p1[0], c1[0]); + Assertions.assertEquals(p2[0], c2[0]); + Assertions.assertEquals(p1[p1.length - 1], c1[c1.length - 1]); + Assertions.assertEquals(p2[p2.length - 1], c2[c2.length - 1]); + // moreover, in the above setting, the 2nd, 3rd and 7th values will be the same + Assertions.assertEquals(p1[2], c1[2]); + Assertions.assertEquals(p2[2], c2[2]); + Assertions.assertEquals(p1[3], c1[3]); + Assertions.assertEquals(p2[3], c2[3]); + Assertions.assertEquals(p1[7], c1[7]); + Assertions.assertEquals(p2[7], c2[7]); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OrderedCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OrderedCrossoverTest.java new file mode 100644 index 0000000000..383f1d8764 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/OrderedCrossoverTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.dummy.DummyListChromosome; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class OrderedCrossoverTest { + + @Test + public void testCrossover() { + final Integer[] p1 = new Integer[] {8, 4, 7, 3, 6, 2, 5, 1, 9, 0}; + final Integer[] p2 = new Integer[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + final DummyListChromosome p1c = new DummyListChromosome(p1); + final DummyListChromosome p2c = new DummyListChromosome(p2); + + final CrossoverPolicy cp = new OrderedCrossover(); + + for (int i = 0; i < 20; i++) { + final Set parentSet1 = new HashSet<>(Arrays.asList(p1)); + final Set parentSet2 = new HashSet<>(Arrays.asList(p2)); + + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final Integer[] c1 = ((DummyListChromosome) pair.getFirst()).getRepresentation() + .toArray(new Integer[p1.length]); + final Integer[] c2 = ((DummyListChromosome) pair.getSecond()).getRepresentation() + .toArray(new Integer[p2.length]); + + Assertions.assertNotSame(p1c, pair.getFirst()); + Assertions.assertNotSame(p2c, pair.getSecond()); + + // make sure that the children have exactly the same elements as their parents + for (int j = 0; j < c1.length; j++) { + Assertions.assertTrue(parentSet1.contains(c1[j])); + parentSet1.remove(c1[j]); + Assertions.assertTrue(parentSet2.contains(c2[j])); + parentSet2.remove(c2[j]); + } + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/UniformCrossoverTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/UniformCrossoverTest.java new file mode 100644 index 0000000000..6bd2f8d818 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/UniformCrossoverTest.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class UniformCrossoverTest { + private static final int LEN = 10000; + private static final List p1 = new ArrayList<>(LEN); + private static final List p2 = new ArrayList<>(LEN); + + @SuppressWarnings("boxing") + @BeforeAll + public static void setUpBeforeClass() { + for (int i = 0; i < LEN; i++) { + p1.add(0); + p2.add(1); + } + } + + @Test + public void testRatioTooLow() { + Assertions.assertThrows(GeneticException.class, () -> { + new UniformCrossover(-0.5d); + }); + } + + @Test + public void testRatioTooHigh() { + Assertions.assertThrows(GeneticException.class, () -> { + new UniformCrossover(1.5d); + }); + } + + @Test + public void testCrossover() { + // test crossover with different ratios + performCrossover(0.5); + performCrossover(0.7); + performCrossover(0.2); + } + + private void performCrossover(double ratio) { + final IntegralValuedChromosome p1c = new IntegralValuedChromosome<>(p1, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + final IntegralValuedChromosome p2c = new IntegralValuedChromosome<>(p2, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2); + + final CrossoverPolicy cp = new UniformCrossover(ratio); + + // make a number of rounds + for (int i = 0; i < 20; i++) { + final ChromosomePair pair = cp.crossover(p1c, p2c, 1.0); + + final List c1 = ((IntegralValuedChromosome) pair.getFirst()).getRepresentation(); + final List c2 = ((IntegralValuedChromosome) pair.getSecond()).getRepresentation(); + + int from1 = 0; + int from2 = 0; + + // check first child + for (int val : c1) { + if (val == 0) { + from1++; + } else { + from2++; + } + } + + Assertions.assertEquals(1.0 - ratio, (double) from1 / LEN, 0.1); + Assertions.assertEquals(ratio, (double) from2 / LEN, 0.1); + + from1 = 0; + from2 = 0; + + // check second child + for (int val : c2) { + if (val == 0) { + from1++; + } else { + from2++; + } + } + + Assertions.assertEquals(ratio, (double) from1 / LEN, 0.1); + Assertions.assertEquals(1.0 - ratio, (double) from2 / LEN, 0.1); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java new file mode 100644 index 0000000000..48099244d6 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/crossover/rategenerator/AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.crossover.rategenerator; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AdaptiveLinearMaximumRankBasedCrossoverRateGeneratorTest { + + @Test + public void testGenerate() { + final double minCrossoverRate = .5; + final double maxCrossoverRate = 1.0; + + IntegralValuedChromosome chromosome1 = IntegralValuedChromosome.randomChromosome(10, c -> 1, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome2 = IntegralValuedChromosome.randomChromosome(10, c -> 2, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome3 = IntegralValuedChromosome.randomChromosome(10, c -> 3, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome4 = IntegralValuedChromosome.randomChromosome(10, c -> 4, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome5 = IntegralValuedChromosome.randomChromosome(10, c -> 5, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + + Population population = new ListPopulation<>(5); + population.addChromosome(chromosome1); + population.addChromosome(chromosome2); + population.addChromosome(chromosome3); + population.addChromosome(chromosome4); + population.addChromosome(chromosome5); + PopulationStatisticalSummary stats = new PopulationStatisticalSummaryImpl<>(population); + + CrossoverRateGenerator crossoverRateGenerator = new AdaptiveLinearMaximumRankBasedCrossoverRateGenerator<>( + minCrossoverRate, maxCrossoverRate); + + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome1, chromosome5, stats, 1), + .00000001); + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome5, chromosome2, stats, 1), + .00000001); + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome3, chromosome5, stats, 1), + .00000001); + Assertions.assertEquals(minCrossoverRate, crossoverRateGenerator.generate(chromosome4, chromosome5, stats, 1), + .00000001); + + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoderTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoderTest.java new file mode 100644 index 0000000000..32b2f05d2d --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/AbstractListChromosomeDecoderTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.dummy.DummyChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractListChromosomeDecoderTest { + + @Test + public void testDecodeWithInvalidChromosomeInstance() { + Decoder decoder = new AbstractListChromosomeDecoder() { + + @Override + protected String decode(AbstractListChromosome chromosome) { + return null; + } + }; + Chromosome ch = new DummyChromosome(); + Assertions.assertThrows(GeneticException.class, () -> { + decoder.decode(ch); + }); + + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoderTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoderTest.java new file mode 100644 index 0000000000..4a2f751f8b --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/RandomKeyDecoderTest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RandomKeyDecoderTest { + + @Test + public void testDecodeChromosome() { + + List sequence = Arrays.asList(new String[] {"a", "b", "c", "d", "e"}); + Double[] keys = new Double[] {0.4, 0.1, 0.5, 0.8, 0.2}; + + RandomKeyDecoder decoder = new RandomKeyDecoder<>(sequence); + RealValuedChromosome> chromosome = new RealValuedChromosome<>(keys, c -> 0, decoder); + List decodedSequence = chromosome.decode(); + + Assertions.assertEquals("b", decodedSequence.get(0)); + Assertions.assertEquals("e", decodedSequence.get(1)); + Assertions.assertEquals("a", decodedSequence.get(2)); + Assertions.assertEquals("c", decodedSequence.get(3)); + Assertions.assertEquals("d", decodedSequence.get(4)); + + } + + @Test + public void testSequenceLength() { + List sequence = Arrays.asList(new String[] {"a", "b", "c", "d", "e", "f"}); + Double[] keys = new Double[] {0.4, 0.1, 0.5, 0.8, 0.2}; + + RandomKeyDecoder decoder = new RandomKeyDecoder<>(sequence); + RealValuedChromosome> chromosome = new RealValuedChromosome<>(keys, c -> 0, decoder); + Assertions.assertThrows(GeneticException.class, () -> { + chromosome.decode(); + }); + } +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoderTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoderTest.java new file mode 100644 index 0000000000..39b6b9eb63 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/decoder/TransparentListChromosomeDecoderTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.decoder; + +import java.util.List; +import java.util.Objects; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TransparentListChromosomeDecoderTest { + + @Test + public void testDecode() { + List rp = ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2); + Chromosome> chromosome = new IntegralValuedChromosome<>(rp, c -> 0, + new TransparentListChromosomeDecoder<>(), 0, 2); + List decodedRp = chromosome.decode(); + Assertions.assertTrue(Objects.equals(rp, decodedRp)); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyChromosome.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyChromosome.java new file mode 100644 index 0000000000..3583b3561c --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyChromosome.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.dummy; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; + +public class DummyChromosome extends AbstractChromosome { + + public DummyChromosome() { + super(c -> 0, c -> "0"); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosome.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosome.java new file mode 100644 index 0000000000..908bb43e78 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosome.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.dummy; + +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; + +/** + * Implementation of ListChromosome for testing purposes + */ +public class DummyListChromosome extends AbstractListChromosome { + + public DummyListChromosome(final Integer[] representation) { + super(representation, chromosome -> 0, new DummyListChromosomeDecoder<>("0")); + } + + public DummyListChromosome() { + super(ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2), chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + } + + public DummyListChromosome(final List representation) { + super(representation, chromosome -> 0, new DummyListChromosomeDecoder<>("0")); + } + + @Override + public DummyListChromosome newChromosome(final List chromosomeRepresentation) { + return new DummyListChromosome(chromosomeRepresentation); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosomeDecoder.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosomeDecoder.java new file mode 100644 index 0000000000..541cd261f0 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/dummy/DummyListChromosomeDecoder.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.dummy; + +import org.apache.commons.math4.ga.chromosome.AbstractListChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.decoder.AbstractListChromosomeDecoder; + +public class DummyListChromosomeDecoder extends AbstractListChromosomeDecoder { + + private String value; + + public DummyListChromosomeDecoder(String value) { + this.value = value; + } + + @Override + protected String decode(AbstractListChromosome chromosome) { + return value; + } + + @Override + protected void checkValidity(Chromosome chromosome) { + // No op + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.java new file mode 100644 index 0000000000..551331a043 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/exception/GeneticExceptionTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.exception; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class GeneticExceptionTest { + + @Test + public void testGeneticExceptionThrowable() { + Assertions.assertThrows(GeneticException.class, () -> { + throw new GeneticException(new NullPointerException()); + }); + } + + @Test + public void testGeneticExceptionStringThrowableObjectArray() { + Assertions.assertThrows(GeneticException.class, () -> { + throw new GeneticException("Nullpointer Exception", new NullPointerException()); + }); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistryTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistryTest.java new file mode 100644 index 0000000000..2b1425849a --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/ConvergenceListenerRegistryTest.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.listener; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ConvergenceListenerRegistryTest { + + @Test + public void testRegister() { + try { + reset(); + ConvergenceListenerRegistry registry = ConvergenceListenerRegistry.getInstance(); + + List> listeners = new ArrayList<>(); + ConvergenceListener convergenceListener = new ConvergenceListener() { + + @Override + public void notify(int generation, Population population) { + // No op + } + }; + listeners.add(convergenceListener); + ConvergenceListener convergenceListener1 = new ConvergenceListener() { + + @Override + public void notify(int generation, Population population) { + // No op + } + }; + listeners.add(convergenceListener1); + registry.addConvergenceListeners(listeners); + Field listenersField = registry.getClass().getDeclaredField("listeners"); + boolean accessible = listenersField.isAccessible(); + if (!accessible) { + listenersField.setAccessible(true); + } + @SuppressWarnings("unchecked") + List> listeners1 = (List>) listenersField + .get(registry); + Assertions.assertSame(listeners1.get(0), convergenceListener); + listenersField.setAccessible(accessible); + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + // No op + } + } + + private void reset() + throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException { + ConvergenceListenerRegistry registry = ConvergenceListenerRegistry.getInstance(); + Field listenersField = registry.getClass().getDeclaredField("listeners"); + boolean accessible = listenersField.isAccessible(); + if (!accessible) { + listenersField.setAccessible(true); + } + @SuppressWarnings("unchecked") + List> listeners = (List>) listenersField + .get(ConvergenceListenerRegistry.getInstance()); + listeners.clear(); + listenersField.setAccessible(accessible); + } + + @Test + public void testNotifyAll() { + try { + reset(); + ConvergenceListenerRegistry registry = ConvergenceListenerRegistry.getInstance(); + ConvergenceListener convergenceListener = new ConvergenceListener() { + + @Override + public void notify(int generation, Population population) { + throw new GeneticException("Test Notify"); + } + }; + registry.addConvergenceListener(convergenceListener); + Assertions.assertThrows(GeneticException.class, () -> { + registry.notifyAll(0, new ListPopulation<>(10)); + }); + Assertions.assertTrue(true); + } catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { + // No op + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLoggerTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLoggerTest.java new file mode 100644 index 0000000000..d59a514810 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/listener/PopulationStatisticsLoggerTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.listener; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class PopulationStatisticsLoggerTest { + + @Test + public void testPopulationStatisticsLogger() { + Population population = new ListPopulation(2); + population.addChromosome( + new IntegralValuedChromosome<>(ChromosomeRepresentationUtils + .randomIntegralRepresentation(10, 0, 10), c -> 0, new DummyListChromosomeDecoder<>("0"), + 0, 10)); + population.addChromosome( + new IntegralValuedChromosome<>(ChromosomeRepresentationUtils + .randomIntegralRepresentation(10, 0, 10), c -> 0, new DummyListChromosomeDecoder<>("0"), + 0, 10)); + PopulationStatisticsLogger logger = new PopulationStatisticsLogger<>(); + logger.notify(1, population); + Assertions.assertTrue(true); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicyTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicyTest.java new file mode 100644 index 0000000000..17c481a200 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/AbstractListChromosomeMutationPolicyTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AbstractListChromosomeMutationPolicyTest { + + @Test + public void testGetMutableGeneIndexes() { + AbstractListChromosomeMutationPolicy chromosomeMutationPolicy = new AbstractListChromosomeMutationPolicy() { + + @Override + protected Integer mutateGene(Integer originalValue) { + return RandomProviderManager.getRandomProvider().nextInt(2); + } + + @Override + protected void checkValidity(Chromosome original) { + // No Op + } + }; + Assertions.assertEquals(1, chromosomeMutationPolicy.getMutableGeneIndexes(10, .1).size()); + chromosomeMutationPolicy.getMutableGeneIndexes(10, .001); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/BinaryMutationTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/BinaryMutationTest.java new file mode 100644 index 0000000000..d2cc2bb7eb --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/BinaryMutationTest.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.BinaryChromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class BinaryMutationTest { + + @Test + public void testCheckValidity() { + BinaryMutation mutation = new BinaryMutation<>(); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(new IntegralValuedChromosome( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, 0, 2), c -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + }); + } + + @Test + public void testMutate() { + BinaryMutation mutation = new BinaryMutation<>(); + + // stochastic testing for single gene mutation :) + for (int i = 0; i < 20; i++) { + BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, + new DummyListChromosomeDecoder<>("0")); + BinaryChromosome mutated = (BinaryChromosome) mutation.mutate(original, .1); + + // one gene should be different + Assertions.assertEquals(1, calculateNoOfMutatedBits(original, mutated)); + } + + // stochastic testing for two gene mutation :) + for (int i = 0; i < 20; i++) { + BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, + c -> "0"); + BinaryChromosome mutated = (BinaryChromosome) mutation.mutate(original, .2); + + // one gene should be different + Assertions.assertEquals(2, calculateNoOfMutatedBits(original, mutated)); + } + + // stochastic testing for three gene mutation :) + for (int i = 0; i < 20; i++) { + BinaryChromosome original = BinaryChromosome.randomChromosome(10, chromosome -> 0, + c -> "0"); + BinaryChromosome mutated = (BinaryChromosome) mutation.mutate(original, .3); + + // three genes should be different + Assertions.assertEquals(3, calculateNoOfMutatedBits(original, mutated)); + } + } + + private int calculateNoOfMutatedBits(BinaryChromosome original, BinaryChromosome mutated) { + int numDifferent = 0; + long[] originalReps = original.getRepresentation(); + long[] mutatedReps = mutated.getRepresentation(); + for (int j = 0; j < originalReps.length; j++) { + long xORValue = originalReps[j] ^ mutatedReps[j]; + String xORValueStr = Long.toBinaryString(xORValue); + for (int k = 0; k < xORValueStr.length(); k++) { + if (xORValueStr.charAt(k) == '1') { + numDifferent++; + } + } + } + return numDifferent; + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutationTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutationTest.java new file mode 100644 index 0000000000..0d6135fdcf --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/IntegralValuedMutationTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class IntegralValuedMutationTest { + + @Test + public void testCheckValidity() { + int min = 0; + int max = 10; + Chromosome chromosome = new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(10), c -> 0, + new DummyListChromosomeDecoder<>("0")); + IntegralValuedMutation mutation = new IntegralValuedMutation<>(min - 10, max); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); + } + + @Test + public void testCheckValidity1() { + int min = 0; + int max = 10; + IntegralValuedChromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + IntegralValuedMutation mutation = new IntegralValuedMutation<>(min - 10, max); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); + } + + @Test + public void testIntegralValuedMutation() { + Assertions.assertThrows(GeneticException.class, () -> { + new IntegralValuedMutation<>(10, 5); + }); + } + + @Test + public void testGetMinMax() { + int min = 0; + int max = 10; + IntegralValuedMutation mutation = new IntegralValuedMutation<>(min, max); + Assertions.assertEquals(min, mutation.getMin()); + Assertions.assertEquals(max, mutation.getMax()); + } + + @Test + public void testMutateGene() { + int min = 0; + int max = 10; + IntegralValuedMutation mutation = new IntegralValuedMutation<>(min, max); + for (int i = 0; i < 100; i++) { + int origValue = min + RandomProviderManager.getRandomProvider().nextInt(max - min); + int mutatedValued = mutation.mutateGene(origValue); + Assertions.assertTrue(min <= mutatedValued && mutatedValued < max); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/RealValuedMutationTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/RealValuedMutationTest.java new file mode 100644 index 0000000000..015801b377 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/RealValuedMutationTest.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation; + +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.utils.ChromosomeRepresentationUtils; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RealValuedMutationTest { + + @Test + public void testCheckValidity() { + int min = 0; + int max = 10; + Chromosome chromosome = new IntegralValuedChromosome<>( + ChromosomeRepresentationUtils.randomIntegralRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + RealValuedMutation mutation = new RealValuedMutation<>(min - 10, max); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); + } + + @Test + public void testCheckValidity1() { + double min = 0; + double max = 10; + RealValuedChromosome chromosome = new RealValuedChromosome<>( + ChromosomeRepresentationUtils.randomDoubleRepresentation(10, min, max), c -> 0, + new DummyListChromosomeDecoder<>("0"), min, max); + RealValuedMutation mutation = new RealValuedMutation<>(min - 10, max); + Assertions.assertThrows(GeneticException.class, () -> { + mutation.checkValidity(chromosome); + }); + } + + @Test + public void testIntegralValuedMutation() { + Assertions.assertThrows(GeneticException.class, () -> { + new RealValuedMutation<>(10, 5); + }); + } + + @Test + public void testGetMinMax() { + double min = 0; + double max = 10; + RealValuedMutation mutation = new RealValuedMutation<>(min, max); + Assertions.assertEquals(min, mutation.getMin(), .001); + Assertions.assertEquals(max, mutation.getMax(), .001); + } + + @Test + public void testMutateGene() { + double min = 0; + double max = 10; + RealValuedMutation mutation = new RealValuedMutation<>(min, max); + for (int i = 0; i < 100; i++) { + double origValue = min + (max - min) * RandomProviderManager.getRandomProvider().nextDouble(); + double mutatedValue = mutation.mutateGene(origValue); + Assertions.assertTrue(min <= mutatedValue && mutatedValue < max); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java new file mode 100644 index 0000000000..604e08db90 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/AdaptiveLinearMutationRateGeneratorTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.stats.PopulationStatisticalSummary; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class AdaptiveLinearMutationRateGeneratorTest { + + @Test + public void testGenerate() { + final double minMutationRate = .01; + final double maxMutationRate = .1; + + IntegralValuedChromosome chromosome1 = IntegralValuedChromosome.randomChromosome(10, c -> 1, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome2 = IntegralValuedChromosome.randomChromosome(10, c -> 2, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome3 = IntegralValuedChromosome.randomChromosome(10, c -> 3, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome4 = IntegralValuedChromosome.randomChromosome(10, c -> 4, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + IntegralValuedChromosome chromosome5 = IntegralValuedChromosome.randomChromosome(10, c -> 5, + new DummyListChromosomeDecoder<>("Fixed"), 0, 10); + + Population population = new ListPopulation<>(5); + population.addChromosome(chromosome1); + population.addChromosome(chromosome2); + population.addChromosome(chromosome3); + population.addChromosome(chromosome4); + population.addChromosome(chromosome5); + PopulationStatisticalSummary stats = new PopulationStatisticalSummaryImpl<>(population); + + MutationRateGenerator mutationRateGenerator = new AdaptiveLinearMutationRateGenerator<>(minMutationRate, + maxMutationRate); + + Assertions.assertEquals(maxMutationRate, mutationRateGenerator.generate(chromosome1, stats, 1), .00000001); + Assertions.assertEquals(minMutationRate, mutationRateGenerator.generate(chromosome5, stats, 1), .00000001); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java new file mode 100644 index 0000000000..0c7793fdd9 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/mutation/rategenerator/ConstantMutationRateGeneratorTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.mutation.rategenerator; + +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.stats.PopulationStatisticalSummaryImpl; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.apache.commons.math4.ga.utils.RandomProviderManager; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ConstantMutationRateGeneratorTest { + + @Test + public void testGenerate() { + for (int i = 0; i < 100; i++) { + double mutationRate = RandomProviderManager.getRandomProvider().nextDouble(); + MutationRateGenerator mutationRateGenerator = new ConstantMutationRateGenerator<>(mutationRate); + IntegralValuedChromosome chromosome = IntegralValuedChromosome.randomChromosome(10, c -> 0, + new DummyListChromosomeDecoder<>("Fixed"), 0, 2); + Population population = new ListPopulation<>(1); + population.addChromosome(chromosome); + Assertions.assertEquals(mutationRate, + mutationRateGenerator.generate(chromosome, new PopulationStatisticalSummaryImpl<>(population), i), + .0000001); + } + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.java new file mode 100644 index 0000000000..3f4cbf17d3 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/population/ListPopulationTest.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.population; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.IntegralValuedChromosome; +import org.apache.commons.math4.ga.dummy.DummyListChromosomeDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ListPopulationTest { + + @Test + public void testGetFittestChromosome() { + AbstractChromosome c1 = new AbstractChromosome(chromosome -> 0, chromosome -> "0") { + }; + AbstractChromosome c2 = new AbstractChromosome(chromosome -> 10, chromosome -> "10") { + }; + AbstractChromosome c3 = new AbstractChromosome(chromosome -> 15, chromosome -> "15") { + }; + + ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(c1); + chromosomes.add(c2); + chromosomes.add(c3); + + ListPopulation population = new ListPopulation(chromosomes, 10); + + Assertions.assertEquals(c3, population.getFittestChromosome()); + Assertions.assertNotNull(population.toString()); + } + + @Test + public void testChromosomes() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(10); + + population.addChromosomes(chromosomes); + + Assertions.assertEquals(chromosomes, population.getChromosomes()); + + population.setPopulationLimit(50); + Assertions.assertEquals(50, population.getPopulationLimit()); + } + + @Test + public void testSetPopulationLimit() { + final ListPopulation population = new ListPopulation<>(10); + + Assertions.assertThrows(GeneticException.class, () -> { + population.setPopulationLimit(-50); + }); + + } + + @Test + public void testConstructorPopulationLimitNotPositive() { + Assertions.assertThrows(GeneticException.class, () -> { + new ListPopulation(-10); + }); + } + + @Test + public void testChromosomeListConstructorPopulationLimitNotPositive() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + Assertions.assertThrows(GeneticException.class, () -> { + new ListPopulation(chromosomes, -10); + }); + } + + @Test + public void testConstructorListOfChromosomesBiggerThanPopulationSize() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + Assertions.assertThrows(GeneticException.class, () -> { + new ListPopulation(chromosomes, 1); + }); + + } + + @Test + public void testAddTooManyChromosomes() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(2); + + Assertions.assertThrows(GeneticException.class, () -> { + population.addChromosomes(chromosomes); + }); + + } + + @Test + public void testAddTooManyChromosomesSingleCall() { + + final ListPopulation population = new ListPopulation<>(2); + + Assertions.assertThrows(GeneticException.class, () -> { + for (int i = 0; i <= population.getPopulationLimit(); i++) { + population.addChromosome(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + } + }); + } + + @Test + public void testIterator() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(10); + + population.addChromosomes(chromosomes); + + final Iterator> iter = population.iterator(); + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + while (iter.hasNext()) { + iter.next(); + iter.remove(); + } + }); + } + + @Test + public void testSetPopulationLimitTooSmall() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(chromosomes, 3); + + Assertions.assertThrows(GeneticException.class, () -> { + population.setPopulationLimit(2); + }); + + } + + @Test + public void testNextGeneration() { + final ArrayList> chromosomes = new ArrayList<>(); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + chromosomes.add(IntegralValuedChromosome.randomChromosome(3, chromosome -> 0, + new DummyListChromosomeDecoder<>("0"), 0, 2)); + + final ListPopulation population = new ListPopulation<>(chromosomes, 3); + + Assertions.assertEquals(1, population.nextGeneration(.4).getPopulationSize()); + Assertions.assertEquals(0, population.nextGeneration(.1).getPopulationSize()); + } +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/selection/TournamentSelectionTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/selection/TournamentSelectionTest.java new file mode 100644 index 0000000000..c855eb85c2 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/selection/TournamentSelectionTest.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.selection; + +import java.util.Iterator; + +import org.apache.commons.math4.ga.chromosome.AbstractChromosome; +import org.apache.commons.math4.ga.chromosome.Chromosome; +import org.apache.commons.math4.ga.chromosome.ChromosomePair; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.apache.commons.math4.ga.population.ListPopulation; +import org.apache.commons.math4.ga.population.Population; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class TournamentSelectionTest { + + private static int counter; + + @Test + public void testSelect() { + TournamentSelection ts = new TournamentSelection<>(2); + + Assertions.assertEquals(2, ts.getArity()); + + ListPopulation pop = new ListPopulation<>(100); + + for (int i = 0; i < pop.getPopulationLimit(); i++) { + pop.addChromosome(new DummyChromosome()); + } + // how to write a test for stochastic method? + for (int i = 0; i < 20; i++) { + ChromosomePair pair = ts.select(pop); + // the worst chromosome should NEVER be selected + Assertions.assertTrue(pair.getFirst().evaluate() > 0); + Assertions.assertTrue(pair.getSecond().evaluate() > 0); + } + } + + private static class DummyChromosome extends AbstractChromosome { + DummyChromosome() { + super(c -> counter++, c -> "0"); + } + + } + + @Test + public void testNonListPopulation() { + + Population population = new Population() { + + @Override + public Iterator> iterator() { + return null; + } + + @Override + public int getPopulationSize() { + return 0; + } + + @Override + public int getPopulationLimit() { + return 0; + } + + @Override + public Population nextGeneration(double elitismRate) { + return null; + } + + @Override + public void addChromosome(Chromosome chromosome) { + } + + @Override + public Chromosome getFittestChromosome() { + return null; + } + }; + Assertions.assertThrows(GeneticException.class, () -> { + new TournamentSelection(5).select(population); + }); + + } + + @Test + public void testInvalidArity() { + Population population = new ListPopulation<>(2); + Assertions.assertThrows(GeneticException.class, () -> { + new TournamentSelection(2).select(population); + }); + } + +} diff --git a/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtilsTest.java b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtilsTest.java new file mode 100644 index 0000000000..0b595c58e6 --- /dev/null +++ b/commons-math-ga/src/test/java/org/apache/commons/math4/ga/utils/ChromosomeRepresentationUtilsTest.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.math4.ga.utils; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import org.apache.commons.math4.ga.chromosome.RealValuedChromosome; +import org.apache.commons.math4.ga.decoder.RandomKeyDecoder; +import org.apache.commons.math4.ga.internal.exception.GeneticException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ChromosomeRepresentationUtilsTest { + + @Test + public void testRandomPermutation() { + // never generate an invalid one + for (int i = 0; i < 10; i++) { + List representation = ChromosomeRepresentationUtils.randomPermutation(10); + Assertions.assertNotNull(representation); + } + } + + @Test + public void testIdentityPermutation() { + List identityPermutation = ChromosomeRepresentationUtils.identityPermutation(5); + List sequence = Arrays.asList(new String[] {"a", "b", "c", "d", "e"}); + RandomKeyDecoder decoder = new RandomKeyDecoder<>(sequence); + RealValuedChromosome> chromosome = new RealValuedChromosome<>(identityPermutation, c -> 0, + decoder); + List decoded = decoder.decode(chromosome); + + Assertions.assertEquals("a", decoded.get(0)); + Assertions.assertEquals("b", decoded.get(1)); + Assertions.assertEquals("c", decoded.get(2)); + Assertions.assertEquals("d", decoded.get(3)); + Assertions.assertEquals("e", decoded.get(4)); + } + + @Test + public void testComparatorPermutation() { + List sequence = Arrays.asList(new String[] {"x", "b", "c", "z", "b"}); + + List permutation = ChromosomeRepresentationUtils.comparatorPermutation(sequence, + new Comparator() { + @Override + public int compare(String o1, String o2) { + return o1.compareTo(o2); + } + }); + Double[] permArr = new Double[sequence.size()]; + permArr = permutation.toArray(permArr); + + Assertions.assertArrayEquals(new Double[] {0.6, 0.0, 0.4, 0.8, 0.2}, permArr); + + RandomKeyDecoder decoder = new RandomKeyDecoder<>(sequence); + List decodedData = decoder.decode(new RealValuedChromosome<>(permutation, c -> 0, decoder)); + + Assertions.assertEquals("b", decodedData.get(0)); + Assertions.assertEquals("b", decodedData.get(1)); + Assertions.assertEquals("c", decodedData.get(2)); + Assertions.assertEquals("x", decodedData.get(3)); + Assertions.assertEquals("z", decodedData.get(4)); + + permutation = ChromosomeRepresentationUtils.comparatorPermutation(sequence, new Comparator() { + @Override + public int compare(String o1, String o2) { + return o2.compareTo(o1); + } + }); + permArr = new Double[sequence.size()]; + permArr = permutation.toArray(permArr); + + Assertions.assertArrayEquals(new Double[] {0.2, 0.6, 0.4, 0.0, 0.8}, permArr); + + decodedData = decoder.decode(new RealValuedChromosome<>(permutation, c -> 0, decoder)); + + Assertions.assertEquals("z", decodedData.get(0)); + Assertions.assertEquals("x", decodedData.get(1)); + Assertions.assertEquals("c", decodedData.get(2)); + Assertions.assertEquals("b", decodedData.get(3)); + Assertions.assertEquals("b", decodedData.get(4)); + } + + @Test + public void testInducedPermutation() { + List origData = Arrays.asList(new String[] {"a", "b", "c", "d", "d"}); + List permutedData = Arrays.asList(new String[] {"d", "b", "c", "a", "d"}); + + RandomKeyDecoder decoder = new RandomKeyDecoder<>(origData); + RealValuedChromosome> chromosome = new RealValuedChromosome<>( + ChromosomeRepresentationUtils.inducedPermutation(origData, permutedData), c -> 0, decoder); + List decoded = decoder.decode(chromosome); + + Assertions.assertEquals("d", decoded.get(0)); + Assertions.assertEquals("b", decoded.get(1)); + Assertions.assertEquals("c", decoded.get(2)); + Assertions.assertEquals("a", decoded.get(3)); + Assertions.assertEquals("d", decoded.get(4)); + + try { + ChromosomeRepresentationUtils.inducedPermutation(Arrays.asList(new String[] {"a", "b", "c", "d", "d"}), + Arrays.asList(new String[] {"a", "b", "c", "d"})); + Assertions.fail("Uncaught exception"); + } catch (GeneticException e) { + // no-op + } + try { + ChromosomeRepresentationUtils.inducedPermutation(Arrays.asList(new String[] {"a", "b", "c", "d", "d"}), + Arrays.asList(new String[] {"a", "b", "c", "d", "f"})); + Assertions.fail("Uncaught exception"); + } catch (GeneticException e) { + // no-op + } + } + + @Test + public void testEqualRepr() { + RandomKeyDecoder decoder = new RandomKeyDecoder<>(Arrays.asList(new String[] {"a", "b", "c"})); + RealValuedChromosome> chromosome = new RealValuedChromosome<>(new Double[] {0.2, 0.2, 0.5}, c -> 0, + decoder); + + List decodedData = decoder.decode(chromosome); + Assertions.assertEquals("a", decodedData.get(0)); + Assertions.assertEquals("b", decodedData.get(1)); + Assertions.assertEquals("c", decodedData.get(2)); + } + + @Test + public void testIntegralRepresentation() { + int min = 0; + int max = 10; + List values = ChromosomeRepresentationUtils.randomIntegralRepresentation(100, min, max); + for (Integer value : values) { + Assertions.assertTrue(min <= value && value < max); + } + } + + @Test + public void testNormalizedDoubleRepresentation() { + List values = ChromosomeRepresentationUtils.randomNormalizedDoubleRepresentation(100); + for (Double value : values) { + Assertions.assertTrue(0 <= value && value < 1); + } + } + +} diff --git a/pom.xml b/pom.xml index d763cb54f0..a3ca676eb3 100644 --- a/pom.xml +++ b/pom.xml @@ -116,6 +116,7 @@ commons-math-core commons-math-neuralnet commons-math-transform + commons-math-ga commons-math-legacy-exception @@ -126,7 +127,6 @@ commons-math-examples - diff --git a/src/main/resources/spotbugs/spotbugs-exclude-filter.xml b/src/main/resources/spotbugs/spotbugs-exclude-filter.xml index 3de6902894..8183da2988 100644 --- a/src/main/resources/spotbugs/spotbugs-exclude-filter.xml +++ b/src/main/resources/spotbugs/spotbugs-exclude-filter.xml @@ -57,4 +57,10 @@ + + + + + +