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 for CMA-ES inconsistencies. #193

Closed
wants to merge 22 commits into from

Conversation

gaurav-singh1998
Copy link
Contributor

  • Issue reference CMA-ES inconsistencies #70
    This pull request aims to resolve inconsistencies that currently exist in CMA-ES implementation. I have added the boundary constraints as per this code snippet which is a part of CMA-ES implementation in C by N. Hansen. I will make the appropriate changes in documentation also after a member's approval of every change I made. Thank you.

@gaurav-singh1998
Copy link
Contributor Author

Hi, @zoq kindly have a look at this whenever you get a chance. Thanks.

include/ensmallen_bits/cmaes/cmaes.hpp Show resolved Hide resolved
include/ensmallen_bits/cmaes/cmaes.hpp Show resolved Hide resolved
include/ensmallen_bits/cmaes/cmaes.hpp Show resolved Hide resolved
include/ensmallen_bits/cmaes/cmaes_impl.hpp Outdated Show resolved Hide resolved
include/ensmallen_bits/cmaes/cmaes_impl.hpp Outdated Show resolved Hide resolved
include/ensmallen_bits/cmaes/cmaes_impl.hpp Outdated Show resolved Hide resolved
include/ensmallen_bits/cmaes/cmaes_impl.hpp Outdated Show resolved Hide resolved
include/ensmallen_bits/cmaes/cmaes_impl.hpp Outdated Show resolved Hide resolved
include/ensmallen_bits/cmaes/cmaes_impl.hpp Outdated Show resolved Hide resolved
include/ensmallen_bits/cmaes/cmaes_impl.hpp Outdated Show resolved Hide resolved
gaurav-singh1998 and others added 6 commits April 8, 2020 12:25
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
Co-Authored-By: Marcus Edel <marcus.edel@fu-berlin.de>
@gaurav-singh1998
Copy link
Contributor Author

gaurav-singh1998 commented Apr 8, 2020

Hi, @zoq as per this discussion, I have implemented the constructor in this pull request but wanted to ask how should mark this constructor deprecated. Should I use Info here?

@favre49
Copy link
Member

favre49 commented Apr 8, 2020

You can use ens_deprecated to mark a constructor as deprecated. Make sure to add it to the comments as well. See the diff of #185 to see how the deprecated constructors were marked.

@favre49
Copy link
Member

favre49 commented Apr 8, 2020

Does it still qualify as breaking reverse compatibility though? Since you made initialSigma the last member of the constructor, I don't think it does (though the behavior if the function does change slightly).

@gaurav-singh1998
Copy link
Contributor Author

Oh! okay, thanks for the clarification @favre49

Copy link
Member

@zoq zoq left a comment

Choose a reason for hiding this comment

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

Looks good, can you update the HISTORY.md file as well.

include/ensmallen_bits/cmaes/cmaes_impl.hpp Outdated Show resolved Hide resolved
@gaurav-singh1998
Copy link
Contributor Author

Hi, @zoq I have made the relevant changes in the code, HISTORY.md and in the documentation as well. Kindly have a look at it whenever you have a chance. Thanks.

