diff --git a/pom.xml b/pom.xml index 4ae3a2f216d7bc146d03cf00b8e70c077821bf81..2e59ad487129fa7b858a9faf302c5827f51138cd 100644 --- a/pom.xml +++ b/pom.xml @@ -7,11 +7,11 @@ <parent> <groupId>org.bioimageanalysis.icy</groupId> <artifactId>pom-icy</artifactId> - <version>3.0.0-a.1</version> + <version>3.0.0-a.3</version> </parent> <artifactId>linear-programming</artifactId> - <version>2.0.0-a.1</version> + <version>2.0.0-a.2</version> <name>Linear Programming</name> <description> @@ -22,11 +22,4 @@ custom problems: java -jar linearProgrammingICY.jar -help for the manual. Sources files are provided in the jar file. </description> - - <repositories> - <repository> - <id>icy</id> - <url>https://nexus-icy.pasteur.cloud/repository/icy/</url> - </repository> - </repositories> </project> \ No newline at end of file diff --git a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/CanonicalSimplexProgram.java b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/CanonicalSimplexProgram.java index 64ddf5ec455e8e2123bff8a5228898339e8eb749..19d3bc0c53fbe0d586014827ad2abc89de22933e 100644 --- a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/CanonicalSimplexProgram.java +++ b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/CanonicalSimplexProgram.java @@ -43,7 +43,7 @@ import java.io.*; * A[i]*x <= b if equalityConstraints[i] * <p> * <p> - * Part of the Linear Programming plugin for ICY http://icy.bioimageanalysis.org + * Part of the Linear Programming plugin for ICY <a href="https://icy.bioimageanalysis.org">https://icy.bioimageanalysis.org</a> * * @author Nicolas Chenouard (nicolas.chenouard.dev@gmail.com) */ @@ -266,7 +266,7 @@ public abstract class CanonicalSimplexProgram { // for (int i = 0; i < numConstraints; i ++) // if (parameters.equalityConstraints[i]) // c[i] = 1;//put weights to slack variables which correspond to equalities - //// tableau0.solverWithLPSolve(c, b0, false); + // tableau0.solverWithLPSolve(c, b0, false); // } if (!success) return false; @@ -530,7 +530,8 @@ public abstract class CanonicalSimplexProgram { /** * @return Manual of the library for stand-alone use through command line */ - public static String getHelp() { + @Contract(pure = true) + public static @NotNull String getHelp() { return """ Run the solver for example problems or for a user-defined system Empty arguments to use the default example. @@ -545,7 +546,7 @@ public abstract class CanonicalSimplexProgram { Example arguments: java -jar linearProgrammingICY.jar -c c.txt -A A.txt -b b.txt -e eq.txt -max -o solution.txt\ Each text file must contain a series of double values separated by ',' in a single line, except for the constraint file which contains one line per constraint. For the equality file '0' stands for 'false' and '1' for true. - + Version 1.0. April 2014. Author: Nicolas Chenouard. nicolas.chenouard.dev@gmail.com. Licence GPL V3.0"""; } @@ -904,7 +905,8 @@ public abstract class CanonicalSimplexProgram { "Optimal score is 6.5", "Solution is [1.0, 1.0, 0.5, 0.0]" ); - A = new double[][]{{2, 1, 0, 0}, + A = new double[][]{ + {2, 1, 0, 0}, {0, 1, 4, 1}, {1, 3, 0, 1} }; @@ -921,7 +923,8 @@ public abstract class CanonicalSimplexProgram { "Optimal score is 1/20.", "Solution is [1/25, 0, 1, 0]." ); - A = new double[][]{{1d / 4, -60, -1d / 25, 9}, + A = new double[][]{ + {1d / 4, -60, -1d / 25, 9}, {1d / 2, -90, -1d / 50, 3}, {0, 0, 1, 0} }; @@ -937,10 +940,10 @@ public abstract class CanonicalSimplexProgram { "An example with equality constraints", "Solution is [0, 4/7, 1 + 5/7, 0, 0]." ); - A = new double[][] - {{5, -4, 13, -2, 1}, - {1, -1, 5, -1, 1}, - }; + A = new double[][]{ + {5, -4, 13, -2, 1}, + {1, -1, 5, -1, 1}, + }; b = new double[]{20, 8}; c = new double[]{1, 6, -7, 1, 5}; equality = new boolean[]{true, true}; @@ -954,11 +957,11 @@ public abstract class CanonicalSimplexProgram { "Optimal score is -3 - 1/16.", "Solution is [3/16, 1 + 1/4, 0, 5/16]." ); - A = new double[][] - {{1, 2, 1, 1}, - {1, -2, 2, 1}, - {3, -1, 0, -1} - }; + A = new double[][]{ + {1, 2, 1, 1}, + {1, -2, 2, 1}, + {3, -1, 0, -1} + }; b = new double[]{3, -2, -1}; c = new double[]{2, -3, 1, 1}; equality = new boolean[]{true, true, true}; diff --git a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/LinearProgrammingICYPlugin.java b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/LinearProgrammingICYPlugin.java index b3df71c62d5b2dff290f2bb7bb89777096af2cf3..0bbae89e38e274244b64ad001c8167622a6f9c44 100644 --- a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/LinearProgrammingICYPlugin.java +++ b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/LinearProgrammingICYPlugin.java @@ -25,7 +25,7 @@ import org.bioimageanalysis.icy.gui.frame.progress.AnnounceFrame; import org.bioimageanalysis.icy.system.logging.IcyLogger; @IcyPluginName("Linear Programming Example") -@IcyPluginIcon(path = "/linear-programming.png") +@IcyPluginIcon(path = "/plugins/nchenouard/linearprogramming/linear-programming.png") public class LinearProgrammingICYPlugin extends PluginActionable { @Override public void run() { diff --git a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexBLAND.java b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexBLAND.java index 79b2b283c7b72b6dda76b6b3374f539e891453de..802671b2cbedc992ba0907f41947a0fe59c586ce 100644 --- a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexBLAND.java +++ b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexBLAND.java @@ -93,24 +93,24 @@ public class SimplexBLAND extends CanonicalSimplexProgram { */ @Override protected int getPivotColumn(final TableauWithSlackVariables tableau, final boolean max) { + int idx = -1; + if (max) { - int idx = -1; for (int i = 0; i < tableau.scoreRow.length; i++) if (tableau.scoreRow[i] < 0 && tableau.originalColOrder[i] >= 0) { idx = i; break; } - return idx; } else { - int idx = -1; for (int i = 0; i < tableau.scoreRow.length; i++) if (tableau.scoreRow[i] > 0 && tableau.originalColOrder[i] >= 0) { idx = i; break; } - return idx; } + + return idx; } /** diff --git a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexLEXICO.java b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexLEXICO.java index 61f78404f3393822989e08502283a7892b7547da..925e7fbc63545c7ce0620d8ce4381922a4c8f68a 100644 --- a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexLEXICO.java +++ b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexLEXICO.java @@ -63,9 +63,10 @@ public class SimplexLEXICO extends CanonicalSimplexProgram { @Override protected int getPivotColumn(final TableauWithSlackVariables tableau, final boolean max) { // max score choice strategy + int idx = -1; + double score = 0; + if (max) { - int idx = -1; - double score = 0; for (int i = 0; i < tableau.scoreRow.length; i++) if (tableau.scoreRow[i] < score) { if (tableau.originalColOrder[i] >= 0 || !parameters.equalityConstraints[i]) { @@ -73,25 +74,20 @@ public class SimplexLEXICO extends CanonicalSimplexProgram { idx = i; } } - if (idx >= 0) - return idx; - else - return -1; } else { - int idx = -1; - double score = 0; for (int i = 0; i < tableau.scoreRow.length; i++) if (tableau.scoreRow[i] > score) if (tableau.originalColOrder[i] >= 0 || !parameters.equalityConstraints[i]) { score = tableau.scoreRow[i]; idx = i; } - if (idx >= 0) - return idx; - else - return -1; } + + if (idx >= 0) + return idx; + else + return -1; } /** diff --git a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexMAX.java b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexMAX.java index e1a13e3eb06e3667892a562bf5f33b05d80f3d99..1f97090ddcbc47922cdd3f62edca3c4de6e49dc6 100644 --- a/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexMAX.java +++ b/src/main/java/plugins/nchenouard/linearprogrammingfullsimplex/SimplexMAX.java @@ -62,32 +62,28 @@ public class SimplexMAX extends CanonicalSimplexProgram { */ @Override protected int getPivotColumn(final TableauWithSlackVariables tableau, final boolean max) { + int idx = -1; + double score = 0; + if (max) { - int idx = -1; - double score = 0; for (int i = 0; i < tableau.scoreRow.length; i++) if (tableau.scoreRow[i] < score && tableau.originalColOrder[i] >= 0) { score = tableau.scoreRow[i]; idx = i; } - if (idx >= 0) - return idx; - else - return -1; } else { - int idx = -1; - double score = 0; for (int i = 0; i < tableau.scoreRow.length; i++) if (tableau.scoreRow[i] > score && tableau.originalColOrder[i] >= 0) { score = tableau.scoreRow[i]; idx = i; } - if (idx >= 0) - return idx; - else - return -1; } + + if (idx >= 0) + return idx; + else + return -1; } /** diff --git a/src/main/resources/README.txt b/src/main/resources/plugins/nchenouard/linearprogramming/README.txt similarity index 100% rename from src/main/resources/README.txt rename to src/main/resources/plugins/nchenouard/linearprogramming/README.txt diff --git a/src/main/resources/linear-programming.png b/src/main/resources/plugins/nchenouard/linearprogramming/linear-programming.png similarity index 100% rename from src/main/resources/linear-programming.png rename to src/main/resources/plugins/nchenouard/linearprogramming/linear-programming.png diff --git a/src/test/java/plugins/nchenouard/linearprogrammingfullsimplex/TestCanonicalSimplexProgram.java b/src/test/java/plugins/nchenouard/linearprogrammingfullsimplex/TestCanonicalSimplexProgram.java new file mode 100644 index 0000000000000000000000000000000000000000..5295bddc1fcec643f1dbd9aadc615318973e6ce0 --- /dev/null +++ b/src/test/java/plugins/nchenouard/linearprogrammingfullsimplex/TestCanonicalSimplexProgram.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2010-2024. Institut Pasteur. + * + * This file is part of Icy. + * Icy is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Icy is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Icy. If not, see <https://www.gnu.org/licenses/>. + */ + +package plugins.nchenouard.linearprogrammingfullsimplex; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class TestCanonicalSimplexProgram { + @Test + void scenarioZero() { + final double[][] A = new double[][]{ + {2, 1, 0, 0}, + {0, 1, 4, 1}, + {1, 3, 0, 1} + }; + final double[] b = new double[]{3, 3, 4}; + final double[] c = new double[]{2, 4, 1, 1}; + final boolean[] equality = new boolean[b.length]; + final boolean maximization = true; + + final CanonicalSimplexProgram program = new SimplexLEXICO(A, b, c, maximization, equality); + + assertTrue(program.solvePrimalSimplex()); + final double score = program.getScore(); + final double[] solution = program.solution; + + assertEquals(6.5d, score); + assertArrayEquals(new double[]{1.d, 1.d, .5d, .0d}, solution); + } + + @Test + void scenarioOne() { + final double[][] A = new double[][]{ + {1d / 4, -60, -1d / 25, 9}, + {1d / 2, -90, -1d / 50, 3}, + {0, 0, 1, 0} + }; + final double[] b = new double[]{0, 0, 1}; + final double[] c = new double[]{3d / 4, -150, 1d / 50, -6}; + final boolean[] equality = new boolean[b.length]; + final boolean maximization = true; + + final CanonicalSimplexProgram program = new SimplexLEXICO(A, b, c, maximization, equality); + + assertTrue(program.solvePrimalSimplex()); + final double score = program.getScore(); + final double[] solution = program.solution; + + assertEquals(1.d / 20.d, score); + assertArrayEquals(new double[]{1.d / 25.d, .0d, 1.d, .0d}, solution); + } + + @Test + void scenarioTwo() { + final double[][] A = new double[][]{ + {5, -4, 13, -2, 1}, + {1, -1, 5, -1, 1}, + }; + final double[] b = new double[]{20, 8}; + final double[] c = new double[]{1, 6, -7, 1, 5}; + final boolean[] equality = new boolean[]{true, true}; + final boolean maximization = false; + + final CanonicalSimplexProgram program = new SimplexLEXICO(A, b, c, maximization, equality); + + assertTrue(program.solvePrimalSimplex()); + final double[] solution = program.solution; + + assertArrayEquals(new double[]{.0d, 4.d / 7.d, 1.d + (5.d / 7.d), .0d, .0d}, solution, .0001d); + } + + @Test + void scenarioThree() { + final double[][] A = new double[][]{ + {1, 2, 1, 1}, + {1, -2, 2, 1}, + {3, -1, 0, -1} + }; + final double[] b = new double[]{3, -2, -1}; + final double[] c = new double[]{2, -3, 1, 1}; + final boolean[] equality = new boolean[]{true, true, true}; + final boolean maximization = true; + + final CanonicalSimplexProgram program = new SimplexLEXICO(A, b, c, maximization, equality); + + assertTrue(program.solvePrimalSimplex()); + final double score = program.getScore(); + final double[] solution = program.solution; + + assertEquals(-3.d - (1.d / 16.d), score, .0001d); + assertArrayEquals(new double[]{3.d / 16.d, 1.d + 1.d / 4.d, .0d, 5.d / 16.d}, solution, .0001d); + } +}