SwarmSolver: add a constrained linear program test

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 <tomaz.vajngerl@collabora.co.uk>
Change-Id: Ie9bd269a92da0db07927a6b7500e2580801369b6
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/4056
Tested-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
(cherry picked from commit 3befd66155132064c8543c0688eadd8ba2010192)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208441
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Tomaž Vajngerl
2026-07-22 21:52:28 +02:00
committed by Xisco Fauli
parent 0209dc2476
commit f4c5255cd6
2 changed files with 75 additions and 0 deletions
+53
View File
@@ -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<sheet::XSpreadsheetDocument> xDocument(mxComponent, uno::UNO_QUERY_THROW);
uno::Reference<sheet::XSolver> 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<table::CellAddress> aVariables{ { 0, 0, 0 }, { 0, 0, 1 } };
uno::Sequence<sheet::SolverConstraint> 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<double> 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);
}
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
<office:body>
<office:spreadsheet>
<table:table table:name="Sheet1">
<table:table-column table:number-columns-repeated="2"/>
<table:table-row>
<table:table-cell office:value-type="float" office:value="0"/>
<table:table-cell table:formula="of:=[.A1]+[.A2]" office:value-type="float" office:value="0"/>
</table:table-row>
<table:table-row>
<table:table-cell office:value-type="float" office:value="0"/>
<table:table-cell table:formula="of:=[.A1]+3*[.A2]" office:value-type="float" office:value="0"/>
</table:table-row>
<table:table-row>
<table:table-cell/>
<table:table-cell table:formula="of:=2*[.A1]+4*[.A2]" office:value-type="float" office:value="0"/>
</table:table-row>
</table:table>
</office:spreadsheet>
</office:body>
</office:document>