// Boundary transformation shift into feasible pre-image.
if (y < xlow)
{
y += (ElemType)(r * (1 + (xlow - y) / r));
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be y += (ElemType)(r * (1 + (int)(xlow - y) / r)); also is the cast to ElemType needed, not sure I can see the reason to do that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi, @zoq sorry for replying so late. I did it to ensure that every element of the matrix are of same types if not required I'll remove it. Thanks.

Copy link
Member

Choose a reason for hiding this comment

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

If you don't mind let's remove the extra cast.

include/ensmallen_bits/cmaes/cmaes_impl.hpp Outdated Show resolved Hide resolved
@zoq
Copy link
Member

zoq commented May 14, 2020

You can use assert, here is an example:

assert(lowerBound.n_rows == iterate.n_rows && "The dimensions of "
let me know if that doesn't work.

@gaurav-singh1998
Copy link
Contributor Author

@zoq, I have made the relevant changes kindly have a look at it whenever you get a chance. Thanks.

Copy link
Member

@favre49 favre49 left a comment

Choose a reason for hiding this comment

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

Before we merge, I'd also like to test how robust the changed algorithm is w.r.t the tests.

include/ensmallen_bits/cmaes/cmaes.hpp Outdated Show resolved Hide resolved
initialSigma(initialSigma)
{
assert(this->lowerBound != this->upperBound && "The values of "
"lowerbound and upperbound must be different.");
Copy link
Member

Choose a reason for hiding this comment

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

Either camelcase the variable names or say lower bound.

@@ -50,7 +50,7 @@ TEST_CASE("CMAESLogisticRegressionTest", "[CMAESTest]")
responses, testResponses, shuffledResponses);
Copy link
Member

Choose a reason for hiding this comment

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

Should we add a test that makes sure CMAES keeps the answers within the given bounds?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi, @favre49 sorry for being late on this. Can you please elaborate on the test for this should be written? Should I consider an armadillo matrix which is out of bounds and then apply the transformation to see if the values are within the specified range? Sorry, that I didn't get the gist of what you were saying.

Copy link
Member

Choose a reason for hiding this comment

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

I meant we should add a test that ensures the optimized coordinates are within the given bounds [-20,20]. Something like REQUIRE(coordinates[i] <=20 && coordinates[i] >= -20)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi, @favre49 the test has been added kindly have a look whenever you get a chance. Thanks.

gaurav-singh1998 and others added 2 commits May 15, 2020 17:04
Co-authored-by: favre49 <40389657+favre49@users.noreply.github.com>
@gaurav-singh1998
Copy link
Contributor Author

Hello, @zoq kindly have a look at this whenever you get a chance. I have made all the necessary changes required. Thanks.

const double diff = (upperBound - lowerBound) / 2.0;
const double al = std::min(diff, (1 + std::abs(lowerBound)) / 20.0);
const double au = std::min(diff, (1 + std::abs(upperBound)) / 20.0);
const double xlow = lowerBound - 2 * al - diff;
Copy link
Member

Choose a reason for hiding this comment

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

Do you think we should make t possible to define bounds for all coordinates similar to what has been done for the PSO method:

const arma::mat& lowerBound = arma::ones(1, 1),
const arma::mat& upperBound = arma::ones(1, 1),

It uses a 1x1 matrix as a default, so the current implementation but you use a matrix of the same size as the coordinates as well. Let me know what you think. We can also implement the change another time and merge this as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @zoq, I implemented this boundary handing function after referring the N Hansen's implementation and in my opinion, I think we should merge this as it is and if there are any issues in the future I will try to change this accordingly. Thanks.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, fine for me.

@gaurav-singh1998
Copy link
Contributor Author

Hi, @zoq Are there any more changes required for this?

@zoq
Copy link
Member

zoq commented Jun 9, 2020

No I think this is fine, I just have to double check the windows issue, but for some reason I can't access the page right now.

@zoq
Copy link
Member

zoq commented Jun 9, 2020

Looks like the decomposition failed on the windows build:

[00:11:38] 1/1 Test #1: ensmallen_tests ..................***Failed  283.19 sec
[00:11:38] ensmallen version: 2.12.1 (Stir Crazy)
[00:11:38] armadillo version: 8.400.0 (Entropy Bandit)
[00:11:38] 
[00:11:38] warning: chol(): decomposition failed
[00:11:38] 
[00:11:38] warning: chol(): decomposition failed
[00:11:38] 
[00:11:38] warning: chol(): decomposition failed
[00:11:38] 
[00:11:38] warning: chol(): decomposition failed
[00:11:38] 
[00:11:38] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[00:11:38] ensmallen_tests.exe is a Catch v2.4.1 host application.
[00:11:38] Run with -? for options
[00:11:38] 
[00:11:38] -------------------------------------------------------------------------------
[00:11:38] BoundaryConditionTestFunction
[00:11:38] -------------------------------------------------------------------------------
[00:11:38] C:\projects\ensmallen\tests\cmaes_test.cpp(180)
[00:11:38] ...............................................................................
[00:11:38] 
[00:11:38] C:\projects\ensmallen\tests\cmaes_test.cpp(194): FAILED:
[00:11:38]   REQUIRE( success == true )
[00:11:38] with expansion:
[00:11:38]   false == true
[00:11:38] 
[00:11:38] ===============================================================================
[00:11:38] test cases:   265 |   264 passed | 1 failed
[00:11:38] assertions: 12149 | 12148 passed | 1 failed

@mlpack-bot
Copy link

mlpack-bot bot commented Jul 10, 2020

This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! 👍

@mlpack-bot mlpack-bot bot added the s: stale label Jul 10, 2020
@gaurav-singh1998
Copy link
Contributor Author

Hi, @zoq please keep this open I want to work on it whenever I get a chance. Thanks

@mlpack-bot mlpack-bot bot removed the s: stale label Jul 10, 2020
@zoq
Copy link
Member

zoq commented Jul 13, 2020

@mlpack-jenkins test this please

*/
TEST_CASE("BoundaryConditionTestFunction", "[CMAESTest]")
{
SGDTestFunction f;
Copy link
Member

Choose a reason for hiding this comment

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

Do you mind to change the object function here, to something more complex, the SGD function has only 3 parameters.

Copy link
Member

Choose a reason for hiding this comment

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

@gaurav-singh1998 let me know if you need any help with this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @zoq, sorry for being so late on this(life got messed up). I want to take this up again. Can you provide some help as to what objective functions I need to use and what other changes do I need to make? Thanks.

Copy link
Member

Choose a reason for hiding this comment

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

@gaurav-singh1998 No worries I hope everything is going well. Let's test this one on the LogisticRegression function, here is an example -

TEST_CASE("CMAESLogisticRegressionTest", "[CMAESTest]")
{
const size_t trials = 3;
bool success = false;
for (size_t trial = 0; trial < trials; ++trial)
{
arma::mat data, testData, shuffledData;
arma::Row<size_t> responses, testResponses, shuffledResponses;
LogisticRegressionTestData(data, testData, shuffledData,
responses, testResponses, shuffledResponses);
LogisticRegression<> lr(shuffledData, shuffledResponses, 0.5);
CMAES<> cmaes(0, -1, 1, 32, 200, 1e-3);
arma::mat coordinates = lr.GetInitialPoint();
cmaes.Optimize(lr, coordinates);
// Ensure that the error is close to zero.
const double acc = lr.ComputeAccuracy(data, responses, coordinates);
const double testAcc = lr.ComputeAccuracy(testData, testResponses,
coordinates);
if (acc >= 99.7 && testAcc >= 99.4)
{
success = true;
break;
}
}
.

@gaurav-singh1998
Copy link
Contributor Author

Hi, @zoq made the relevant changes in the test function. Kindly have a look at it now.

BaseMatType initialVal;
initialVal.randu(iterate.n_rows, iterate.n_cols);
initialVal += (BaseMatType)(iterateIn);
mPosition[0] = initialVal;
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 it's still valid to apply the lower/upper bound here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi, @zoq so should I remove the boundary transformation from here and implement it like it was before like here. Thanks

@zoq
Copy link
Member

zoq commented Feb 4, 2021

Hi, @zoq made the relevant changes in the test function. Kindly have a look at it now.

Sorry for the slow response, I'll review the code over the weekend.

// Transforms the candidate into the given bounds.
template<typename SelectionPolicyType>
template<typename BaseMatType>
void CMAES<SelectionPolicyType>::BoundaryTransform(BaseMatType& matrix)
Copy link
Member

Choose a reason for hiding this comment

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

So I dived into the code and I think it makes sense to introduce another policy e.g. TransformationPolicy so we can enable/disable the boundary constraint. Let me know what you think, I can push the changes to you branch, if you like?

Copy link
Contributor Author

@gaurav-singh1998 gaurav-singh1998 Feb 7, 2021

Choose a reason for hiding this comment

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

Hi, @zoq thanks for the review. I will try and implement the TransformationPolicy this week. Can you point to some source code to which I can refer before implementing this? Thank you.

Copy link
Member

Choose a reason for hiding this comment

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

So basically it's similar to how the update policies are implemented https://github.com/mlpack/ensmallen/tree/master/include/ensmallen_bits/sgd/update_policies.

We basically add a new template type called TransformationPolicy or something like that and define an interface. I think we just need two functions at this point Transform and Inverse. So the class policy class for the BoundaryBoxConstraint could look like:

class BoundaryBoxConstraint
{
 public:
  void Transform(...)
  {
  }

  void Inverse(...)
  {
  }
}

we also a policy class that does nothing:

class EmptyTransformation
{
 public:
  void Transform(...)
  {
  }

  void Inverse(...)
  {
  }
}

in the CMAES class we can than use an instantiation of the TransformationPolicy and call the Transform and Inverse methods. Hopefully what I wrote above makes sense, happy to clarify anything.

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