I wanted to calculate the efficient frontier with equality and inequality constraints for given covariance matrix and expected returns. While doing so, the solver calculates weights that unfortunately seem to break the constraints. The overall risks and returns seem to be fine tho.
Primitive64Store tmpCOV = Primitive64Store.FACTORY.rows(new double[][]
{{0.04161599999999999,0.029532687342839998,0.027204649602,0.0077310803303999994,-1.1671155792E-4,-5.6919104880000004E-5,0.011540367260999999,-0.0011583596544,0.0016547567508,0.011887951467599998,-0.0021016779250799997,0.01217717900784,0.0354566417292},
{0.029532687342839998,0.034969,0.02882914496033,0.00673688572095,-1.5753286164E-4,-7.799020878E-5,0.008590460804089999,-0.00120903466574,0.0014109339056999999,0.0104683749489,-0.00296095338671,0.0075381371219,0.03405788034132},
{0.027204649602,0.02882914496033,0.028561000000000003,0.0095923333779,-1.4991438384E-4,-8.476063180000002E-5,0.009686536403049999,-0.0011563569438200001,9.994519899E-4,0.0082659675852,-3.1685615143000007E-4,0.00860292214782,0.03129653885267},
{0.0077310803303999994,0.00673688572095,0.0095923333779,0.011024999999999998,-5.7071784E-6,-1.6705374000000003E-5,0.0062936927109,-2.7820294949999996E-4,8.405088299999999E-5,6.61814874E-4,0.0037795989147,0.0055210432431,0.00810029447955},
{-1.1671155792E-4,-1.5753286164E-4,-1.4991438384E-4,-5.7071784E-6,1.6E-5,5.40282216E-6,-3.1099949760000004E-5,1.671549776E-5,-3.051312E-7,-7.517952359999999E-5,3.06077038E-5,1.3556312E-7,-2.345551502E-4},
{-5.6919104880000004E-5,-7.799020878E-5,-8.476063180000002E-5,-1.6705374000000003E-5,5.40282216E-6,4.0E-6,-1.7530565920000003E-5,3.13729976E-6,-4.841874E-6,-4.2240274199999995E-5,5.2011296E-6,3.44228808E-6,-1.3104418078000002E-4},
{0.011540367260999999,0.008590460804089999,0.009686536403049999,0.0062936927109,-3.1099949760000004E-5,-1.7530565920000003E-5,0.011881,4.4589363133999997E-4,9.499062894E-4,0.0036563291469,0.00251147898767,0.004586606807859999,0.0108024647181},
{-0.0011583596544,-0.00120903466574,-0.0011563569438200001,-2.7820294949999996E-4,1.671549776E-5,3.13729976E-6,4.4589363133999997E-4,0.001444,6.672796998E-4,-9.37965438E-5,0.00112122563298,-0.0011028714312,-0.0013360677697000002},
{0.0016547567508,0.0014109339056999999,9.994519899E-4,8.405088299999999E-5,-3.051312E-7,-4.841874E-6,9.499062894E-4,6.672796998E-4,9.0E-4,0.0013451552549999999,2.136913209E-4,4.8565227479999997E-4,0.0022035050261999998},
{0.011887951467599998,0.0104683749489,0.0082659675852,6.61814874E-4,-7.517952359999999E-5,-4.2240274199999995E-5,0.0036563291469,-9.37965438E-5,0.0013451552549999999,0.0081,-0.0018377539344,0.0026756272619999997,0.0138893569515},
{-0.0021016779250799997,-0.00296095338671,-3.1685615143000007E-4,0.0037795989147,3.06077038E-5,5.2011296E-6,0.00251147898767,0.00112122563298,2.136913209E-4,-0.0018377539344,0.004489000000000001,1.718834348E-4,-0.0025610649546900003},
{0.01217717900784,0.0075381371219,0.00860292214782,0.0055210432431,1.3556312E-7,3.44228808E-6,0.004586606807859999,-0.0011028714312,4.8565227479999997E-4,0.0026756272619999997,1.718834348E-4,0.023716,0.010939810005740002},
{0.0354566417292,0.03405788034132,0.03129653885267,0.00810029447955,-2.345551502E-4,-1.3104418078000002E-4,0.0108024647181,-0.0013360677697000002,0.0022035050261999998,0.0138893569515,-0.0025610649546900003,0.010939810005740002,0.051529000000000005}});
Primitive64Store tmpR = Primitive64Store.FACTORY.rows(new double[][] {{0.067},{0.045},{0.049},{0.01},{0.02},{1.0E-4},{0.034},{0.001},{0.006},{0.027},{0.005},{0.02},{0.04}});
Primitive64Store tmpEqualityMatrix = Primitive64Store.FACTORY.rows(new double[][]{{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}});
Primitive64Store tmpEqualityVector = Primitive64Store.FACTORY.rows(new double[][] {{1}});
Primitive64Store tmpInequalityMatrix = Primitive64Store.FACTORY.rows(new double[][] {{1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0},
{-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0},
{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0},
{-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0}});
Primitive64Store tmpInequalityVector = Primitive64Store.FACTORY.rows({{0.1},{0.2},{0.2},{0.1},{0.2},{0.1},{0.1},{1.0},{1.0},{1.0},{1.0},{0.1},{1.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0}{0.0}{0.0},{0.0},{0.0},{1.0},{0.0}});
//calculating the Portfolio with the maximum return, this always seems to work fine
ConvexSolver tmpSolverMinDiversification = new ConvexSolver.Builder(tmpCOV.multiply(0), tmpR)
.equalities(tmpEqualityMatrix, tmpEqualityVector).inequalities(tmpInequalityMatrix, tmpInequalityVector).build();
Optimisation.Result tmpResultMinDiversification = tmpSolverMinDiversification.solve();
//calculating the portfolio with minimal risk, this is where the error starts for me
ConvexSolver tmpSolverMaxDiversification = new ConvexSolver.Builder(tmpCOV, tmpR.multiply(0))
.equalities(tmpEqualityMatrix, tmpEqualityVector).inequalities(tmpInequalityMatrix, tmpInequalityVector).build();
Optimisation.Result tmpResultMaxDiversification = tmpSolverMaxDiversification.solve();
The Results im getting are as follows:
Weights of the portfolio with maximum return
OPTIMAL -0.0455 @ { 0.1, 0.2, 0.2, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.4999999999999999 }
Weights of the portfolio with minimal risk
OPTIMAL 1.9065360317211953E-4 @ { -1.3215574127953212E-12, -3.0462153636587428E-12, 4.302357005267538E-12, 0.03119627351114846, 0.19999999989250858, 0.10000000045688691, -9.548402360338467E-13, 0.1370395162618518, 0.48451020053501254, 2.5326118583080594E-12, 0.03544670257704311, 0.011807307116290258, 6.086745301734428E-13 }
If i add an additional inequality (1 new row on both) constraint and run the calculation again
Primitive64Store tmpInequalityMatrix = Primitive64Store.FACTORY.rows(new double[][] {{1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0},
{-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0,0.0},
{0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,-1.0},
{1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0},
{-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0,-1.0},
{-0.067,-0.045,-0.049,-0.01,-0.02,-1.0E-4,-0.034,-0.001,-0.006,-0.027,-0.005,-0.02,-0.04}});
Primitive64Store tmpInequalityVector = Primitive64Store.FACTORY.rows({{0.1},{0.2},{0.2},{0.1},{0.2},{0.1},{0.1},{1.0},{1.0},{1.0},{1.0},{0.1},{1.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0},{0.0}{0.0}{0.0},{0.0},{0.0},{1.0},{0.0},{ -0.037349326902676616 }});
ConvexSolver tmpSolverMaxDiversification = new ConvexSolver.Builder(tmpCOV, tmpR.multiply(0))
.equalities(tmpEqualityMatrix, tmpEqualityVector).inequalities(tmpInequalityMatrix, tmpInequalityVector).build();
Optimisation.Result tmpResultEfficientPortfolio = tmpSolverMaxDiversification.solve();
i get the following result
OPTIMAL 0.004591387674786172 @ { 0.10000000000095524, 0.0, 0.20000000000667748, -3.148187023285784E-12, 0.19999999988537426, -2.6699549475823382E-11, 0.09999999999830238, 3.2126828658623563E-12, 7.645985136245771E-12, 0.5204239501042672, -0.12042395010749231, 1.4422465593413845E-12, -3.1890111868219504E-12 }
Is there a way to avoid those constraint breaking weights? Should i have used an ExpressionBasedModel instead?