From f4c5255cd62b94f491f8b22f168bd7556a538eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Vajngerl?= Date: Wed, 3 Jun 2026 11:19:32 +0900 Subject: [PATCH] SwarmSolver: add a constrained linear program test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing tests are single variable or bound only. Add a small two variable linear program with real inequality constraints on formula cells: maximize 2x + 4y subject to x + y <= 4 and x + 3y <= 6, with both variables in [0, 10]. The two constraints meet at x = 3, y = 1, where the objective reaches its maximum of 10, and the solver finds that point. The model is written in a small FODF spreadsheet so the formulas are easy to read in the diff. Signed-off-by: Tomaž Vajngerl Change-Id: Ie9bd269a92da0db07927a6b7500e2580801369b6 Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/4056 Tested-by: Tomaž Vajngerl Reviewed-by: Miklos Vajna (cherry picked from commit 3befd66155132064c8543c0688eadd8ba2010192) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208441 Tested-by: Xisco Fauli Reviewed-by: Xisco Fauli --- sc/qa/unit/SwarmSolverTest.cxx | 53 +++++++++++++++++++ sc/qa/unit/data/solver/ConstrainedLinear.fods | 22 ++++++++ 2 files changed, 75 insertions(+) create mode 100644 sc/qa/unit/data/solver/ConstrainedLinear.fods diff --git a/sc/qa/unit/SwarmSolverTest.cxx b/sc/qa/unit/SwarmSolverTest.cxx index 0df2f5806a2d..993b539f19e4 100644 --- a/sc/qa/unit/SwarmSolverTest.cxx +++ b/sc/qa/unit/SwarmSolverTest.cxx @@ -80,6 +80,7 @@ class SwarmSolverTest : public UnoApiTest void testUnboundedIntegerVariable(); void testRepeatedSolveResetsState(); void testControllersUnlockedAfterError(); + void testConstrainedLinearProgram(); public: SwarmSolverTest() @@ -104,6 +105,7 @@ public: CPPUNIT_TEST(testUnboundedIntegerVariable); CPPUNIT_TEST(testRepeatedSolveResetsState); CPPUNIT_TEST(testControllersUnlockedAfterError); + CPPUNIT_TEST(testConstrainedLinearProgram); CPPUNIT_TEST_SUITE_END(); }; @@ -841,6 +843,57 @@ void SwarmSolverTest::testControllersUnlockedAfterError() !xModel->hasControllersLocked()); } +void SwarmSolverTest::testConstrainedLinearProgram() +{ + // A small linear program with two binding inequality constraints on formula + // cells. Maximize 2x + 4y subject to x + y <= 4 and x + 3y <= 6, with both + // variables in [0, 10]. The two constraints meet at x = 3, y = 1, where the + // objective reaches its maximum of 10. + + loadFromFile(u"ConstrainedLinear.fods"); + + uno::Reference xDocument(mxComponent, uno::UNO_QUERY_THROW); + + uno::Reference xSolver; + xSolver.set(m_xContext->getServiceManager()->createInstanceWithContext( + u"com.sun.star.comp.Calc.SwarmSolver"_ustr, m_xContext), + uno::UNO_QUERY_THROW); + + // objective 2x + 4y is in B3 + table::CellAddress aObjective(0, 1, 2); + // x is A1, y is A2 + uno::Sequence aVariables{ { 0, 0, 0 }, { 0, 0, 1 } }; + + uno::Sequence aConstraints{ + // box bounds on the two variables + { table::CellAddress(0, 0, 0), sheet::SolverConstraintOperator_GREATER_EQUAL, + uno::Any(0.0) }, + { table::CellAddress(0, 0, 0), sheet::SolverConstraintOperator_LESS_EQUAL, uno::Any(10.0) }, + { table::CellAddress(0, 0, 1), sheet::SolverConstraintOperator_GREATER_EQUAL, + uno::Any(0.0) }, + { table::CellAddress(0, 0, 1), sheet::SolverConstraintOperator_LESS_EQUAL, uno::Any(10.0) }, + // x + y <= 4 (cell B1) + { table::CellAddress(0, 1, 0), sheet::SolverConstraintOperator_LESS_EQUAL, uno::Any(4.0) }, + // x + 3y <= 6 (cell B2) + { table::CellAddress(0, 1, 1), sheet::SolverConstraintOperator_LESS_EQUAL, uno::Any(6.0) } + }; + + xSolver->setDocument(xDocument); + xSolver->setObjective(aObjective); + xSolver->setVariables(aVariables); + xSolver->setConstraints(aConstraints); + xSolver->setMaximize(true); + + xSolver->solve(); + + CPPUNIT_ASSERT(xSolver->getSuccess()); + + uno::Sequence aSolution = xSolver->getSolution(); + CPPUNIT_ASSERT_EQUAL(aVariables.getLength(), aSolution.getLength()); + CPPUNIT_ASSERT_DOUBLES_EQUAL(3.0, aSolution[0], 1E-3); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, aSolution[1], 1E-3); +} + CPPUNIT_TEST_SUITE_REGISTRATION(SwarmSolverTest); } diff --git a/sc/qa/unit/data/solver/ConstrainedLinear.fods b/sc/qa/unit/data/solver/ConstrainedLinear.fods new file mode 100644 index 000000000000..5954b9a23f65 --- /dev/null +++ b/sc/qa/unit/data/solver/ConstrainedLinear.fods @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + +