Skip to content

solveLP() infeasible results fix#19530

Closed
rayonnant14 wants to merge 4 commits intoopencv:3.4from
rayonnant14:core_solvelp_bug_fix
Closed

solveLP() infeasible results fix#19530
rayonnant14 wants to merge 4 commits intoopencv:3.4from
rayonnant14:core_solvelp_bug_fix

Conversation

@rayonnant14
Copy link
Copy Markdown
Contributor

fix #12343

Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on a code under GPL or other license that is incompatible with OpenCV
  • The PR is proposed to proper branch
  • There is reference to original bug report and related work
  • There is accuracy test, performance test and test data in opencv_extra repository, if applicable
    Patch to opencv_extra has the same branch name.
  • The feature is well documented and sample code can be built with the project CMake

@alalek
Copy link
Copy Markdown
Member

alalek commented Feb 17, 2021

Regression test case:

  • should fail without the patch
  • should pass with enabled patch.

Provided test case doesn't fail without the patch.

@rayonnant14
Copy link
Copy Markdown
Contributor Author

@alalek thanks for comment.
I made a change in the body of the test, it should be correct now

@rayonnant14 rayonnant14 requested a review from alalek February 18, 2021 15:58
Copy link
Copy Markdown
Member

@alalek alalek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for update!

bool all_nonzero=true;
for(pos_ptr=c.begin();pos_ptr!=c.end();pos_ptr++,pos_ctr++){
if(*pos_ptr==0){
if ((*pos_ptr >= -1e-12) && (*pos_ptr <= 1e-12))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. avoid magic constants
  2. use fabs() to compare near 0
const double SOLVELP_EPS = 1e-12;
...
if (fabs(*pos_ptr) < SOLVELP_EPS)

if(*pos_ptr==0){
if ((*pos_ptr >= -1e-12) && (*pos_ptr <= 1e-12))
*pos_ptr = 0.0;
if(*pos_ptr==0.0){
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second check can be eliminated:

  • if the first condition is true, then the second condition is true too.
  • if the first condition is false, then the second condition is false too.

For finite / infinite and NaN values.

Comment on lines 307 to +308
double val=it[b.cols-1]/myite;
if(val<min || (val==min && B[row_it]<min_var)){
if((val<min) || ((val==min) && (B[row_it]<min_var))){
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(not a part of this fix)
prefer multiplication over division.

(a/b < c) ==> (a < c*b) (works if b > 0)
(a/b == c) ==> (a == c*b)

4., 0., 4., 1., 4.);

Mat z;
int result = cv::solveLP(A,B,z);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe, we still need some normalization or auto-tuning of SOLVELP_EPS value.

  • cv::solveLP(A,B,z); => OK
  • cv::solveLP(A,B*2,z); => OK
  • cv::solveLP(A,B*10,z); => OK
  • cv::solveLP(A,B*1e3,z); => OK
  • cv::solveLP(A,B*1e4,z); => FAIL

A can be multiplied by any positive scalar too.


BTW, use 2**k multiplier to minimize calculation errors in normalization (if normalization is going to be used).

@@ -259,10 +268,12 @@ static int inner_simplex(Mat_<double>& c, Mat_<double>& b,double& v,vector<int>&
int e=-1,pos_ctr=0,min_var=INT_MAX;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove buggy "static" modifier from variable too

}
//check constraints feasibility
Mat prod = Constr(Rect(0, 0, Constr.cols - 1, Constr.rows)) * z;
Mat constr_check = (prod - 1e-12) > Constr.col(Constr.cols - 1);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mat constr_check = Constr.col(Constr.cols - 1) - prod;
double minValue = 0;
minMaxIdx(constr_check, &minValue)  // we need minValue only
Analyze `minValue`

//! return codes for cv::solveLP() function
enum SolveLPResult
{
SOLVELP_LOST = -3, //!< problem is feasible, but solver lost solution due to floating-point numbers representation error
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"due to floating-point arithmetic errors"

for (int i = 1; i < 1e5; i*=5)
{
Mat z;
int result = cv::solveLP(A,B*i,z);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In linear task both A/B should be multiplied to keep the same result.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it's not true. 5x+10y < 25 is the same as x+2y < 5. So mathematically it's correct test, but the idea of test is not obvious. Is the provided matrix is bed defined for Simplex Method?

@asmorkalov
Copy link
Copy Markdown
Contributor

@rayonnant14 Friendly reminder.

1 similar comment
@asmorkalov
Copy link
Copy Markdown
Contributor

@rayonnant14 Friendly reminder.

@asmorkalov
Copy link
Copy Markdown
Contributor

@rayonnant14 Friednly reminder.

1 similar comment
@asmorkalov
Copy link
Copy Markdown
Contributor

@rayonnant14 Friednly reminder.

double myite=0;
//check constraints, select the tightest one, reinforcing Bland's rule
if((myite=it[e])>0){
if (fabs(it[b.cols-1]) <= SOLVELP_EPS_REL * fabs(it[b.cols-1]) + SOLVELP_EPS_ABS)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check is equivalent to (1-SOLVELP_EPS_REL)*fabs(it[b.cols-1] <= SOLVELP_EPS_ABSorfabs(it[b.cols-1] < SOLVELP_EPS_ABS/0.2`. I do not think that it's expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants