Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/segfaults #33

Merged
merged 4 commits into from
Oct 15, 2018
Merged

Fix/segfaults #33

merged 4 commits into from
Oct 15, 2018

Conversation

viviansuzano
Copy link
Contributor

When the user chooses to use the "finite-difference-values" method for the "jacobian_approximation" option, it should be possible to leave the FillJacobianBlock function of the constraint class empty. However, Ifopt uses the Jacobian defined by the user to determine the number of non-zero elements in the constraint Jacobian, necessary information for interfacing the problem with Ipopt.

nnz_jac_g = nlp_->GetJacobianOfConstraints().nonZeros();

The result is a Segmentation Fault when the constraint Jacobian was not defined by the user (because nnz_jac_g is defined as zero), even if "jacobian_approximation" is set to "finite-difference-values".

The solution I implemented is to use a dense Jacobian when the "finite-difference-values" option is set, which always works. The downside is, if the user did define the sparse structure for the Jacobian and the "finite-difference-values" option is set, Ifopt will still use a dense matrix instead of the sparse, which most likely will increase the computational cost.

Unless the number of non-zeros elements in the constraint Jacobian is also a user entry in Ifopt, I don't see a better way to fix this.

@awinkler
Copy link
Member

awinkler commented Oct 7, 2018

Thanks for your help on this! However, the solution for this is quite difficult as you already pointed out. I don't like that ipopt_adapter now depends on ipopt_solver. And there are other harder-coded definitions there.

Can you definitely test that this is the source of the problem? I was under the impression that if "finite-difference-values" is set, the function below should never be called.

