/******************************************************************************* * Copyright 2014 Analog Devices, Inc. * * 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. ********************************************************************************/ package com.analog.lyric.dimple.test.solvers.lp; import static java.util.Objects.*; import static org.junit.Assert.*; import static org.junit.matchers.JUnitMatchers.*; import org.junit.Ignore; import org.junit.Test; import com.analog.lyric.dimple.exceptions.DimpleException; import com.analog.lyric.dimple.factorfunctions.Cos; import com.analog.lyric.dimple.factorfunctions.core.FactorFunction; import com.analog.lyric.dimple.model.core.FactorGraph; import com.analog.lyric.dimple.model.domains.DiscreteDomain; import com.analog.lyric.dimple.model.domains.RealDomain; import com.analog.lyric.dimple.model.values.Value; import com.analog.lyric.dimple.model.variables.Discrete; import com.analog.lyric.dimple.model.variables.Real; import com.analog.lyric.dimple.solvers.core.SolverBase; import com.analog.lyric.dimple.solvers.lp.LPSolver; import com.analog.lyric.dimple.solvers.lp.LPSolverGraph; import com.analog.lyric.dimple.solvers.lp.Solver; import com.analog.lyric.dimple.solvers.sumproduct.SumProductSolver; import com.analog.lyric.dimple.test.DimpleTestBase; public class TestLPSolver extends DimpleTestBase { /** * Deterministic factor that asserts that only one argument may be true. */ public static class OnlyOneTrue extends FactorFunction { static public OnlyOneTrue INSTANCE = new OnlyOneTrue(); @Override public final double evalEnergy(Value[] args) { int nTrue = 0; for (Value arg : args) { if (arg.getBoolean()) { if (++nTrue > 1) { return Double.POSITIVE_INFINITY; } } } return 0; } } /** * Deterministic factor that asserts there are no duplicate arguments. */ public static class NoDups extends FactorFunction { static public NoDups INSTANCE = new NoDups(); @Override public final double evalEnergy(Value[] args) { for (int i = args.length; --i >= 0;) { Object arg = requireNonNull(args[i].getObject()); for (int j = i; --j >= 0;) { if (arg.equals(args[j].getObject())) { return Double.POSITIVE_INFINITY; } } } return 0; } } @SuppressWarnings("deprecation") @Test public void test() { // Test solver equality SolverBase<?> solver1 = new LPSolver(); SolverBase<?> solver2 = new Solver(); SolverBase<?> solver3 = new SumProductSolver(); assertEquals(solver1, solver2); assertNotEquals(solver2, solver3); assertEquals(solver1.hashCode(), solver2.hashCode()); assertNotEquals(solver2.hashCode(), solver3.hashCode()); RealDomain realDomain = new RealDomain(0.0, 1.0); FactorGraph fg0 = new FactorGraph(); fg0.setSolverFactory(new Solver()); assertTrue(fg0.getSolver() instanceof LPSolverGraph); Real real1 = new Real(realDomain); real1.setName("real1"); try { fg0.addVariables(real1); fail("expected exception"); } catch (DimpleException ex) { assertThat(ex.getMessage(), containsString("Variable 'real1' is not discrete")); } try { fg0.addFactor(new Cos(), real1); fail("expected exception"); } catch (DimpleException ex) { assertThat(ex.getMessage(), containsString("is not a DiscreteFactor")); } DiscreteDomain booleanDomain = DiscreteDomain.bool(); FactorGraph fg1 = new FactorGraph(); Discrete x = new Discrete(booleanDomain); x.setName("x"); Discrete y = new Discrete(booleanDomain); y.setName("y"); Discrete z = new Discrete(booleanDomain); z.setName("z"); fg1.addFactor(OnlyOneTrue.INSTANCE, x, y); fg1.addFactor(OnlyOneTrue.INSTANCE, y, z); fg1.addFactor(OnlyOneTrue.INSTANCE, x, z); x.setInput(.3, .7); y.setInput(.2, .8); z.setInput(.6, .3); LPSolverTestCase case1 = new LPSolverTestCase(fg1); case1.expectedConstraints = new String[] { "p(x=false) + p(x=true) = 1", "p(y=false) + p(y=true) = 1", "p(z=false) + p(z=true) = 1", "-p(x=false) + p(x=false,y=false) + p(x=false,y=true) = 0", "-p(x=true) + p(x=true,y=false) = 0", "-p(y=false) + p(x=false,y=false) + p(x=true,y=false) = 0", "-p(y=true) + p(x=false,y=true) = 0", "-p(y=false) + p(y=false,z=false) + p(y=false,z=true) = 0", "-p(y=true) + p(y=true,z=false) = 0", "-p(z=false) + p(y=false,z=false) + p(y=true,z=false) = 0", "-p(z=true) + p(y=false,z=true) = 0", "-p(x=false) + p(x=false,z=false) + p(x=false,z=true) = 0", "-p(x=true) + p(x=true,z=false) = 0", "-p(z=false) + p(x=false,z=false) + p(x=true,z=false) = 0", "-p(z=true) + p(x=false,z=true) = 0", }; case1.testLPState(); // Fix Y to false, but don't use fixed value API y.setInput(1, 0); LPSolverTestCase case2 = new LPSolverTestCase(fg1); case2.expectedConstraints = new String[] { "p(x=false) + p(x=true) = 1", "p(z=false) + p(z=true) = 1", "-p(x=false) + p(x=false,y=false) = 0", "-p(x=true) + p(x=true,y=false) = 0", "-p(z=false) + p(y=false,z=false) = 0", "-p(z=true) + p(y=false,z=true) = 0", "-p(x=false) + p(x=false,z=false) + p(x=false,z=true) = 0", "-p(x=true) + p(x=true,z=false) = 0", "-p(z=false) + p(x=false,z=false) + p(x=true,z=false) = 0", "-p(z=true) + p(x=false,z=true) = 0", }; case2.testLPState(); // Fix Z to true z.setFixedValue(true); LPSolverTestCase case3 = new LPSolverTestCase(fg1); case3.expectedConstraints = new String[] { "p(x=false) + p(x=true) = 1", "-p(x=false) + p(x=false,y=false) = 0", "-p(x=true) + p(x=true,y=false) = 0", "-p(x=false) + p(x=false,z=true) = 0", }; case3.testLPState(); // Now fix X x.setFixedValue(false); LPSolverTestCase case4 = new LPSolverTestCase(fg1); case4.expectedConstraints = new String[0]; case4.testLPState(); DiscreteDomain stoogeDomain = DiscreteDomain.create("Moe", "Larry", "Curly"); FactorGraph fg2 = new FactorGraph(); Discrete a = new Discrete(stoogeDomain); a.setName("a"); Discrete b = new Discrete(stoogeDomain); b.setName("b"); Discrete c = new Discrete(stoogeDomain); c.setName("c"); fg2.addFactor(NoDups.INSTANCE, a, b); fg2.addFactor(NoDups.INSTANCE, b, c); a.setInput(0.0, .5, .5); // Not Moe b.setInput(.3, .5, .2); c.setInput(.7, .1, .2); LPSolverTestCase stoogeCase1 = new LPSolverTestCase(fg2); stoogeCase1.expectedConstraints = new String[] { "p(a=Larry) + p(a=Curly) = 1", "p(b=Moe) + p(b=Larry) + p(b=Curly) = 1", "p(c=Moe) + p(c=Larry) + p(c=Curly) = 1", "-p(a=Larry) + p(a=Larry,b=Moe) + p(a=Larry,b=Curly) = 0", "-p(a=Curly) + p(a=Curly,b=Moe) + p(a=Curly,b=Larry) = 0", "-p(b=Moe) + p(a=Larry,b=Moe) + p(a=Curly,b=Moe) = 0", "-p(b=Larry) + p(a=Curly,b=Larry) = 0", "-p(b=Curly) + p(a=Larry,b=Curly) = 0", "-p(b=Moe) + p(b=Moe,c=Larry) + p(b=Moe,c=Curly) = 0", "-p(b=Larry) + p(b=Larry,c=Moe) + p(b=Larry,c=Curly) = 0", "-p(b=Curly) + p(b=Curly,c=Moe) + p(b=Curly,c=Larry) = 0", "-p(c=Moe) + p(b=Larry,c=Moe) + p(b=Curly,c=Moe) = 0", "-p(c=Larry) + p(b=Moe,c=Larry) + p(b=Curly,c=Larry) = 0", "-p(c=Curly) + p(b=Moe,c=Curly) + p(b=Larry,c=Curly) = 0", }; stoogeCase1.testLPState(); // Regression case for bug 51 a.setInput(.5, .5, 0.0); // Not Curly LPSolverTestCase bug51 = new LPSolverTestCase(fg2); bug51.testLPState(); } @Test @Ignore public void testGLPK() { DiscreteDomain booleanDomain = DiscreteDomain.create(false,true); FactorGraph fg1 = new FactorGraph(); Discrete x = new Discrete(booleanDomain); x.setName("x"); Discrete y = new Discrete(booleanDomain); y.setName("y"); Discrete z = new Discrete(booleanDomain); z.setName("z"); fg1.addFactor(OnlyOneTrue.INSTANCE, x, y); fg1.addFactor(OnlyOneTrue.INSTANCE, y, z); fg1.addFactor(OnlyOneTrue.INSTANCE, x, z); x.setPrior(.3, .7); y.setPrior(.2, .8); z.setPrior(.6, .3); LPSolverGraph solver = new Solver().createFactorGraph(fg1); solver.setLPSolverName("GLPK"); solver.solve(); } }