bool IpoptAdapter::eval_jac_g(Index n, const double* x, bool new_x,

Then, the jacobian structure could only still come in here

nnz_jac_g = nlp_->GetJacobianOfConstraints().nonZeros();

but I also think that variable is not used internally by IPOPT if the "finite-difference-values" is set.

Unfortunately i don't have a laptop with me, so can't test, can others confirm this problem?

@jelavice
Copy link

Hi Alex,

I confirm that I get this issue as well. When providing empty implementations for the functions that fill Jacobians and using finite differences, I also get a segfault. The segfault happens in the ipopt, internally. Vivian will attach the gdb traceback. If the "finite-difference-values" option is set, the function is called once at the beginning to provide the structure of the jacobian to the ipopt. Since there is no structure provided, ipopt will subsequently segfault.

Vivian will also update the pull request with a cleaner fix that doesn't make ipopt adapter dependent on the ipopt solver.

@viviansuzano
Copy link
Contributor Author

Hi Alex,

Following @jelavice's comment, here is the gdb traceback when running the original code with "finite-difference-values" option set and empty Jacobian implementation:

Program received signal SIGSEGV, Segmentation fault. 0x00007ffff68efc03 in Ipopt::TNLPAdapter::internal_eval_jac_g(bool) () from /usr/lib/libipopt.so.1 (gdb) bt #0 0x00007ffff68efc03 in Ipopt::TNLPAdapter::internal_eval_jac_g(bool) () from /usr/lib/libipopt.so.1 #1 0x00007ffff68efce4 in Ipopt::TNLPAdapter::Eval_jac_c(Ipopt::Vector const&, Ipopt::Matrix&) () from /usr/lib/libipopt.so.1 #2 0x00007ffff6957f45 in Ipopt::GradientScaling::DetermineScalingParametersImpl(Ipopt::SmartPtr<Ipopt::VectorSpace const>, Ipopt::SmartPtr<Ipopt::VectorSpace const>, Ipopt::SmartPtr<Ipopt::VectorSpace const>, Ipopt::SmartPtr<Ipopt::MatrixSpace const>, Ipopt::SmartPtr<Ipopt::MatrixSpace const>, Ipopt::SmartPtr<Ipopt::SymMatrixSpace const>, Ipopt::Matrix const&, Ipopt::Vector const&, Ipopt::Matrix const&, Ipopt::Vector const&, double&, Ipopt::SmartPtr<Ipopt::Vector>&, Ipopt::SmartPtr<Ipopt::Vector>&, Ipopt::SmartPtr<Ipopt::Vector>&) () from /usr/lib/libipopt.so.1 #3 0x00007ffff69b070f in Ipopt::StandardScalingBase::DetermineScaling(Ipopt::SmartPtr<Ipopt::VectorSpace const>, Ipopt::SmartPtr<Ipopt::VectorSpace const>, Ipopt::SmartPtr<Ipopt::VectorSpace const>, Ipopt::SmartPtr<Ipopt::MatrixSpace const>, Ipopt::SmartPtr<Ipopt::MatrixSpace const>, Ipopt::SmartPtr<Ipopt::SymMatrixSpace const>, Ipopt::SmartPtr<Ipopt::MatrixSpace const>&, Ipopt::SmartPtr<Ipopt::MatrixSpace const>&, Ipopt::SmartPtr<Ipopt::SymMatrixSpace const>&, Ipopt::Matrix const&, Ipopt::Vector const&, Ipopt::Matrix const&, Ipopt::Vector const&) () from /usr/lib/libipopt.so.1 #4 0x00007ffff69ba616 in Ipopt::OrigIpoptNLP::InitializeStructures(Ipopt::SmartPtr<Ipopt::Vector>&, bool, Ipopt::SmartPtr<Ipopt::Vector>&, bool, Ipopt::SmartPtr<Ipopt::Vector>&, bool, Ipopt::SmartPtr<Ipopt::Vector>&, bool, Ipopt::SmartPtr<Ipopt::Vector>&, bool, Ipopt::SmartPtr<Ipopt::Vector>&, Ipopt::SmartPtr<Ipopt::Vector>&) () from /usr/lib/libipopt.so.1 #5 0x00007ffff698f03a in Ipopt::IpoptData::InitializeDataStructures(Ipopt::IpoptNLP&, bool, bool, bool, bool, bool) () from /usr/lib/libipopt.so.1 #6 0x00007ffff6948340 in Ipopt::DefaultIterateInitializer::SetInitialIterates() () from /usr/lib/libipopt.so.1 #7 0x00007ffff695a3d6 in Ipopt::IpoptAlgorithm::InitializeIterates() () from /usr/lib/libipopt.so.1 #8 0x00007ffff695fbb5 in Ipopt::IpoptAlgorithm::Optimize(bool) () from /usr/lib/libipopt.so.1 #9 0x00007ffff68e12c9 in Ipopt::IpoptApplication::call_optimize() () from /usr/lib/libipopt.so.1 #10 0x00007ffff68e4b40 in Ipopt::IpoptApplication::OptimizeNLP(Ipopt::SmartPtr<Ipopt::NLP> const&, Ipopt::SmartPtr<Ipopt::AlgorithmBuilder>&) () from /usr/lib/libipopt.so.1 #11 0x00007ffff68d7f09 in Ipopt::IpoptApplication::OptimizeNLP(Ipopt::SmartPtr<Ipopt::NLP> const&) () from /usr/lib/libipopt.so.1 #12 0x00007ffff68e0d2b in Ipopt::IpoptApplication::OptimizeTNLP(Ipopt::SmartPtr<Ipopt::TNLP> const&) () from /usr/lib/libipopt.so.1 #13 0x00007ffff7bce381 in ifopt::IpoptSolver::Solve (this=0x7fffffffd920, nlp=...) at /home/vivian/ifopt/ifopt_ipopt/src/ipopt_solver.cc:75 #14 0x000000000041fc0c in main () at /home/vivian/ifopt/ifopt_ipopt/test/ex_test_ipopt.cc:51

As you can see, Ipopt still expects the structure for the Jacobian and segfaults.

I updated the code so ipopt_adapter no longer depends on ipopt_solver and simply gets a flag indicating whether the "finite-difference-values" option is set or not, which is a cleaner fix.
However, the problem with the use of a dense Jacobian instead of a sparse one in this condition still remains. Without any extra input for the user, I don't see a solution where a dense Jacobian is not required.

Copy link
Member

@awinkler awinkler left a comment

Choose a reason for hiding this comment

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

Hi @viviansuzano and @jelavice
Thanks for your help on this. I'd be great if you could still clarify the comments I left below and update the PR accordingly. Thanks! 👍

* https://www.coin-or.org/Ipopt/documentation/node40.html
*/
void SetOption(const std::string& name, const std::string& value);
void SetOption(const std::string& name, int value);
void SetOption(const std::string& name, double value);

/** Get options of the IPOPT solver.
Copy link
Member

Choose a reason for hiding this comment

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

These two functions are not used if i'm not mistaken? so let's better remove them (and the .cc implementation) until we notice we need this call more often.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are right, they are not used anymore. I implemented them to read the solver options, but they turned out not to be necessary. I removed them from the .h and .cc files.

@@ -107,17 +112,29 @@ bool IpoptAdapter::eval_jac_g(Index n, const double* x, bool new_x,
Index m, Index nele_jac, Index* iRow, Index *jCol,
double* values)
{
// defines the positions of the nonzero elements of the jacobian
Copy link
Member

Choose a reason for hiding this comment

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

I think the above comment is more precise, let's keep it for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

ipopt_app_->Options()->GetStringValue("jacobian_approximation", value, "");
if (value.compare("finite-difference-values") == 0)
finite_diff = true;

Copy link
Member

Choose a reason for hiding this comment

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

how about replacing line 76, 79, 80 with:

bool finite_diff = value=="finite-difference-values";

keeping declaration and definition closer together. Maybe also rename value to something like jac_type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, good suggestion.

@@ -98,4 +107,16 @@ IpoptSolver::SetOption (const std::string& name, double value)
ipopt_app_->Options()->SetNumericValue(name, value);
}

void
Copy link
Member

Choose a reason for hiding this comment

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

All the lines below can be removed if I'm not missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

@@ -44,7 +44,8 @@ int main()
// 2. choose solver and options
IpoptSolver ipopt;
ipopt.SetOption("linear_solver", "mumps");
ipopt.SetOption("jacobian_approximation", "exact");
//ipopt.SetOption("jacobian_approximation", "exact");
Copy link
Member

Choose a reason for hiding this comment

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

For the test lets keep in exact, there the jacobians are defined.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@awinkler awinkler merged commit bd8bd81 into ethz-adrl:master Oct 15, 2018
@awinkler
Copy link
Member

Great, thanks a lot for your help @viviansuzano 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants